有没有办法检查C / C ++中预处理器符号值的类型

时间:2012-01-23 19:07:15

标签: c types c-preprocessor

我的部分代码取决于预处理器符号的值:

int a()
{
#if SDK_VERSION >= 3
    return 1;
#else
    return 2;
#endif
}

比较取决于SDK_VERSION的值。它应该是一个整数或比较整数的东西,在本例中为3.如果SDK_VERSION是无法与整数比较的东西,则会出现编译错误。

如果SDK_VERSION不是预期的类型,有没有办法中止编译?例如:

#if type(SDK_VERSION) != int  # Does not compile, I know
#error "SDK_VERSION must be an integer."
#endif

7 个答案:

答案 0 :(得分:12)

使用模板生成此类错误:

template<typename T> struct macro_error;

template<> struct macro_error<int> {};

template<typename T> void check(T) 
{ 
   macro_error<T> SDK_VERSION_must_be_int; 
}
int ignored = (check(SDK_VERSION), 0);

如果SDK_VERSION不是int,则此代码将生成编译错误,其中包含以下字符串:

SDK_VERSION_must_be_int

请参阅以下演示:

并且还注意到第一种情况中的错误消息。它打印出来:

prog.cpp:9: error: ‘SDK_VERSION_must_be_int’ has incomplete type
prog.cpp:9: warning: unused variable ‘SDK_VERSION_must_be_int’

答案 1 :(得分:4)

不。原因是预处理器符号根本没有类型。

预处理器符号最好被认为只是一个字符串。 C的类型系统直到编译步骤才真正存在,这在预处理器运行之后就会发生。

答案 2 :(得分:2)

如果SDK_VERSION是一个字符串,这将无法编译(但它适用于float ...):

int get_SDK_Version()
{
    return SDK_VERSION;
}

答案 3 :(得分:0)

预处理程序不了解类型或任何语言关键字。

答案 4 :(得分:0)

我想你可以在这里拉一些模板元编程。

如果我有所作为,我会尝试自己的魔法并发布一个例子。我的理论是你可以实例化一个类或函数模板。如果模板的默认定义不合适,则会导致编译错误。但int的模板专门化不会导致错误。

这超出了我对模板和预处理器魔术的经验范围,但值得一看。我期待的一个问题是SDK_VERSION实际上并不仅仅是要替换的符号。 SDK_VERSION本身没有类型。

充其量,我认为你可以检查它可以转换成什么类型​​,但是预处理器定义为“常量”的概念非常......暴力,几乎是hackish。这可能是一种常见的做法,但它根本不安全。最好记住它们只是查找和替换的预处理器版本。

答案 5 :(得分:0)

事实上,#if指令将整数的权利视为整数。

因此,如果你想达到你所要求的,你必须做一些算术。例如,使用以下命令执行test.c文件:

#define VERSION 7

#if VERSION
  #if VERSION - (VERSION % 10 )
    #warning Number out of range (1-9)
  #else
    #warning Number in range (1-9)
  #endif
#else
  #warning Zero or not a number
#endif

使用

进行编译
gcc -c -o /dev/null test.c

您将收到消息: “零不是数字”...如果您的VERSION为0,或者不作为整数评估(预处理器)。

如果VERSION按整数评估,您将根据其值获得第一条或第二条消息。

这将允许您执行您要搜索的内容。

#if:http://gcc.gnu.org/onlinedocs/cpp/If.html

上的文档

请注意,整数可以表示为:123或0xCC,或者在递归宏扩展后计算为整数常量的任何内容。

如果数字是浮点数,如:3.14则认为是零。

你不能简单地将0(整数)与不是整数的东西区分开来。但是,使用宏字符串连接可能存在一种可能性,但仍有待探索。

答案 6 :(得分:0)

好主意@Navaz! 我必须找到特定类型的参数进行简单的调整,并且很容易找到未列出类型的剩余调用,以防万一:

template<typename T> struct ints_error;
template<typename T> struct double_error;

template<> struct ints_error<uint8_t> {};
template<> struct ints_error<uint16_t> {};
template<> struct ints_error<int64_t> {};
template<> struct double_error<double> {};

template<typename T> int check_int(T)
{
    ints_error<T> must_be_listed;
    (void)must_be_listed;
    return 0;
}
template<typename T> int check_double(T)
{
    double_error<T> must_be_double;
    return must_be_double, 0;
}
#define sqlite3_bind_int(a,b,c) check_int(c);
#define sqlite3_bind_int64(a,b,c) check_int(c); 
#define sqlite3_bind_double(a,b,c) check_double(c);