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

Trick : filling array values using macros (code generation)

本文关键字:代码生成 数组 填充 技巧      更新时间:2023-10-16

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。预处理器库作为构建块,您将避免从头开始重写。

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

#include <iostream>
#include <boost/preprocessor/repetition/enum.hpp>
#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 的文档条目

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

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

然后,它将被MACRO的n个连续调用所取代,用逗号分隔:

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

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

对您来说,它可能有一个缺点,即它使用C99功能,特别是具有可变长度参数的宏。

否,这不是有效的代码;这种行为仍不明确。由于数组初始化元素之间没有序列点,因此对f((的调用可以按任何顺序进行。

可以生成序列。促进预处理器这样做,并使用这样的序列来发出更有趣的东西。

我认为template仍然会提供更出色的解决方案,它将是确定的,而且不容易出错。请参阅以下代码;很多东西都是在编译时计算的,它应该生成高效的代码。

template<int VALUE, int INDEX, int SIZE, bool ALLOW>
struct Assign
{
  static void Element (int *p) 
  {
    Assign<VALUE + 1, INDEX + 1, SIZE, (INDEX < SIZE)>::Element(p);
    p[INDEX] = VALUE;
  }
};
template<int VALUE, int INDEX, int SIZE>
struct Assign<VALUE, INDEX, SIZE, false>
{
  static void Element (int *p) { p[INDEX] = VALUE; }
};
template<int START, int SIZE>
void Initialize (int (&a)[SIZE])
{
  Assign<START, 0, SIZE, true>::Element(a);
}

乍一看可能有点复杂,但可以理解。它仍然可以变得更普遍。用法如下所示:

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

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

简单的代码生成怎么样。

#include <fstream> 
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<i; ++j)
        {
            fout << ",(a+" << j << ")";
        }
        fout << 'n';
    }
}

然后投入使用:

#include <iostream>
#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<sz; ++i)
      std::cout << x[i] << 'n';
}

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

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