从现有的x宏创建相关的x宏

时间:2018-07-20 19:55:01

标签: c c-preprocessor x-macros

考虑以下用户样式x-macro

#define PRIMES_X(func) \
  func(2) \
  func(3) \
  func(5) \
  func(7) 

我们可以使用它来使用前四个素数反复扩展传入的宏func。例如:

#define MAKE_FUNC(num) void foo ## num();
PRIMES_X(MAKE_FUNC)

将声明返回void的函数foo2()foo3()foo5()foo7()

到目前为止,太好了。

比方说,我知道要创建一个相关的x宏,它不使用素数23等来调用其参数,而是使用派生自其的某些标记,例如作为上面的函数名称。也就是说,我想要这个:

#define PRIMES_FOO_X(func) \
  func(foo2) \
  func(foo3) \
  func(foo5) \
  func(foo7) 

,但实际上并没有全部写出来(实际上,PRIMES_X更改时,它会不同步。

我想要的是一种根据PRIMES_FOO_X来定义PRIMES_X的方法。我几乎可以到达那里,例如:

#define FOO_ADAPT(num) func(foo ## num)
#define PRIMES_FOO_X(f) PRIMES_X(FOO_ADAPT)

在这种情况下,PRIMES_FOO_X扩展为:

  func(foo2) \
  func(foo3) \
  func(foo5) \
  func(foo7)

...看起来不错,但是这里的func不是传递的arg,而是纯令牌func,因为FOO_ADAPT没有名为{{1 }},只有func使用(并且不使用它)。

我想不出一种方法来完成这项工作。

4 个答案:

答案 0 :(得分:1)

也许一个简单的解决方法就足够了。

您可以预先声明而不是将参数func传递给PRIMES_FOO_X。例如,在此代码中,我们使用FOO_FUNC来保存func

#define PRIMES_FOO_X PRIMES_X(FOO_ADAPT)
#define FOO_ADAPT(num) FOO_FUNC(foo ## num)

#define FOO_FUNC bar
PRIMES_FOO_X

#undef  FOO_FUNC
#define FOO_FUNC(x) x();
PRIMES_FOO_X

结果是:

bar(foo2) bar(foo3) bar(foo5) bar(foo7)

foo2(); foo3(); foo5(); foo7();

答案 1 :(得分:1)

主要观察结果……

#define PRIMES_X(func) \
  func(2) \
  func(3) \
  func(5) \
  func(7)

PRIMES_X()扩展为(2) (3) (5) (7),根据CPP元编程,它是一个 sequence 数据结构。考虑到这一点,让我们开始倒退。您想要这样的东西:

#define PRIMES_FOO_X(func) \
  /* something that expands to: func(foo2) func(foo3) func(foo5) func(foo7) */

...并且您希望foo2foo3foo5foo7来自PRIMES_X扩展。显然2变成foo23变成foo3,依此类推;因此,我们假设根据称为FOOIDENT_OF的宏发生这种情况。然后在PRIMES_FOO_X中,您需要在func上呼叫(FOOIDENT_OF(2)),依此类推;也就是说,您想要更精确的像这样的东西:

#define PRIMES_FOO_X(func) \
  /* something that expands to: \
   * func(FOOIDENT_OF(2)) func(FOOIDENT_OF(3)) \
   * func(FOOIDENT_OF(5)) func(FOOIDENT_OF(7)) */

结合这两个想法,我们拥有的要素是:

  • func,应用于派生的X宏的操作
  • FOOIDENT_OF,将每个X宏参数列表转换为新格式的操作
  • PRIMES_X(),所有参数列表的序列

这是可能的,而且如果我们使用boost预处理程序的序列,甚至可以轻松做到。

#include <boost/preprocessor/seq.hpp>

#define PAIR_ELEMENT_1(A,B) A
#define PAIR_ELEMENT_2(A,B) B

#define PAIR_XFORM_MACRO(r, data, elem) \
   PAIR_ELEMENT_1 data ( PAIR_ELEMENT_2 data (elem) )

#define PAIR_XFORM(PAIR_, SEQ_) \
   BOOST_PP_SEQ_FOR_EACH(PAIR_XFORM_MACRO, PAIR_, SEQ_)

在这里,我有一个PAIR_XFORM,它带有2个元组(“对”)的宏,并将它们都应用于序列的每个元素。 IOW,PAIR_XFORM((func, FOOIDENT_OF), PRIMES_X())生成我们的目标。现在,我们需要生成新的X宏并制作内部转换宏:

#define FOOIDENT_OF(N) foo##N
#define PRIMES_FOO_X(func) PAIR_XFORM((func, FOOIDENT_OF), PRIMES_X())

Here是堆叠弯曲的样子。

答案 2 :(得分:1)

(备注:这是我对这个问题的第二个回答)

受沃尔特斯(H Walters)的answer使用Boost的启发,我想找到一个仅C语言的解决方案。威廉·斯旺森(William Swanson)对Foreach macro on macros arguments的出色回答似乎提供了一个答案。

从他的答案中获取代码,我们可以生成以下解决方案:

// The first part here is taken from William Swanson's answer
// to https://stackoverflow.com/questions/6707148
#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL(...)  EVAL4(EVAL4(EVAL4(__VA_ARGS__)))

#define MAP_OUT
#define MAP_END(...)
#define MAP_GET_END() 0, MAP_END
#define MAP_NEXT0(item, next, ...) next MAP_OUT
#define MAP_NEXT1(item, next) MAP_NEXT0(item, next, 0)
#define MAP_NEXT(item, next)  MAP_NEXT1(MAP_GET_END item, next)

#define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__)
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__)
#define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__ (), 0))

