Want to check if templated argument is std::endl

Viewed 113

I want to write a functionality like:

DEBUG_LOG<<"append" << 123 <<" float=" <<123.432 <<  std::endl;

the << operator for this looks like:

        Logger& operator<<(const T& message)
        {
                if(message == std::endl)
                {
                        flushBuffer();
                        return *this;
                }
                std::stringstream ss;
                ss << message;
                writeToBuffer(ss.str());
                return *this;
        }

Now, this definitely does not compile since the if statement has a compilation error. The flushBuffer() writes one line into a file and clears the buffer, writeToBuffer() appends to the buffer. Can you please suggest something in place of the if statement? Basically I need to know when a newline has been received accordingly the other two functions will be called.

2 Answers

I am not sure what write to buffer or flush buffer are going to be, I have used std::stringstream under the hood.

std::endl is not a valid type of your template function it will not compile (with an error message like : undefined reference to `operator<<(logger&, std::ostream& (*)(std::ostream&))', so you need to provide a match for it and implement the desired behaviour in there.

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

class logger
{
    std::stringstream buffer;
    template<typename T> friend logger& operator<<(logger& log, const T& t);
    friend logger& operator<<(logger& log, std::ostream& (*var)(std::ostream&));
public:
    void flush()
    {
        std::cout << buffer.str();
    }
};

template<class T>
logger& operator<<(logger& log, const T& t)
{
    log.buffer << t;
    return log;
}

logger& operator<<(logger& log, std::ostream& (*var)(std::ostream&)) {
  log.buffer << std::endl;
  return log;
}

int main()
{
    logger l;
    l << "append " << 123 <<" float=" << 123.432 <<  std::endl;
    l << "some other stuff here" << std::endl;
    l.flush();
}

If this is more than and exercise, I will suggest you to pick one the many available libraries before start doing something from the scratch.

You can overload operator<< the same way std::ostream does:

class Logger{
public:

    using CharT=char;
    using Traits=std::char_traits<CharT>;

    Logger& operator<<(std::basic_ostream<CharT,Traits>& (*func)(std::basic_ostream<CharT,Traits>&) ){
        if(func==std::endl<CharT,Traits>){
            //...
        }
        return *this;
    }
};

int main(){
    Logger{}<<std::endl;
}

Because std::endl is a template function, you have to force the overload set resolution by accepting a concrete function pointer. Feel free to modify the traits, but since you are not actually going to call the function, you can leave them as they are.

Your solution will not work even with if constexpr because C++ does not support passing template functions as arguments.

Consider perfect forwarding your message. Also, std::stringstream is not particularly fast AFAIK, but since you are writing a logger from scratch instead of using some public ones, performance is not a concern I guess.

Related