【问题标题】:Are there directives in C/C++ preprocessor to convert string to number?C/C++ 预处理器中是否有将字符串转换为数字的指令?
【发布时间】:2013-11-20 01:24:55
【问题描述】:

我希望在我的代码中添加一些条件指令来控制不同的构建,例如:

#if VERSION > 100
/* Compiling here */
#endif

问题是“VERSION”在我无法更改的其他代码中。它被定义为一个字符串:

#define VERSION "101"

我想知道是否有某种宏或指令可以将字符串转换为数字,所以我可以简单地做

#if STRING_TO_NUMBER(VERSION) > 100
/* Compiling here */
#endif

请问可以吗?

PS。 看来我的描述不是很清楚。这个需求的主要目的是控制版本分支。例如,在旧版本 100 之前的版本中,该程序会使用 old_function()。在这个版本之后,所有函数都迁移到了 new_function。所以我需要写这样的代码:

#if VERSION >= 100
    old_function();
#else
    new_function();
#endif
#if VERSION >= 100
int old_function()
{
    ...
}
#else
int new_function()
{
    ...
}
#endif

你可以看到只有一个函数会被编译。因此条件必须在预处理阶段确定,而不是在运行时确定。

棘手的部分是,VERSION 被定义为一个字符串,这带来了这个问题。

【问题讨论】:

  • 在预处理阶段是否需要提供结果?或者您可以使用inline 函数或macro
  • 他们把宏变成了字符串太糟糕了——从宏的值中获取字符串很容易(使用# 'stringize' 运算符),但要反其道而行之并不容易据我所知。
  • 可能有一种方法可以使用模板元编程来代替。通过本质上调用或实例化基于版本的不同模板类。不过,您必须根据 VERSION 字符串创建一个类型。
  • 我需要这个来编写分解代码。编译器将通过不同的版本分支生成不同的基于库的执行。在这种情况下,必须在预处理阶段完成。否则会命中链接错误。

标签: c c-preprocessor preprocessor-directive


【解决方案1】:

如果您需要与预处理器交互以设置其他#defines 或有条件的#include 不同的标头。直到您可以将 VERSION “固定”为整数...

我能想到的唯一一件事就是创建一个定义 PROPER_VERSION 的小头文件,并通过将每个文件命名为版本号来包含它。所以在这里你会创建:

100:
#define PROPER_VERSION 100

101:
#define PROPER_VERSION 101

102:
#define PROPER_VERSION 102

然后您需要添加以下内容:

#include VERSION

然后根据需要使用PROPER_VERSION

#if PROPER_VERSION > 100
...

它并不优雅,但我看不出还有什么可以做的。您可以自动生成 VERSION 文件。

【讨论】:

  • 谢谢格伦。它看起来没有进行条件编译的低成本方法。我将使用脚本来转换字符串。
【解决方案2】:

只要您不需要在VERSION 上进行声明或预处理器定义条件,并且只要您确信VERSION 字符串将只是一个没有前导零的整数,(两者都是可能要求太多),如果您的编译器具有相当有效的常量表达式评估器,您可能能够在编译时执行此操作。

例如,gcc 4.8 将优化掉以下if 测试,只留下适当的手臂:

if (strlen(VERSION) > 3 || (strlen(VERSION) == 3 && strcmp(VERSION, "100") > 0)) {
  // code if VERSION is at least "101"
} else {
  // code if VERSION is "100" or less
}

if 语句只有一个分支在编译过程中幸存下来这一事实很容易通过在未使用的分支中插入对未定义函数的调用来证明。使用 gcc(和 clang)并启用优化,不会产生链接器错误:

#include <stdio.h>
#include <string.h>
#define VERSION "101"
// This function is not available for linking
int unknown_function();
// This one is in standard library
int rand();
int main(void) {
    int x;
    if (strlen(VERSION) > 3 || (strlen(VERSION) == 3 && strcmp(VERSION, "100") > 0)) {
      // code if VERSION is at least "101"
      x = rand();
    } else {
      // code if VERSION is "100" or less
      x = unknown_function();
    }
    printf("%d\n", x);
    return 0;
}

(查看http://ideone.com/nGLGTH


在 C++11 中,有一个更清晰的编译时版本。您可以创建constexpr 版本的atoi。结合某些人可能称之为滥用模板的行为,允许条件声明:

constexpr int const_atoi(const char* num, int accum=0) {
  return *num ? const_atoi(num+1, accum*10 + (*num - '0')) : accum;
}

template<bool V_GT_100> struct MoreOrLess_Impl;
template<> struct MoreOrLess_Impl<false> {
  // Old prototype
  void doit(double x) {...}
};
template<> struct MoreOrLess_Impl<true> {
  // New prototype
  void doit(long double x) {...}
};
using MoreOrLess = MoreOrLess_Impl<(const_atoi(VERSION) > 100)>;

// ...
// ... MoreOrLess::doit(x) ...

http://ideone.com/H1sdNg 的愚蠢示例)

【讨论】:

  • 这不允许预处理器指令有条件地包含一个或另一个标头 - 否则 (VERSION[0]-'0')*100+(VERSION[1]-'0')*10+VERSION[2]-'0' 就足够了
  • 恐怕行不通。 strlen() 是一个运行时函数。但是我需要在编译阶段处理源代码,这样我就可以控制不同的库分支。
  • @user2023470:大多数编译器会在编译时评估strlen("....");我检查了我提供的示例,没有留下任何 if 的痕迹供运行时使用。但是,它不会让您有条件地#define 事物,或有条件地#include 标头。
  • @user2023470:另外,C++11 版本显然是在编译时发生的。在某种程度上,像这样的模板内容是“清晰的”:)
  • @GlennTeitelbaum:也许我误解了你的评论。我使用了我使用的公式(可以通过 gcc 进行编译时优化)以避免假设 VERSION 是特定长度。
猜你喜欢
  • 1970-01-01
  • 2012-09-09
  • 2010-11-25
  • 2017-02-25
  • 2011-01-12
  • 2011-08-17
  • 2010-09-19
相关资源
最近更新 更多