尽管这样做是一个坏主意,但
可以做到,所以这里有一种方法。
实际上,如果您使用适当强大的元编程库(如
Order,如上所述,Boost是另一个可能的候选项),则可以在预处理器中循环并定义递归宏。Order允许您以熟悉Scheme或ML的函数式风格进行编程。
要循环,请使用
for_each
结构。要简单地创建给定数量的内容,可以使用
for_each_in_range
和
1,N+1
。
ORDER_PP( // within this block Order code runs
8for_each_in_range(8fn(8_, 8print( (*) ) ),
1, 8)
)
上面的代码将打印出7个星号。您可以将元编程块包装在常规宏中,这些宏遵循普通的预处理器规则:
// print COUNT stars
#define STARS(COUNT) ORDER_PP( \
8for_each_in_range(8fn(8_, 8print((*)) ), 1, 8plus(COUNT, 1)) \
)
在ORDER_PP
块中,假定所有的内容都是Order代码而不是C预处理器代码,这意味着只能调用已识别的Order函数(所有的值/预处理标记必须是原始整数或用8(val)
结构进行“引用”)。为了将stars
定义为一个Order函数而不是CPP宏,以便可以在嵌套表达式中从ORDER_PP
中调用它,我们需要像这样编写它:
#define ORDER_PP_DEF_8stars ORDER_PP_FN( \
8fn(8C, 8for_each_in_range(8fn(8_, 8print((*)) ), 1, 8plus(8C, 1)) ))
ORDER_PP( 8stars(7) )
Order可以完全透明地提供递归,因此编写嵌套的初始化程序循环相对简单:
8fn(8N, 8T, 8C, 8I, 8D, \
8do( \
8print( 8N (=malloc) 8lparen 8seq_head(8D) (*sizeof) 8lparen 8T 8stars(8minus(8C, 1)) 8rparen 8rparen (;) ), \
8if(8equal(8C, 1), \
8print(((void)0;)), \
8do( \
8print( (for) 8lparen (int) 8I (=0;) 8I (<) 8seq_head(8D) (;) 8I (++) 8rparen ({) ), \
8ndim_init(8adjoin(8N, 8([), 8I, 8(])), 8T, 8minus(8C, 1), 8cat(8I, 8(_K)), 8seq_tail(8D)), \
8print( (}) ) \
)))))
像这样调用ndim_init
:
// print the nested initializer from the question
ORDER_PP(
8ndim_init(8(nda), 8(int), 3, 8(i), 8seq(8(n1), 8(n2), 8(n3)))
)
请注意,C变量名(
nda
,
i
等)在出现在
ORDER_PP
块中时需要加引号,以便Order将它们视为文本,而不是尝试评估它们。最后一个参数是一个运行时变量列表,包含每个维度的大小(
8seq
构建一个列表,
8
再次引用C变量名)。
您可以将
ndim_init
的调用打包到一个常规预处理器宏中,以便轻松访问,就像第一个示例中的
STARS
一样;您可以通过这种方式轻松地将其与声明宏结合起来,在单个调用中发出声明和初始化:
#define NDIM(NAME, TYPE, ...) ORDER_PP ( \
8lets( (8D, 8((__VA_ARGS__))) \
(8C, 8tuple_size(8D)), \
8do( \
8print( (TYPE) 8stars(8C) (NAME; {) ), \
8ndim_init(8(NAME), 8(TYPE), 8C, 8(_ITER), 8tuple_to_seq(8D)), \
8print( (}) ) \
)) \
)
NDIM(nda, int, n1, n2, n3) // emits declaration and init block for int ***nda
更多订单示例
如果上面的内容看起来一点也不直观...这就是为什么人们说你不应该这样做。(如果你觉得很简单,那太好了,但其他人可能无法阅读你的代码。)
n
和sizeof object
作为参数声明一个函数,并使其分配/初始化所有东西。 - snf