【问题标题】:How to check a value like "#define VERSION 3.1.4" at compile time?如何在编译时检查“#define VERSION 3.1.4”之类的值?
【发布时间】:2015-09-04 16:56:03
【问题描述】:

我正在向我公司的 C++ 项目添加编译时检查,以确保所有开发机器和构建服务器上的第三方库都是最新的。大多数库定义如下内容,例如3.1.4 版:

#define VERSION_MAJOR 3
#define VERSION_MINOR 1
#define VERSION_BUILD 4

使用static_assert 或预处理器指令很容易检查。

现在我正在寻找一个定义单个宏的第三方库:

#define VERSION 3.1.4

如何在编译时验证此类宏的值?


使用 C++11,我可以使用 constexpr 字符串比较函数,并将宏字符串化以检查它:

constexpr bool static_equal(const char * a, const char * b)
{
    return (*a == *b) && (*a == '\0' || static_equal(a + 1, b + 1));
}

// stringification functions
#define str(x) #x
#define xstr(x) str(x)

static_assert(static_equal(xstr(VERSION), "3.1.4"), "incorrect version of libwhatever");

但是我们在 Windows 机器上使用 Visual Studio 2013,所以我只能使用它支持的 C++11 子集。很遗憾,不支持 constexpr

【问题讨论】:

  • 维护另一个文件可能更容易按照您希望的方式拆分版本。
  • 恐怕你不能在预处理器中。比较仅适用于整数,无法拆分令牌 3.1.4。它需要是某种外部文本处理步骤,或在运行时检查。 VS2015 rc 已经发布,可能支持 constexpr。
  • @RSahu:这是一个第三方库。如果我想向其中添加文件,我必须创建自定义包并强制开发人员使用这些包。这比从原始来源下载,或者对于 Debian 及其衍生版本,apt-get libwhatever-dev 下载要繁琐得多。
  • 顺便说一下,static_assert(xstr(VERSION)[0] == '3', "..") 等因“C2057:预期的常量表达式”而失败。 @jxh:这也是我从你的建议中得到的。
  • @PaperBirdMaster:Visual C++ 12.0(又名 2013)和 g++ 4.8.2。只有当我尝试使用宏而不对其进行字符串化时,才会出现此类错误。

标签: c++ c++11 c-preprocessor


【解决方案1】:

这是我现在正在做的事情:

#define str(x) #x
#define xstr(x) str(x)

#include xstr(libwhatever.version.is.VERSION.should.be.3.1.4)

除此之外,我还将一个名为 libwhatever.version.is.3.1.4.should.be.3.1.4 的空文件添加到项目中。所以如果版本是正确的,预处理器就会成功的包含这个文件。否则,它将失败并显示“无法打开'libwhatever.version.is.2.7.2.should.be.3.1.4',没有这样的文件或目录”。最后,以某种有意义的消息使构建失败才是最重要的。

当然这种方法不是很灵活;例如,我无法检查最小版本或一系列版本。但对我来说,能够检查确切的值就足够了。

这似乎适用于 Visual C++ 和 g++。不过,我不确定该行为是否完全符合标准。

【讨论】:

    【解决方案2】:

    如果您右键单击您的项目->属性->构建事件->预构建事件 您将看到一个显示“命令行”的选项。您可以在此处调用另一个程序。

    您可以用 C++ 或您喜欢的任何语言编写另一个程序来检查您的文件(或您想要的任意数量的文件)是否有“#define VERSION 3.1.4”。您可以中止构建并在该程序中添加您想要的任何警告。

    这里有一个教程:https://dillieodigital.wordpress.com/2012/11/27/quick-tip-aborting-builds-in-visual-studio-based-on-file-contents/

    相关阅读:https://msdn.microsoft.com/en-us/library/e85wte0k.aspx

    我尝试弄乱预处理器命令很长时间,但我找不到仅使用预处理器命令的方法。

    【讨论】:

      【解决方案3】:

      你不能在预处理器中,但你可以滥用类型特征!

      VS 2013 似乎支持可变参数模板。尝试在https://stackoverflow.com/a/15912824/2097780 上使用宏CSTRING(您应该能够将constexpr 替换为const 并且代码仍然可以工作)并执行以下操作:

      #define STRT(x) decltype(CSTRING(x))
      static_assert(std::is_same<STRT(VERSION), STRT("3.1.4")>::value, "incorrect version of libwhatever");
      

      编辑:这行不通。但是,如果您的编译器编译它没有错误:

      extern const char data[] = "abc";
      template <char C> struct x {
          static const char c = C;
      };
      char buf[(int)x<"ABC123"[0]>::c];
      int main() { return (int)buf; }
      

      那你可以试试这个:

      #include <type_traits>
      #define VERSION 1.2.3
      #define STR2(x) #x
      #define STR(x) STR2(x)
      
      template <char...> struct ststring;
      
      // https://stackoverflow.com/a/15860416/2097780
      
      #define MACRO_GET_1(str, i) \
          (sizeof(str) > (i) ? str[(i)] : 0)
      
      #define MACRO_GET_4(str, i) \
          MACRO_GET_1(str, i+0),  \
          MACRO_GET_1(str, i+1),  \
          MACRO_GET_1(str, i+2),  \
          MACRO_GET_1(str, i+3)
      
      #define MACRO_GET_16(str, i) \
          MACRO_GET_4(str, i+0),   \
          MACRO_GET_4(str, i+4),   \
          MACRO_GET_4(str, i+8),   \
          MACRO_GET_4(str, i+12)
      
      #define MACRO_GET_64(str, i) \
          MACRO_GET_16(str, i+0),  \
          MACRO_GET_16(str, i+16), \
          MACRO_GET_16(str, i+32), \
          MACRO_GET_16(str, i+48)
      
      #define MACRO_GET_STR(str) MACRO_GET_64(str, 0), 0
      
      static_assert(std::is_same<ststring<MACRO_GET_STR(STR(VERSION))>,
                                 ststring<MACRO_GET_STR("1.2.3")>>::value,
                    "invalid library version");
      

      【讨论】:

      • decltype 将只检查字符串文字的长度,而不是字符内容
      • @BenVoigt 不,不会。我以前在我的代码中使用过这个。 decltype 将返回 string&lt;'3', '.', '1', '.', '4'&gt; 或类似的。
      • 哦,CSTRING 宏太神奇了。不,没有 constexpr 将无法工作。
      • @BenVoigt 即使用static const 代替?
      • 问题不在于 constexpr 关键字本身,而是在不支持任意编译时常量表达式的情况下,下标不会产生编译时常量表达式,因此不能在模板参数中使用。
      猜你喜欢
      • 2010-12-06
      • 2012-03-30
      • 2012-06-10
      • 2012-09-09
      • 1970-01-01
      • 2011-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多