【问题标题】:Passing variadic template arguments to a variadic function将可变参数模板参数传递给可变参数函数
【发布时间】:2014-10-20 04:20:21
【问题描述】:

我们正在使用第三方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);

所以写上面的函数是一件比较简单的事情。然而不幸的是variadic functions don't play well with 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 函数的情况下,&amp;&amp; 和 std::forward 在这里真的必要吗?如果我使用const T&amp;,甚至只是按价值使用T,不管有没有std::forward,它似乎也能正常工作。

【问题讨论】:

    标签: c++ c++11 variadic-templates variadic-functions perfect-forwarding


    【解决方案1】:

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

    而且想转发的时候转发也没有坏处。但是通过const&amp; 获取也可以传达一些有用的信息。

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

    这些都不关心引用。

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

    【讨论】:

      【解决方案2】:

      我不确定内联对于日志记录功能是否重要。事实上,许多 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 的字符串文字连接,它应该始终是文字形式的字符串。典型用法是RATE_LIMITED_LOG("x=%d y=%d", x, y);...

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

      【讨论】:

      • 我自己,我会使用将__FILE__ 和__LINE__ 注入内部函数的外部宏,因为调试宏生成的代码通常很痛苦。在宏中做需要做的事情(有时像字符串连接,这可能会更快),其他一切都用普通的旧代码(PS:你需要更多的上面)
      猜你喜欢
      • 2011-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-07
      • 2011-11-15
      • 1970-01-01
      相关资源
      最近更新 更多