10

我们正在使用第三方 C 库,它提供了printf()-style 日志功能,

void log(const char *format, ...);

由于不值得讨论的原因,我们需要限制记录消息的速率,类似于

void rate_limited_log(const char* format, ...)
{
    if (<not too fast>) {
         log(format, ...);
    }
}

幸运的是,C 库的作者知道他们在做什么,并提供了

void logv(const char* format, va_list ap);

所以写上面的函数是一件比较简单的事情。不幸的是,可变参数函数不能很好地使用 inlining,所以我想出了第二个解决方案:

template <typename... T>
void rate_limited_log(const char* format, T&&... args)
{
    if (<not too fast>) {
        log(format, std::forward<T>(args)...);
    }
}

这完美地工作并根据我们的需要内联速率限制条件。但我有几个问题:

  • 将参数包扩展为像这样的 C 风格的可变参数函数调用在 C++11 中是合法的、定义明确的事情,还是我们很幸运它可以工作?

  • 鉴于我们正在调用 C 函数,这里是否真的有&&必要?如果我使用,甚至只是按价值,无论有没有 ,std::forward它似乎都可以正常工作。const T&Tstd::forward

4

2 回答 2

8

将参数包扩展为可变参数是有效的。

而且想转发的时候转发也没有坏处。但采取const&也传达一些有用的东西。

传递给的值...将经历“默认参数提升”。请参阅http://en.cppreference.com/w/cpp/language/variadic_arguments

这些都不关心引用。

你可以改进你的代码。您可以Ts...通过“类型类型”和实际解析格式化字符串和确认参数的数量(有时是类型)来检查传递给打印例程的有效类型。如果失败,您可以记录错误消息而不是崩溃。

于 2014-08-26T20:48:07.373 回答
3

我不确定内联对于日志记录功能是否重要。事实上,许多 C 编译器不会内联可变参数函数。但是,您可以将其设为宏

#define RATE_LIMITED_LOG(Fmt,...) do { \
   if (not_too_fast())      \
     log(Fmt,__VA_ARGS__);  \
} while(0)

特别是对于日志功能,将它们设为宏是很好的,因为它们可以使用__LINE__并且__FILE__喜欢这样

#define RATE_LIMITED_LOG_AT2(Fil,Lin,Fmt,...) do {
   if (not_too_fast())
     log("%s:%d " Fmt, Fil, (Lin), __VA_ARGS__);
} while(0)
#define RATE_LIMITED_LOG_AT(Fil,Lin,Fmt,...) \
   RATE_LIMITED_LOG_AT2(Fil,Lin,Fmt,__VA_ARGS__)
#define RATE_LIMITED_LOG(Fmt,...) \
   RATE_LIMITED_LOG_AT(__FILE__,__LINE__,Fmt,__VA_ARGS__)

"%s:%d "请注意与 real的字符串文字连接Fmt,它应该始终是文字形式的字符串。典型的用途是RATE_LIMITED_LOG("x=%d y=%d", x, y);......

我承认它技术含量低且非常像 C(不使用任何 C++11 小工具),但它在实践中运行良好。在实践中,我对宏使用了特定于软件的前缀(例如我MOM_FATAPRINTFmonimelt.h中的,它在 C 而不是 C++ 中)。

于 2014-08-26T18:24:45.433 回答