【问题标题】:Is there way to check the type of a preprocessor symbol value in C/C++有没有办法检查 C/C++ 中预处理器符号值的类型
【发布时间】:2023-04-07 21:44:01
【问题描述】:

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

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

【问题讨论】:

  • 错误信息会更晦涩一些,但在包含SDK 的标头中添加类似int ___SDK_VERSION_SHOULD_BE_CONVERTIBLE_TO_INT___ = SDK_VERSION; 的内容可能会解决您的问题。
  • 如果SDK_VERSION 的类型错误,您说您已经收到编译错误,您想要更好/不同的错误消息吗?
  • @FrédéricHamidi:忽略与标识符中的双下划线相关的问题。
  • @Loki,实际上,它们是三个下划线,以防万一:)
  • @FrédéricHamidi:没关系。连续 2 个下划线由实现保留。请参阅stackoverflow.com/a/228797/14065(以下划线和大写字母开头的标识符也是如此)。

标签: c types c-preprocessor


【解决方案1】:

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

int get_SDK_Version()
{
    return SDK_VERSION;
}

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 哦,虽然我喜欢 Jason 的回答——聪明,但一定要添加一些文档,因为当编译失败时,sn-p 的意图是检查 SDK_VERSION 的类型并不太清楚.
    【解决方案3】:

    使用模板产生这样的错误:

    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’
    

    【讨论】:

      【解决方案4】:

      预处理器不知道类型或任何语言关键字。

      【讨论】:

        【解决方案5】:

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

        我会尝试一些我自己的魔法,如果我得到一些工作,我会发布一个例子。我的理论是你可以实例化一个类或函数模板。如果模板的默认定义不是适当的类型,则会导致编译错误。但是,int 的模板特化不会导致错误。

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

        充其量,我认为您可以检查它可以转换为哪些类型,但是预处理器定义的“常量”的概念非常...蛮力,几乎是骇人听闻的。这可能是一种常见的做法,但它一点也不安全。最好记住它们只是查找和替换的预处理器版本。

        【讨论】:

          【解决方案6】:

          事实上,#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(整数)与非整数区分开来。虽然可能有一种可能性,使用宏字符串连接,但仍有待探索。

          【讨论】:

            【解决方案7】:

            好主意@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);
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-06-29
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多