// This is the example given by the OP:
#define PRIMES_X(func) \
  func(2) \
  func(3) \
  func(5) \
  func(7)

#define FOO_LIST(num) foo ## num, // note comma
#define PRIMES_FOO_X(f) MAP(f, PRIMES_X(FOO_LIST))

#define XXX(x) bar(x)
#define YYY(x) x();
PRIMES_FOO_X(XXX)
PRIMES_FOO_X(YYY)

使用gcc -E -P ...的结果是:

bar(foo2) bar(foo3) bar(foo5) bar(foo7)
foo2(); foo3(); foo5(); foo7();

注意:

  • MAP的定义中,我必须删除__VA_ARGS__之后的逗号,以防止在末尾出现多余的垃圾值。但这破坏了宏的其他用途。有人会认为,只需在FOO_LIST中移动逗号即可解决此问题,但事实并非如此。 (Todo:修复)

  • 使用##串联运算符的任何类似MAP或类似FOREACH的解决方案在这里都不太可行,因为由宏给出的任何输入列表都不会扩展 。 (这对我来说是新的:()


编辑:使用相同想法的第二种替代解决方案基于https://esolangs.org/wiki/ELIP中的代码。输出与上面相同。 (这表明我对##的评论不正确。)

// The first part here is based on esolangs.org/wiki/ELIP (CC0 public domain)
// (Note MAP here is their FOREACH)
#define XCAT(x,y) x ## y
#define CAT(x,y) XCAT(x,y)
#define EMPTY()
#define LPAREN (
#define RPAREN )
#define DEFER(x) x EMPTY()
#define EAT(...)
#define EXPAND(...) __VA_ARGS__
#define TRUE(x,...) x
#define FALSE(x,...) __VA_ARGS__
#define TRANSFORM(seq, ...) CAT(TRANSFORM1_A seq,0END)(EAT, __VA_ARGS__)
#define TRANSFORM1_A(...) (EXPAND, __VA_ARGS__)() TRANSFORM1_B
#define TRANSFORM1_B(...) (EXPAND, __VA_ARGS__)() TRANSFORM1_A
#define TRANSFORM1_A0END
#define TRANSFORM1_B0END
#define RPXFRM(m, ...) m(RPAREN RPXFRM_ID)
#define RPXFRM_ID() RPXFRM
#define INFUSE(seq, ...) INFUSE5(INFUSE1(TRANSFORM(seq), __VA_ARGS__))
#define INFUSE1(xfrm, ...) INFUSE2 xfrm, __VA_ARGS__ RPXFRM xfrm
#define INFUSE2(m, ...) m(INFUSE3 DEFER(XCAT)(LPA,REN)(__VA_ARGS__), INFUSE2_ID)
#define INFUSE2_ID() INFUSE2
#define INFUSE3(...) INFUSE4(__VA_ARGS__)
#define INFUSE4(x, rest, ...) (__VA_ARGS__, EXPAND x)() rest, __VA_ARGS__
#define INFUSE5(...) INFUSE6(__VA_ARGS__)
#define INFUSE6(...) INFUSE7(__VA_ARGS__)
#define INFUSE7(seq, ...) seq
#define MAP(macro, seq) EXPAND(MAP1 INFUSE(seq, TRUE, macro)(FALSE, EAT,))
#define MAP1(p, m, ...) m(__VA_ARGS__) p(MAP1_ID)
#define MAP1_ID() MAP1

// This is the example given by the OP:
#define PRIMES_X(func) \
  func(2) \
  func(3) \
  func(5) \
  func(7)

#define FOO_LIST(num) (foo ## num) // parentheses, no commas
#define PRIMES_FOO_X(f) MAP(f, PRIMES_X(FOO_LIST))

#define XXX(x) bar(x)
#define YYY(x) x();
PRIMES_FOO_X(XXX)
PRIMES_FOO_X(YYY)

答案 3 :(得分:0)

尝试以下解决方案:

<style type="">

它展开

#define PRIMES_X(func)                          \
  func(2) \
  func(3) \
  func(5) \
  func(7)

#define DERIVE_TOKEN(num) (foo##num);
#define FOO_ADAPT(f) f DERIVE_TOKEN
#define PRIMES_FOO_X(f) PRIMES_X(FOO_ADAPT(f))

PRIMES_FOO_X(funct)