【问题标题】:Mixing variables and integer constants in the Boost Preprocessor在 Boost 预处理器中混合变量和整数常量
【发布时间】:2010-10-07 18:05:05
【问题描述】:

我正在使用 BOOST_PP 在预处理器中进行预编译计算。我专注于代码大小对我来说极其重要的应用程序。 (所以请不要说编译器应该通常这样做,我需要控制在编译时执行什么以及生成什么代码)。但是,我希望能够对整数常量和变量使用相同名称的宏/函数。作为一个简单的例子,我可以有

#define TWICE(n) BOOST_PP_MUL(n,2)
//.....
// somewhere else in code
int a = TWICE(5);

这是我想要的,评估为

int a = 10;

在编译期间。

但是,我也希望它用于

int b = 5;
int a = TWICE(b);

这应该被预处理为

int b = 5;
int a = 5 * 2;

当然,我可以通过使用传统的宏来做到这一点,比如

#define TWICE(n) n * 2

但是它并没有做我希望它为整数常量做的事情(在编译时评估它们)。

所以,我的问题是,是否有一个技巧可以检查参数是文字还是变量,然后使用不同的定义。即,像这样:

#define TWICE(n) BOOST_PP_IF( _IS_CONSTANT(n), \
                              BOOST_PP_MUL(n,2), \
                              n * 2 )

编辑: 所以我真正追求的是某种方法来检查某些东西在编译时是否可用,因此是 BOOST_PP_ 函数的一个很好的论据。我意识到这与大多数人对预处理器和一般编程建议的期望不同。但是没有错误的编程方式,所以如果你不同意它的理念,请不要讨厌这个问题。 BOOST_PP 库的存在是有原因的,这个问题也是如此。不过,这可能是不可能的。

【问题讨论】:

  • 你可以通过使用模板而不是预处理器来获得这个
  • @aaa 模板可以选择常量吗?如果可以的话,我很乐意(即使模板需要 c++ 编译)。但我不知道如何编写这样的模板。
  • 作为替代方案,你考虑过boost.org/doc/libs/1_44_0/libs/wave/index.html
  • cboost 在一起?只是问......它可以完全没问题
  • @pmg 看来 boost.pp 与纯 c 兼容。

标签: c boost-preprocessor


【解决方案1】:

您正在尝试做一些最好留给编译器优化的事情。

int main (void) {
  int b = 5;
  int a = b * 2;

  return a; // return it so we use a and it's not optimized away
}

gcc -O3 -s t.c

 .file "t.c"
 .text
 .p2align 4,,15
.globl main
 .type main, @function
main:
.LFB0:
 .cfi_startproc
 movl $10, %eax
 ret
 .cfi_endproc
.LFE0:
 .size main, .-main
 .ident "GCC: (Debian 4.5.0-6) 4.5.1 20100617 (prerelease)"
 .section .note.GNU-stack,"",@progbits

优化编译器会优化。

编辑:我知道您不想听到编译器“应该”或“通常”这样做。但是,您尝试做的事情并不是要在 C 预处理器中完成的;设计 C 语言和 C 预处理器的人将其设计为与预处理令牌一起工作,因为它是基本原子。在许多方面,CPP 是“愚蠢的”。这不是一件坏事(事实上,在许多情况下,这正是它如此有用的原因),但归根结底,它是一个预处理器。它在解析源文件之前对其进行预处理。它在语义分析发生之前预处理源文件。它在检查给定源文件的有效性之前预处理源文件。我知道您不想听到这是解析器和语义分析器应该处理或通常会处理的事情。然而,这就是现实的情况。如果你想设计非常小的代码,那么你应该依靠你的编译器来完成它的工作,而不是试图创建预处理器结构来完成工作。可以这样想:编译器投入了数千小时的工作,因此请尝试尽可能多地重用这些工作!

【讨论】:

  • 谢谢!我喜欢你的回答。看到汇编代码,我可以看到编译器确实对其进行了优化。我不确定它是否会为高阶函数这样做,以及它们涉及控制逻辑的地方。但是看到的唯一方法是通过实验,所以我会回去检查我感兴趣的功能。
  • 这里最好的做法是进行实验。如果您使用 GCC 或 clang,您可以通过多种方式检查编译器正在做什么以及编译器没有做什么。您甚至可能想查看 clang 静态分析器,这是一个非常酷的工具,用于检查 C 或 C++ 源代码是如何编译的。 Visual Studio 也为这类事情提供了极好的工具。
【解决方案2】:

但不是很直接的方法:

struct operation {
    template<int N>
    struct compile {
        static const int value = N;
    };
    static int runtime(int N) { return N; }
};

operation::compile<5>::value;
operation::runtime(5);

或者

operation<5>();
operation(5);

【讨论】:

  • 嗯,它看起来仍然像两个函数,调用方式不同。它们似乎只是驻留在同一个结构中。我也可以制作两个不同的宏,就像在我的示例中一样,然后调用适当的宏,这取决于我想要编译时间还是运行时计算。我实际上的目标是一个选择正确代码的界面。
  • @highBandWidth 啊好吧,我明白了……可能很难做到
【解决方案3】:

没有真正的机会混合两个级别(预处理器和变量的评估)。根据我对您的问题的理解,b 应该是一个符号常量?

我认为你应该使用传统的

#define TWICE(n) ((n) * 2)

但是你应该用编译时常量来初始化它们,而不是用表达式来初始化变量。我看到的唯一在编译时强制评估并在 C 中具有符号常量的是整数枚举常量。这些被定义为具有int 类型并在编译时进行评估。

enum { bInit = 5 };
int b = bInit;
enum { aInit = TWICE(bInit) };
int a = aInit; 

通常你不应该对const(至于你的b)过于节俭,并使用-S检查生成的汇编程序。

【讨论】:

  • 我明白你的意思。我实际上正在尝试编写一个代码大小非常重要的库,并且我试图提供一个可以使用常量和变量调用的接口。在这种情况下,它们可能是真正的变量,必须在运行时进行评估。可能做不到。
  • @highBandWidth:如果这对您来说如此重要,您应该真正确定这些是编译时表达式的点,并按照我的回答执行这些操作。如果这样做,编译器会立即告诉您是否尝试使用动态表达式声明枚举常量,因此这是一个很好的测试。对于其他地方,您无法运行时评估,因此无论如何您都必须使用经典宏。
  • 我同意。虽然我正在尝试编写一个库,所以我没有选择检查编译时间问题然后返回更改代码的选项。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-30
  • 2016-08-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多