在Boost预处理器中混合变量和整数常量

时间:2010-10-07 18:05:06

标签: c boost-preprocessor

我使用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库存在是有原因的,这个问题也是一致的。但这可能是不可能的。

3 个答案:

答案 0 :(得分:2)

你正在尝试做一些最好留给编译器优化的东西。

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是“愚蠢的”。这不是一件坏事(实际上,这在许多情况下是什么使它如此有用),但在一天结束时,它是一个预处理器。它在解析源文件之前对其进行预处理。它在语义分析发生之前预处理源文件。它在检查给定源文件的有效性之前预处理源文件。我知道你不想听到这是解析器和语义分析器应该处理或通常做的事情。但是,这是现实情况。如果您想设计非常小的代码,那么您应该依靠编译器来完成它的工作,而不是尝试创建预处理器构造来完成工作。可以这样想:数千小时的工作进入你的编译器,所以尽可能多地重用这些工作!

答案 1 :(得分:1)

然而,

不是直接的方法:

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);

答案 2 :(得分:1)

没有真正混合两个级别的机会(预处理器和变量的评估)。根据我对你的理解,b应该是一个象征性的常数吗?

我认为你应该使用传统的

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

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

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

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