1

我想设计一个具有以下特点的日志功能:

  • 基于 std::string 而不是 char*
  • 支持可变数量的变量,例如 printf
  • 接受作为第一个参数的严重性级别
  • 避免严重级别低于日志级别时的格式化开销
  • 像 printf 一样简单,或者几乎如此

我倾向于使用 boost::format 因为它的自动类型转换。但这里有一些我看到的问题:

它的语法有点尴尬:有点难看format("Mgr %s on pid %d is in state %s" % mgr % pid % s)(变量的列表性质没有逗号就不那么明显了)。日志调用如下所示:

mylog(INFO, format("Mgr %s on pid %d is in state %s" % mgr % pid % s));

更糟糕的是,是否有可能实现 mylog() 来检查我们是否在构造格式对象 之前记录了 INFO 消息?

我想到的另一种看起来更接近 printf 的方法是

mylog(INFO, "Mgr %s on pid %d is in state %s", mgr, pid, s);

甚至

mylog_info("Mgr %s on pid %d is in state %s", mgr, pid, s);

实现将类似于:

mylog(int severity, string pattern, ...) {
    if (severity >= LOGLEVEL) {
        boost::format fmt(pattern);
        for parm /* remaining parameters */ {
            fmt % parm; // feed into format one at a time
        }
    }
}

这肯定会推迟格式对象的构建,直到需要它。但是据我所知,在遍历变量参数列表时,无法判断您何时到达终点!

有人可以建议一种语法上简单的技术来实现这一点吗?

注意:我有 g++ 4.4,它不支持所有的 c++11(虽然它支持可变参数模板)

4

2 回答 2

1

您可以使用可变参数模板和递归。

注意:由于您提到 GCC 4.4,因此该编译器可以使用此功能,但默认情况下未启用。您必须将-std=c++0xor-std=gnu++0x选项添加到编译器才能启用该功能。

解决方案可以按照以下方式实现:

// Does the actual logging of the formatted message
void mylog_r (int severity, boost::format &fmt) {
  std::cout << "[" << severity << "] "
            << fmt
            << std::endl;
}

// Unpacks the variadic arguments one at a time recursively
template <typename T, typename... Params>
void mylog_r (int severity, boost::format &fmt, T arg, Params... parameters) {
  mylog_r(severity, fmt % arg, parameters...); // recursively unpack
}

// This version of mylog() checks severity and converts fmt to boost::format
template <typename... Params>
void mylog (int severity, std::string fmt, Params... parameters) {
  if (severity < LEVEL) return;
  boost::format bfmt(fmt);
  mylog_r(severity, bfmt, parameters...);
}
于 2013-08-21T03:21:37.300 回答
0

你可以做类似的事情,即像 printf 的实现:

#include <stdio.h>
#include <stdarg.h>
#include <string>

using namespace std;

enum Info_t
  {
    NONE,
    WARNING,
    ERROR   // so on
  };

int myLog(Info_t severity, string format, ...)
{
  int done = 0;
  if ( severity > WARNING ) {
    va_list arg;

    va_start(arg, format.c_str());
    done = vfprintf(stdout, format.c_str(), arg);
    va_end(arg);
  }

  return done;
}

int main()
{
  myLog(NONE, "Print %s %d.\n", "NONE", 0);
  myLog(WARNING, "Print %s %d.\n", "WARNING", 1);
  myLog(ERROR, "Print %s %d.\n", "ERROR", 2);
}
于 2013-08-21T02:55:40.807 回答