1

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

4

3 回答 3

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 tc

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

于 2010-10-08T19:32:01.700 回答
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);
于 2010-10-07T18:20:12.463 回答
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.

于 2010-10-07T19:52:30.857 回答