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

C++ 技巧:使用宏填充数组值(代码生成),c++,c,arrays,macros,C++,C,Arrays,Macros,我正在读上面的主题,突然想到了这个想法:为什么不试着编写一些可以在我们的真实代码中使用的复杂宏(不仅仅是在现实生活中无用的谜题) 所以首先想到的是:用宏填充数组值: 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 a

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

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

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;
}
在线演示:

我们能否在紧凑性或泛型性(可能两者兼而有之)方面进一步改进此解决方案?我们可以去掉宏中需要的变量
i
?还是其他的改进

我想知道这是否是C++和C(当然忽略打印部分)的有效代码?< /P> 编辑:


我意识到调用
f()
的顺序似乎还没有确定。但我不确定,因为我认为数组初始化中的逗号可能与逗号运算符(通常)不同。但是如果是,我们能避免它吗?标准的哪一部分说它未指定?

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


可以生成序列。预处理器就是这样做的,并使用这样的序列来产生更多有趣的东西。

简单的代码生成怎么样

#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';
    }
}
#包括
int main(){
std::of stream fout(“sequence_macros.hpp”);
for(int i=1;i有一个宏,它可以完全满足您的需要

#include "p99_map.h"

int Ara[] = { P99_POSS(100) };
它的优点是完全是编译时的,没有函数的动态初始化等等


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

我认为
模板将提供更优的解决方案,它将是明确的,并且不容易出错。请参阅以下代码;许多内容仅在编译时计算,并且应该生成高效的代码

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*p)
{
赋值::元素(p);
p[指数]=数值;
}
};
模板
结构分配
{
静态空元素(int*p){p[INDEX]=VALUE;}
};
模板
无效初始化(整型(&a)[大小])
{
赋值::元素(a);
}
乍一看可能有点复杂,但可以理解。它仍然可以变得更一般。用法如下所示:

int a[100];
Initialize<1>(a);  // '1' is the starting value
inta[100];
初始化(a);//“1”是起始值

这可以用于任何
inta[N]
。以下是.

如果您希望深入研究预处理器编程,我只能建议将该库作为一个构建块,您将避免从头重写

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

使用
,您可以随心所欲



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

@Marcelo:是的。我在发布代码后意识到了这一点。但是任何改进,我们可以避免吗?@Nawaz:对不起,我意识到我的评论实际上是一个答案,所以我将其移动。@Downvoters:为什么是downvote?不是问一个问题,要求改进/纠正?@Nawaz-问题也可能不好。你为什么要发明一个宏来做什么,例如
generate()
from can ready do?@Bo Persson:这个问题有什么不好的地方吗?我不能尝试一些我还不知道的东西吗?我不能以一种我以前从未尝试过的方式来探索这门语言吗?另外,我不知道
generate()
可用于在初始化过程中填充数组。请给我举个例子。我认为它现在是“未指定”的,而不是“未定义的”。@ybungalobill:我没有时间调查,因此同意或不同意,但是的,听起来差不多。@Marcelo:我认为数组初始化中的逗号与逗号运算符(通常)不同。我可能错了。标准怎么说?@Marcelo:你能举一个生成序列的
Boost.Preprocessor
的例子吗?它能填充数组值吗?@Nawaz:是的,逗号运算符不同于数组初始化中使用的逗号,但这两种用法都没有引入序列点。逗号运算符可以自由计算其参数按相反的顺序,只要它“返回”最后一个参数。这将需要多次编译和运行代码;我认为OP需要一个更简单的解决方案或增强功能。+1.我喜欢这个想法,它实际上是基于我已经实现的想法。而且,它使用不同的程序生成宏。它可以合并吗?@Nawaz,为了合并这2个;我们必须增强上面的程序,而ch将首先放置
#define
,然后运行实际的可执行文件。@iammilind:到ideone的第二个链接是合并,但这不是我的意思。我的意思是,我们可以编写更少的宏来完成越来越多的不同类型的工作,因为@Benjamin的解决方案有太多的宏。它们没有太多的通用性。+1.太棒了。我们能自己编写这样的宏吗(对于C++)?它是如何编写的?@Nawaz,不幸的是,这绝非直截了当。你真的需要查看P99的细节。(或者在我的博客上了解一些解释)许多C++编译器都有变量作为扩展,所以它可以工作。如果你想使用模板,那么就不需要自己编写模板。你可以使用现有的一个:..但然后尝试1。@Nawaz,我觉得上面的模板解决方案将在编译时完成大部分工作,并将提供与,
inta[100]={1,2,3,…,99};
几乎相同的效果(它将避免运行时
i++
对较大数组的操作)。就数组而言,它在编译时不起任何作用。每个数组元素都有一个函数调用。@Nawaz,我的意思是针对我的模板解决方案;它将在编译时完成。但是,我不能保证,但我觉得编译器应该能够优化代码,并做出正确的选择
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
#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;
}
MACRO(z, 0, data), MACRO(z, 1, data), ... , MACRO(z, n-1, data)