将可变模板参数传递给可变函数

11

我们正在使用一个第三方的C库,该库提供了一个类似于printf()的日志函数。

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);

所以编写上述函数是相对简单的事情。不幸的是,可变参数函数与内联不兼容,因此我想出了第二个解决方案:

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

这个代码完美运行并且内联了我们想要的速率限制条件。但是我有一些疑问:

  • 在C++11中,将参数包扩展到类似C风格的可变参数函数调用中是否合法和定义良好?或者我们只是走了运,它恰好起作用了?

  • 既然我们正在调用一个C函数,那么这里的 &&std::forward 是否真的必要?如果我使用const T&,甚至只是按值传递T,都可以正常工作,无论是否使用std::forward

2个回答

9

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

当你想要转发时,进行转发并没有什么坏处。但使用const&也传递了一些有用的信息。

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

这些都不关心引用。

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


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 " 与实际的 Fmt 进行拼接,Fmt 应该始终是一个字面量格式化字符串。典型用法为 RATE_LIMITED_LOG("x=%d y=%d", x, y);... 我承认这很低技术且非常类似于 C(不使用任何 C++11 工具),但在实践中它的效果相当好。在实践中,我在宏中使用特定于软件的前缀(例如我的 MOM_FATAPRINTFmonimelt.h 中,在 C 而不是 C++ 中)。

1
我个人会使用一个外部宏来注入__FILE____LINE__到内部函数中,因为调试宏生成的代码通常很痛苦。在宏中执行必要的操作(有时候像字符串连接这样的操作可以更快),而其他所有操作都在普通的代码中执行(PS:你需要在上面添加一些\)。 - Yakk - Adam Nevraumont

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接