Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

What I am trying to achieve?

How can I find if a stream chain is ended? Look at the function below (all these functions are inside a LogRouter class in this question):

template<typename First, typename... Rest>
void log(const LogLevel &level_, First first_, Rest... rest_) {
    sstream << first_ << " ";
    log(level_, rest_...);
}

void log(const LogLevel &level_) {
    for(auto &route : routes)
        route->stream() << sstream.str() << std::endl;

    sstream.clear();
    sstream.str("");
}

I want to achieve the exact same functionality in the above but using streams. So, when I reach an end of a stream it needs to send the final data to the routes and instead of using

router.log(LogLevel::Alert, "test stream", 15);

I want to be able to use

router.log(LogLevel::Alert) << "test stream " << 15;

What I have tried:

  • std::ostream operator overloading does not accept packed variables.

  • going through every single passed value one by one. Like below:

     struct LogEnd {};
    
     static LogEnd end() { return LogEnd; }
    
     template<typename T> LogRouter &operator<<(const T &value) {
         sstream << value;
         return *this;
     }
    
     LogRouter &log(const LogLevel &level_) {
         currentLogLevel = level_; //had to add another variable
         return *this;
     }
    
     void operator<<(const LogEnd &end) {
        for(auto &route : routes)
            route.stream() << sstream.str() << std::endl;
        currentLogLevel = LogLevel::None;
    }
    

    This gives me what I want syntax wise but I need to call the additional LogRouter::end() at the end of every:

     router.log(LogLevel::Alert) << "test stream " << 15 << LogRouter::end();
    

    I have a syntax for std::endl also but it would be best if I can call it without anything in the end.

Question

Is there a way to know an end of a stream chain. Something similar to what you can do when using recursive variadic template function.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
310 views
Welcome To Ask or Share your Answers For Others

1 Answer

You could put the interesting logic into the stream's destructor. Obviously, I would also properly deal with stream rather than cooking up something which somewhat looks like a stream but isn't really a stream:

#include <iostream>
#include <sstream>
#include <string>

class logstream
    : private virtual std::stringbuf
    , public std::ostream {
    std::string level;
public:
    logstream(std::string l)
        : std::ostream(this)
        , level(l) {
    }
    logstream(logstream&& other)
    : std::stringbuf(std::move(other))
    , std::ostream(std::move(other))
    , level(std::move(other.level)) {
        this->rdbuf(0);
    }
    ~logstream() {
        std::cout << "do something interesting here("
                  << this->level<< ", " << this->str() << ")
";
    }
};

logstream trace() {
    return logstream("trace");
}

int main()
{
    trace() << "hello, world";
}

The stream buffer used (std::stringbuf in this case but it could also be a custom stream buffer) is made a base class to have it constructed before the std::ostream. In principle it is meant to be a data member but data members are constructed after the base classes. Thus, it is made a private base class instead.

It turns out that std::ostream has a virtual base class (std::ios) which would cause the std::ostream to still be constructed before the std::stringbuf if normal inheritance where used for std::stringbuf. Using virtual inheritance and making the std::stringbuf the first base class makes sure it really is constructed first.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...