【问题标题】:Creating a related x-macro from an existing one从现有的 x 宏创建相关的 x 宏
【发布时间】:2018-07-20 19:55:00
【问题描述】:

考虑以下用户样式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_X 定义PRIMES_FOO_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 不是传递的参数,而只是普通标记 func,因为 FOO_ADAPT 没有名为 func 的参数,只有 PRIMES_FOO_X(func) 有(并且它不使用它)。

我想不出办法来完成这项工作。

【问题讨论】:

  • 如果您可以提供一两个用例来显示PRIMES_FOO_X 宏的所需输出,这可能会有所帮助。
  • @JosephQuinsey 以及 PRIMES_FOO_X 所需的 PRIMES_FOO_X expansion 显示在我的问题末尾:它的行为应该与#define PRIMES_FOO_X(func) ` followed by the text shown in my last code block. Of course, I don't want to write it out by hand but for it to have this effect but be based on the existing PRIMES_X 完全一样` 宏(特别是对PRIMES_X 的更改将反映在PRIMES_FOO_X 中)。

标签: c c-preprocessor x-macros


【解决方案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,你需要在(FOOIDENT_OF(2))上调用func,等等;也就是说,你想要更精确的东西:

#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】:

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

    您可以事先声明它,而不是将参数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();
    

    【讨论】:

      【解决方案3】:

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

      受到 H Walters 的 answer 使用 Boost 的启发,我想找到一个纯 C 的解决方案。威廉斯旺森对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 中移动逗号即可解决此问题,但事实并非如此。 (待办事项:修复)

      • 任何使用## 连接运算符的类似 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)
      

      【讨论】:

        【解决方案4】:

        尝试以下解决方案:

        #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)
        

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-01-02
          • 2017-04-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多