技巧:使用宏填充数组值(代码生成)

C ++模板是伪装的宏吗?

我正在阅读上面的主题,突然想到了这个想法:为什么不尝试编写一些可以在我们的真实代码中使用的棘手宏(不仅仅是作为现实生活中无用的谜题)?

所以首先想到的是:用宏填充数组值:

int f(int &i) { return ++i; } #define e100 r5(m20) #define m20 m5,m5,m5,m5 #define m5 r5(e1) #define e1 f(i) //avoiding ++i right here, to avoid UB! #define r5(e) e,e,e,e,e int main() { int i=0; //this is used in the macro e1 int a[] = {e100}; //filling array values with macros! int n = sizeof(a)/sizeof(int); cout << "count = " << n << endl; for(int i = 0 ; i < n ; i++ ) cout << a[i] << endl; return 0; } 

输出:

 count = 100 1 2 3 4 . . . 100 

在线演示: http : //www.ideone.com/nUYrq

我们可以在紧凑性或通用性(可能两者)方面进一步改进这个解决方案吗? 我们可以摆脱宏中需要的变量i吗? 还是其他任何改进?

我还想知道这是否是C ++和C中的有效代码(当然忽略了打印部分)?

编辑:

我意识到调用f()的顺序似乎仍未指定 。 我不确定,因为我认为数组初始化中的逗号与逗号运算符(通常) 一样。 但如果确实如此,我们能否避免它,标准的哪一部分说明未指明

如果您希望深入研究预处理器编程,我只能推荐Boost.Preprocessor库作为构建块,您将避免从头开始重写。

例如,为了创建你的表,我会使用( ideone ):

 #include  #include  #define ORDER(z, n, text) n int main() { int const a[] = { BOOST_PP_ENUM(100, ORDER, ~) }; std::size_t const n = sizeof(a)/sizeof(int); std::cout << "count = " << n << "\n"; for(std::size_t i = 0 ; i != n ; ++i ) std::cout << a[i] << "\n"; return 0; } 

并把所有的遗嘱留给Boost 🙂

注意:这枚举从0到99,而不是1到100,还有其他操作可用于执行算术;)

编辑:这是如何工作的?

首先,我只推荐BOOST_PP_ENUM的doc条目

BOOST_PP_ENUM是一个带有3个参数的宏: (n, MACRO, data)

  • n :整数
  • MACRO :接受3个参数的宏: (z, i, data)
  • data :一些数据,方便您传递给macro

然后它将被逗号分隔的n次连续MAC调用取代:

 MACRO(z, 0, data), MACRO(z, 1, data), ... , MACRO(z, n-1, data) 

你可以用MACRO做任何你想做的事情。

我恐怕我从未使用过z参数,它是在内部使用的,理论上你可以用它来加速这个过程。

P99有一个完全符合你想要的宏

 #include "p99_map.h" int Ara[] = { P99_POSS(100) }; 

它的优点是它完全是编译时间,没有function等的动态初始化。

对于您来说,它可能具有使用C99function的缺点,特别是具有可变长度参数的宏。

不,这不是有效的代码; 行为仍未定义。 由于数组初始化元素之间没有序列点,因此对f()的调用可以按任何顺序发生。

可以生成序列。 Boost.Preprocessor这样做,并使用这样的序列发出更有趣的东西。

我认为template仍然可以提供更好的解决方案,这种解决方案是明确的,不易出错。 请参阅以下代码; 许多东西只在编译时计算,它应该生成有效的代码。

 template struct Assign { static void Element (int *p) { Assign::Element(p); p[INDEX] = VALUE; } }; template struct Assign { static void Element (int *p) { p[INDEX] = VALUE; } }; template void Initialize (int (&a)[SIZE]) { Assign::Element(a); } 

乍一看可能有点复杂,但可以理解。 它仍然可以更加通用。 用法将如下所示:

 int a[100]; Initialize<1>(a); // '1' is the starting value 

这可以用于任何int a[N] 。 这是代码的输出 。

一些简单的代码生成怎么样。

 #include  int main() { std::ofstream fout("sequence_macros.hpp"); for(int i=1; i<=100; ++i) { fout << "#define e" << i << "(a) "; fout << "(a+0)"; for(int j=1; j 

然后把它用于:

 #include  #include "sequence_macros.hpp" int main() { // Create an array with 157 elements, in // sequence, starting at 25 int x[] = {e100(25),e50(25+100),e7(25+100+50)}; int sz = sizeof(x) / sizeof(*x); for(int i=0; i 

生成代码: http : //www.ideone.com/iQjrj

使用代码: http : //ideone.com/SQikz