过载<<运算符并将参数传递给std :: cout

时间:2015-05-26 04:53:30

标签: c++ templates c++11 stdout ostream

我想创建一个用于记录目的的类,其行为类似std::cout,但会自动向流中插入其他信息。

我想要的示例用法类似于(暂时不关心对象和上下文类型,只是假设它们是std :: string):

Logger l;

l << "Event with object : " << obj << " while in context : " << context;

然后输出为:

[timestamp] Event with object : [obj_desc] while in context : [this context][eol][flush]

我一直在尝试:

template<typename T>
Logger& operator << (const T& msg){
    std::cout << timestamp() << msg << std::endl << std::flush;
    return *this;
}

但似乎std::cout无法解析typename T并且在segfaulting时无法输出std :: string。

一种可能的解决方案是为所有类型重载这个,但这相当烦人且耗时。

有更好的选项可以用更多信息来装饰std::cout输出吗?

编辑:

我现在意识到endl和flush将被附加到每个消息的目的,但我仍然对一般的想法感兴趣。我更关心monadic语法附加任意数量的消息而不是<<重载

2 个答案:

答案 0 :(得分:0)

您的代码无效的原因是您没有为要传递给它的所有内容实现operator<<

本声明:

Logger l;
l << "Event with object : " << obj << " while in context : " << context;

基本上是这样做的(假设operator<<Logger的成员,你的实现意味着它是):

Logger l;
l.operator<<("Event with object : ").operator<<(obj).operaator<<(" while in context : ").operator<<(context);

因此,对于字符串,对象,上下文等,您需要单独的operator<<重载。您需要一种方法来指示何时将完整的日志消息刷新到std::cout

我会建议更像这样的事情:

struct LoggerStream
{
    std::ostringstream strm;

    struct Timestamp
    {
    };

    ~LoggerStream()
    {
        std::string s = strm.str();
        if (!s.empty())
            std::cout << s << std::flush;
    }

    LoggerStream& operator<< (const Timestamp &t)
    {
        strm << "[timestamp] "; // format this however you need
        return *this;
    }

    LoggerStream& operator<< (const object &obj)
    {
        strm << "[obj_desc]"; // format this however you need
        return *this;
    }

    LoggerStream& operator<< (const context &ctx)
    {
        strm << "[this context]"; // format this however you need
        return *this;
    }

    LoggerStream& operator<< (std::ostream&(*f)(std::ostream&))
    {
        if (f == (std::basic_ostream<char>& (*)(std::basic_ostream<char>&)) &std::flush)
        {
            std::string s = strm.str();
            if (!s.empty())
                std::cout << s << std::flush;
            strm.str("");
            strm.clear();
        }
        else
            strm << f;

        return *this;
    }

    template<typename T>
    LoggerStream& operator<< (const T& value)
    {
        strm << value;
        return *this;
    }
};

class Logger
{
    LoggerStream getStream()
    {
        LoggerStream strm;
        strm << Timestamp;
        return strm;
    }
};

然后你可以这样做:

Logger l;
l.getStream() << "Event with object : " << obj << " while in context : " << context;
...
l.getStream() << "Event with object : " << obj << " while in context : " << context;
...

Logger l;
LoggerStream strm = l.getStream();
strm << "Event with object : " << obj << " while in context : " << context << std::flush;
...
strm << Logger::Timestamp << "Event with object : " << obj << " while in context : " << context << std::flush;
...

可替换地:

struct Logger
{
    std::ostringstream strm;

    ~Logger()
    {
        std::string s = strm.str();
        if (!s.empty())
            std::cout << "[timestamp] " << s << std::flush;
    }

    Logger& operator<< (const object &obj)
    {
        strm << "[obj_desc]"; // format this however you need
        return *this;
    }

    Logger& operator<< (const context &ctx)
    {
        strm << "[this context]"; // format this however you need
        return *this;
    }

    Logger& operator<< (std::ostream&(*f)(std::ostream&))
    {
        if (f == (std::basic_ostream<char>& (*)(std::basic_ostream<char>&)) &std::flush)
        {
            std::string s = strm.str();
            if (!s.empty())
                std::cout << "[timestamp] " << s << std::flush;
            strm.str("");
            strm.clear();
        }
        else
            strm << f;

        return *this;
    }

    template<typename T>
    Logger& operator<< (const T& value)
    {
        strm << value;
        return *this;
    }
};

Logger() << "Event with object : " << obj << " while in context : " << context;
...
Logger() << "Event with object : " << obj << " while in context : " << context;
...

Logger l;
l << "Event with object : " << obj << " while in context : " << context << std::flush;
...
l << "Event with object : " << obj << " while in context : " << context << std::flush;
...

答案 1 :(得分:0)

如果需要,你当然可以重载流类,为你想要支持的所有数据类型提供operator<<(这可能是“正确的”方式),但是,如果您只是追求的是将记录添加到常规流的快速方法,有一种更简单的方法:

#include <iostream>
#include <iomanip>
#include <sstream>
#include <ctime>
#include <unistd.h>

#define logcout std::cout << timestamp()

std::string timestamp(void) {
    time_t now = time(0);
    struct tm *tmx = localtime(&now);
    std::ostringstream oss;
    oss << '['
        << (tmx->tm_year+1900)
        << '-'
        << std::setfill('0') << std::setw(2) << (tmx->tm_mon+1)
        << '-'
        << std::setfill('0') << std::setw(2) << (tmx->tm_mday)
        << ' '
        << std::setfill('0') << std::setw(2) << (tmx->tm_hour)
        << ':'
        << std::setfill('0') << std::setw(2) << (tmx->tm_min)
        << ':'
        << std::setfill('0') << std::setw(2) << (tmx->tm_sec)
        << "] ";
    return oss.str();
}

int main (int argc, char *argv[]) {
    logcout << "A slightly\n";
    sleep (5);
    logcout << "sneaky" << " solution\n";
    return 0;
}

输出:

[2015-05-26 13:37:04] A slightly
[2015-05-26 13:37:09] sneaky solution

不要被代码的大小所欺骗,我只提供了一个完整的可编译样本进行测试。问题的关键是行:

#define logcout std::cout << timestamp()

然后您可以使用logcout而不是std::cout,并且每次出现都会在流内容前面加上一个任意字符串(在这种情况下,时间戳代表了大部分代码)。< / p>

这不是我称之为 C ++代码的东西,但是,如果你的需求基本上就是你所说的,那肯定会有所帮助。