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

时间:2011-05-21 06:47:03

标签: c++ c arrays macros

Are C++ Templates just Macros in disguise?

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

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

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()的顺序似乎仍未未指定。我不确定,因为我认为数组初始化中的逗号可能与逗号运算符(通常)相同。但如果确实如此,我们可以避免它吗?标准的哪一部分说明未指明

5 个答案:

答案 0 :(得分:5)

P99有一个宏,可以完全按照您的要求进行操作

#include "p99_map.h"

int Ara[] = { P99_POSS(100) };

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

对于您而言,它可能具有使用C99功能的缺点,特别是具有可变长度参数的宏。

答案 1 :(得分:5)

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

例如,为了创建表格,我会使用(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参数,它在内部使用,理论上你可以用它来加速这个过程。

答案 2 :(得分:3)

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

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

答案 3 :(得分:2)

我认为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]。这是output of the code

答案 4 :(得分:1)

如何生成一些简单的代码。

#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