【问题标题】:Can I validate a preprocessor macro to make sure it's a number?我可以验证预处理器宏以确保它是一个数字吗?
【发布时间】:2014-11-29 16:52:46
【问题描述】:

我正在使用宏在我的代码中定义简单的变量(类似这样):

#define foobar 1
//...
barfoo(foobar);

为了防止错误,我想通过验证输入来确保foobar 是一个数字。 foobar 应该始终是一个数字,所以它可以很简单。

显然,预处理器不处理数学,因此任何使用算术产生某种类型错误的希望都落空了。

出于这个原因,我猜常量确实更好,但我正在尝试使用所有宏,因此它在我拥有的配置文件中是一致的(有些确实需要宏)。正则表达式可能是一个不错的解决方法,但 GCC 似乎不支持 [with macros] 这个(另外,http://xkcd.com/1171/)。

【问题讨论】:

  • GCC 从 4.9 开始支持正则表达式。无论如何,我想你必须先做#define ISNUM(x) ISNUM_##x(),然后是大量的#define ISNUM_0() 1 等。如果你没有为输入定义宏,你会得到一个错误。
  • @chris 不在宏中
  • @chris 这与这个问题有什么关系? GCC 的正则表达式支持用于运行时正则表达式匹配,没有预处理器扩展。
  • 我猜常量确实因为这个原因更好 - 你能否详细说明你的上下文/配置情况,以便人们可以理解如何可能建议你如何适应或合并您认为正确的解决方案?
  • @hvd,没错,我一开始并没有意识到它是 PP 的正则表达式,因为问题没有注释,而且一开始就提到缺乏支持似乎很奇怪众所周知的 GCC 就在后面。

标签: c++ validation macros c-preprocessor


【解决方案1】:

在 C++11 中,有一些类型特征和静态断言可以满足您的目的:

#include <type_traits>

#define foo 1
// #define foo "bar" // will lead to a compiler error containing the message "foo is not int"

static_assert(std::is_integral<decltype(foo)>::value, "foo is not int");

【讨论】:

    【解决方案2】:

    由于对该问题的评论说也应禁止使用表达式,因此涉及编译时检查表达式是否具有整数类型的答案将无法解决问题:这将检测到 1 之间没有区别, (1)(0+1),因为这三个都是 int 类型的纯右值,具有完全相同的值。

    因此,答案必须涉及查看宏定义本身,这可以通过将其字符串化,并将验证放入接受字符串的constexpr 函数中,并在静态断言中使用它。正十进制无后缀整数文字示例:

    #define A 1
    #define B 2
    #define C 3
    #define D (4)
    
    #define STR_(x) #x
    #define STR(x) STR_(x)
    
    constexpr bool is_number(const char *str) {
      return (*str >= '0' && *str <= '9') && (str[1] == '\0' || is_number(str+1));
    }
    
    #define VERIFY(x) static_assert(is_number(STR(x)), STR(x) " is not a number!")
    
    VERIFY(A); // passes
    VERIFY(B); // passes
    VERIFY(C); // passes
    VERIFY(D); // fails: (4) is not a number!
    

    如果您还需要处理后缀文字 (123L) 或非十进制文字 (0x123),则需要对其进行扩展。如果作为一个特殊例外,您希望允许将一元 - 应用于整数文字,那也需要扩展。

    这几乎肯定不值得防范。如果有人认为如果D 被定义为(4)(3+1)(C+1),而不是4,则代码更具可读性和/或可维护性,并且如果除了这一断言之外,这些定义将工作得很好,断言会很快从代码中删除。

    【讨论】:

      【解决方案3】:

      我建议您只在必要时使用宏,否则使用常量。一致性并不总是最重要的优先事项。

      【讨论】:

      • @Wintermute 在这里肯定回答了这个问题,但这几乎是一个不应该回答的问题。对此答案+1,因为我会说这是一个自己造成的问题。如果 OP 使用常量而不是宏,那么它们已经是正确的类型。
      • @void.pointer 我已经提交了对 Wintermute 的回答的编辑,带有这个警告。
      【解决方案4】:

      为此目的使用常量而不是定义是有充分理由的。

      Scott Meyers 在他的《Effective C++》一书中有一章专门讨论这个问题:
      http://books.google.com/books?id=U7lTySXdFk0C&pg=PT41&lpg=PT41#v=onepage&q&f=false

      第 2 项:“更喜欢 consts、枚举和内联而不是 #defines”。

      使用常量可以使调试更容易,因为编译器可以访问名称(而不仅仅是预处理器替换)。

      Defines 也没有相同的类型安全功能(这可能是您在这里尝试解决的问题)。它们没有相同的作用域特征。

      【讨论】:

        猜你喜欢
        • 2015-07-12
        • 2011-05-31
        • 1970-01-01
        • 1970-01-01
        • 2019-07-21
        • 1970-01-01
        • 2010-09-23
        • 2018-12-27
        • 2020-10-09
        相关资源
        最近更新 更多