2

我想制作一个可以像 一样使用的记录器std::cout,但我想记录一些额外的数据,例如日期、时间、、,__LINE__并且__func__应该__FILE__自动保存到文件中。

例子

ToolLogger log;
log << "some data" << std::endl;

预期产出

[14.11.2015 21:10:12.344 (main.cpp) (main,14): some data

解决方案不足

为此,我必须将诸如__LINE__直接之类的宏放在我调用记录器的行中,否则宏将无法正常工作。我发现我可以std::endl用我的宏替换,它会像这样执行这个黑魔法:

#define __FILENAME__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/') + 1 : __FILE__)
#define logendl \
    ((ToolLogger::fileName = __FILENAME__).empty() ? "" : "") \
    << ((ToolLogger::line = __LINE__) ? "" : "") \
    << ((ToolLogger::function = __func__).empty() ? "" : "") \
    << std::endl

该宏logendl使用我的ToolLogger类中的静态变量来保存 的值__LINE____func__并在__FILE__以后需要。所以实际使用记录器将如下所示:

ToolLogger log;
log << "some data" << logendl;

在课堂上,我必须重载operator<<才能让它工作,我需要其中两个。一个用于取正常值,如std::stringor int,另一个用于取std::endl操纵器。这是我班上最重要的事情:

class ToolLogger
{
  public:

    // standard operator<< //
    template<typename T>
    ToolLogger& operator<< (const T& str)
    {
        out << str;
        return *this;
    }

    // operator<< for taking the std::endl manipulator //
    typedef std::basic_ostream<char, std::char_traits<char> > CoutType;
    typedef CoutType& (*StandardEndLine)(CoutType&);
    ToolLogger& operator<<(StandardEndLine manip)
    {
        // save fileName, line and function to the file //
        // and all what is already in stringstream //
        // clear stringstream //
        return *this;
    }

    static string fileName;
    static int line;
    static string function;

  private:

    ofstream file;
    std::stringstream out;
};

string ToolLogger::fileName;
int ToolLogger::line;
string ToolLogger::function;

问题

这个解决方案的问题是我可以通过两种方式使用我的记录器:

log << "some data" << logendl;   // correct //
log << "some data" << std::endl; // compiles -> wrong /

所以实际上我需要operator<<从我的类中删除带有std::endl机械手的类,并以其他方式解决它,但该怎么做呢?我正在考虑将宏更改std::endllogendl其他自定义操纵器,然后这个自定义操纵器将完成实际正在执行的工作operator<<,但我不知道该怎么做。我正在寻找其他解决方案,有什么建议吗?

4

3 回答 3

2

这就是我所做的。它有点绕开你的问题。也就是说, is 不需要定义一个endl. 我所做的是从构建消息的类中分离出一个Logger类(它只需要字符串和输出然后到你需要它们去的任何地方) 。LogMessage

好处是:

  • 每个类,就其自身而言,非常简单。

  • 非常简单的宏。我没有定义下面的宏,但它很容易做到。

  • 无需定义endl. LogMessage 类析构时消息以分号结束

让我知道你的想法:

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

// logger class
// this is not complete, it exists just to illustrate the LogIt function
class Logger
{
public:
    void LogIt(const std::string & s)
    {
        std::cout << s << std::endl;
    }
};

// builds a logging message; outputs it in the destructor
class LogMessage
{
public:
    // constructor
    // takes identifying info of message.  You can add log level if needed
    LogMessage(const char * file, const char * function, int line)
    {
        os << file << ": " << function << '('  << line << ") ";
    }

    // output operator
    template<typename T>
    LogMessage & operator<<(const T & t)
    {
        os << t;
        return *this;
    }

    // output message to Logger
    ~LogMessage()
     {
         Logger logger; // get logger here (perhaps it's a singleton?)
         logger.LogIt(os.str());
     }
private:
     std::ostringstream os;
};

int main()
{
// example usage
// typically this is invoked via a simple macro to reduce typing of the LogMessage constructor
LogMessage(__FILE__, __func__, __LINE__) << "this is an int " << 5;
}
于 2015-11-14T21:02:08.000 回答
1

您可能有一个LoggerAt带有LoggerAt(const char*filename, int lineno)构造函数的类(可能是 的子类std::ostringstream等),然后定义

#define LOG(Out) do {LoggerAt(__FILE__,__LINE__) \
  << Out << std::endl; }while(0)

在我的一些 C++ 项目中,我编写了代码:

void mom_inform_at(const char*fil, int lin, std::ostringstream& out)
{ out.flush(); 
  std::clog << fil << ":" << lin 
            << " INFORM: " << out.str() << std::endl ;
}

#define MOM_INFORM_AT(Fil,Lin,Output) do {      \
      std::ostringstream out_##Lin;               \
        out_##Lin << mom_outlog << Output ;       \
        mom_inform_at(Fil,Lin,out_##Lin);         \
    } while(0)

  #define MOM_INFORM_AT_BIS(Fil,Lin,Output) \
    MOM_INFORM_AT(Fil,Lin,Output)

  #define MOM_INFORM(Out)                         \
    MOM_INFORM_AT_BIS(__FILE__,__LINE__,Out)

并使用类似于MOM_INFORM("x=" << " point:" << pt);您可以想象Point pt;具有适当std::ostream& operator << (std::ostream&out, const Point&point)功能的通常示例的东西。

请注意,为了方便 __FILE__使用,__LINE__您最好使用宏。

于 2015-11-14T20:28:10.207 回答
0

我已经解决了我自己的问题。此处发布的其他答案可能比主要答案更好,但我想以一种简单的方式使用记录器,就像在 C++std::cout中使用的一样。此外,我的解决方案可能不是最佳的,可能会导致其他问题,但它符合我的要求。

我添加了一个自定义std::ostream

class CustomOstream : public std::ostream
{
  public:

    static CustomOstream& endl( CustomOstream& out )
    {
        return out;
    }
};

并更改宏以使用endl函数CustomOstream

#define __FILENAME__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/') + 1 : __FILE__)
#define logendl \
    ((ToolLogger::fileName = __FILENAME__).empty() ? "" : "") \
    << ((ToolLogger::line = __LINE__) ? "" : "") \
    << ((ToolLogger::function = __func__).empty() ? "" : "") \
    << ToolLogger::CustomOstream::endl

主类的也operator<<已更改

ToolLogger& operator<< (CustomOstream& (*f)(CustomOstream&))
{
    // do something //
    return *this;
}

现在记录器可以像我想要的那样使用

log << "some data" << logendl;   // correct //
log << "some data" << std::endl; // won't compile -> correct //
于 2015-11-23T19:50:48.283 回答