问题 技巧:使用宏填充数组值(代码生成)
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()
似乎还在 不明。我不确定,因为我认为数组初始化中的逗号是 不 可能与逗号运算符相同(一般情况下)。但如果确实如此,我们能否避免它以及标准的哪一部分说出来 不明?
2059
2018-05-21 06:47
起源
答案:
如果你想深入研究预编程器编程,我只能推荐 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,还有其他操作可用于执行算术;)
编辑: 这个怎么用 ?
首先,我只能推荐doc条目 BOOST_PP_ENUM
BOOST_PP_ENUM
是一个带有3个参数的宏: (n, MACRO, data)
n
:一个整数
MACRO
:一个接受3个参数的宏: (z, i, data)
data
:一些数据,方便您传递给 macro
它将被n次连续调用取代 MACRO
被逗号隔开:
MACRO(z, 0, data), MACRO(z, 1, data), ... , MACRO(z, n-1, data)
你可以随心所欲地做任何事情 MACRO
。
我恐怕从未使用过 z
参数,它在内部使用,理论上你可以用它来加速这个过程。
5
2018-05-21 14:16
P99 有一个宏,完全符合你的要求
#include "p99_map.h"
int Ara[] = { P99_POSS(100) };
它的优点是它完全是编译时间,没有功能等的动态初始化。
对于您来说,它可能具有使用C99功能的缺点,特别是具有可变长度参数的宏。
5
2018-05-21 09:16
不,这不是有效的代码;行为仍未定义。由于数组初始化元素之间没有序列点,因此对f()的调用可以按任何顺序发生。
可以生成序列。 Boost.Preprocessor这样做,并使用这样的序列发出更有趣的东西。
3
2018-05-21 06:56
我觉得还是 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]
。这里是 输出代码。
2
2018-05-21 09:31
一些简单的代码生成怎么样。
#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
1
2018-05-21 07:35