【问题标题】:C++ Deferred Logging (Without C++11)C++ 延迟日志记录(无 C++11)
【发布时间】:2016-08-24 21:26:46
【问题描述】:

目前,我们的代码库有很多如下代码:

void log(int level, const char *msg) {
    // logLevel is some global int defining which messages to log
    if (level <= logLevel) {
        cout << msg << endl;
    }
}

...

int someNum = 3;
if (1 <= logLevel) {
    char msg[200];
    sprintf(msg, "Some format %d", someNum);
    log(1, msg);
}

我们使用的是 Visual Studio 2008,因此无法使用 C++11 的任何功能。有没有一种干净的方法可以将闭包传递给 log 方法,所以我可以删除重复的“if”条件?例如,我正在寻找与 pre C++11 语法中的以下代码等效的代码:

void log(int level, std::function<std::string ()> getMessage) {
    if (level <= logLevel) {
        cout << getMessage() << endl;
    }
}

...

int someNum = 3;
log(1, [someNum]() -> std::string {
    std::ostringstream sstream;
    sstream << "Some format " << someNum;
    return sstream.str();
});

我能想到的最好的方法是:

struct LogMessage {
    virtual std::string operator()() const = 0;
};

void log(int level, const LogMessage &getMessage) {
    if (level <= logLevel) {
        cout << getMessage() << endl;
    }
}

...

struct X : public LogMessage {
    X(int num) : myNum(num) { }
    std::string operator()() const {
        std::ostringstream out;
        out << "Some format " << myNum;
        return out.str();
    }
    private: const int myNum;
} a(someNum);
log(1, a);

【问题讨论】:

  • 您想避免 if 条件被测试两次(通过调用者和日志函数)?
  • 由于本地结构不能在模板中使用(在 c++03 中),我会说这是你能拥有的最好的。
  • @jpo38 是的,你的解释是正确的。
  • @JeffG:那么宏可以帮助你。它们通常用于设置跟踪系统。
  • 将函数传递给函数,然后在里面执行传递的函数,会不会比简单的第二次整数比较更糟糕?

标签: c++ logging c++03


【解决方案1】:

即使通常不推荐使用宏,它们也可以在这种特定情况下提供帮助,特别是如果您的所有文本都可以由流处理。

让你的日志函数成为一个宏:

#define LOG(level, msg) { if (level <= logLevel) { cout << msg << endl; } }

那么,如果你这样做:

LOG( 1, "Some format " << someNum )

if 测试已完成,只有在日志级别条件为 true 时,才会执行宏的第二个参数中完成的一个和任何复杂格式。

注意:对于使用 sprintf 的现有行,您仍然需要声明一个函数...:

inline std::string printNum( int someNum )
{
    char msg[200];
    sprintf(msg, "Some format %d", someNum);
    return msg;
}

LOG( 1, printNum(3) )

【讨论】:

  • 如果您的宏参数包含逗号(如他的 sprintf 示例),您始终可以用括号括住宏参数,以便逐字传递。你说如果不会做两次,但你的例子没有如果。你可以让你的例子成为问题中使用的例子吗?
  • @JerryJeremiah:我相信任何sprint 语句都可以替换为流操作。顺便说一句,在宏(if (level &lt;= logLevel))中肯定有一个if
  • 有趣的想法。许多有问题的行已经是 sprintf 风格,而另一些则结合了 sprintf 和 cout。我可以将所有内容转换为使用 cout 样式,但这需要大量工作,并且在转换过程中引入错误的风险非零(我们当前的代码库中有数千条日志语句)。我想我更喜欢让代码保持原样,而不是做所有需要执行宏路线的工作。
【解决方案2】:

为了补充@jpo38 提供的答案,只需添加另一个宏来接受可变参数以支持printf 风格的函数:

#define LOGF(LEVEL, ...)\
    if ((LEVEL) <= logLevel) {\
        char msg[200];\
        sprintf(msg, __VA_ARGS__);\
        log((LEVEL), msg);\
    }

那么以下任一调用都会产生相同的输出:

LOG(1, "Some format " << someNum);
LOGF(1, "Some format %d", someNum);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-07
    • 1970-01-01
    • 1970-01-01
    • 2015-12-21
    • 1970-01-01
    • 2022-07-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多