【问题标题】:Pass a char array to a function template such that gcc can tell it is a literal?将 char 数组传递给函数模板,以便 gcc 可以判断它是文字?
【发布时间】:2014-07-23 07:11:40
【问题描述】:

我正在尝试将 gcc 的 printf 格式字符串的编译时检查与 c++11 的 可变参数模板包结合起来。

我知道我可以使用 gcc 的 __attribute__((format(__printf__, ...)) 装饰器来装饰可变参数函数。

我知道我可以将可变参数模板包扩展为可变参数函数。例如:printf(fmt, pack...);

两者可以结合吗?

this question 中,接受的答案指出 gcc 需要 literals 才能进行格式检查。

在下面的示例中,const char array 被同时传递给可变参数函数和可变参数函数模板。当在main 中调用check 时,gcc 可以判断它是一个文字,但不是来自check_t

有什么方法可以将fmt 字符串传递给check_t,以使gcc 将其视为文字?

我正在努力实现的工作示例:

#include <iostream>

void check(const char*, ...) __attribute__((format(__printf__, 1, 2)));
void check(const char*, ...)
{ 
    // exists only for the purposes of catching bogus format strings
}

template<typename... Ts>
void check_t(const char fmt[], Ts... ts)
{
    // check the format string by expanding ts into the check function
    check(fmt, ts...);
}

int main()
{
    const char f[] = "foo %s";
    check_t(f, 5);              // compiles
    check(f, 5);                // fails to compile
    return 0;
}

【问题讨论】:

  • 当格式字符串不是编译时常量时会发生什么?
  • 据我所知,您无法将字符串文字与字符数组区分开来。
  • @KonradRudolph - gcc 的 printf 格式检查不起作用 - 一直都是这样
  • @KerrekSB 我想你的意思是“常量字符数组”

标签: c++ gcc c++11


【解决方案1】:

您可以在模板参数中传递 c 字符串:

template<const char* fmt, typename... Ts>
void check_t(Ts... ts)
{
    // check the format string by expanding ts into the check function
    check(fmt, ts...);
}

constexpr const char f[] = "foo %s";

int main()
{
    check_t<f>(5); // fails to compile
    //check(f, 5); // fails to compile
    return 0;
}

Live example

【讨论】:

  • 非常好 - 但不幸的是我无法使用这个 - 格式字符串必须作为参数传递
  • #define CHECK_T(FMT, ...) check_t&lt;FMT&gt;(__VA_ARGS__) 可能会有所帮助。
  • 我也需要能够传递非编译时常量,所以很遗憾这不起作用。最后,我想出了类似的东西,但并不理想,但可以让我继续前进:#define CHECK_T(fmt, ...) { check(fmt, __VA_ARGS__); check_t(fmt, __VA_ARGS__); }
  • 使用comma operator#define CHECK_T(fmt, ...) ( check(fmt, __VA_ARGS__), check_t(fmt, __VA_ARGS__) )
【解决方案2】:

如 cmets 中所述,不可能在函数模板中区分字符串文字和字符数组,除非它是模板参数。

因此利用 within 中的 gcc 的 __attribute__((format(__printf__, ...)) 语法是不可能的(除非格式字符串是模板参数而不是 @Jarod42 的答案中的函数参数) .

这种行为很容易让人联想到日志框架——你需要可变参数模板的类型安全和格式字符串的编译时检查。

解决方法:使用逗号运算符

虽然这不是特定问题的解决方案,但它是一种解决方法,可以兼顾两者。

它利用了comma operator,尤其是链接文章中描述的示例:

i = (a, b); // discards a, stores b into i 

因此宏变成这样:

#define CHECK_T(fmt, ...) ( check(fmt, __VA_ARGS__), check_t(fmt, __VA_ARGS__) )
                                       ^           ^              ^
                                       |           |              |
    calls variadic function `check` ---+           |              |
                                                   |              |
    comma operator discards 1st operand -----------+              |
                                                                  |
    returns result of variadic function template `check_t` -------+

尽管check 调用在编译期间会使格式字符串无效,但我的假设是它实际上会从二进制的发布版本中编译出来,因为它是一个空函数,只留下check_t 调用进行实际的日志记录。

【讨论】:

    猜你喜欢
    • 2012-01-12
    • 1970-01-01
    • 1970-01-01
    • 2020-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-08
    相关资源
    最近更新 更多