创建宏以将令牌(参数)一个一个地收集到列表中

create Macro to collect token (parameter) into a list, one by one

我正在尝试创建一个生成指向另一个实例的指针的宏 class 来表示方向关系。

//#define BIND(A,B) ?
//can be modified a little, top header
BIND(CAT,DOG)
BIND(CAT,TREE)
BIND(CAT,RAT)
BIND(DOG,TREE)
//#define MACRO_CAT (need?) ? 
//#define MACRO_DOG (need?) ?

以上为相关图解。 (在实际情况下,有 100+ classes。)
箭头(红色)为 Right<>。箭尾(绿色)为Left<>。 (在片段下方)

上面的代码有没有可能像这样自动MACRO_CAT/MACRO_DOG创建宏? :-

//v should not be modified
class Cat{
    MACRO_CAT
    /* expand to :-
        Right<Dog> dogs;
        Right<Tree> trees;
        Right<Rat> rats;
    */
};
class Dog{
    MACRO_DOG
    /* expand to :-
        Right<Tree> trees;
        Left<Cat> cats;
    */
};

这个 hackery 宏魔法对于维持 objects 与最终性能之间的关系非常有用。

我想 X-macro 是一个可能的解决方案,但我对此的经验相对较少。
我也读过:-

粗略的 guide/idea 不胜感激。 (不需要完整代码,但我不介意)

编辑: 在实际情况中,BIND 散布在许多 headers.
中 这是 #include 流程的示例(较低 #include 较高):-

在此解决方案中,我倾向于使用 TMP 而不是宏。

第一步是收集所有声明的绑定。允许自由声明绑定并将它们分散到其他代码中可能是可行的。然而,它需要一些方法来保持和更新列表的状态,这几乎是 the most arcane thing you would want to do in C++。所以我们不要那样做。

绑定文件将仅包含预处理器指令和对宏BIND的调用,如下所示:

BIND(Cat, Dog)
BIND(Cat, Tree)
BIND(Cat, Rat)
BIND(Dog, Tree)

换句话说,预处理文件必须只包含 BIND 的替换输出。然后我们可以将它们夹在 bindTop.hbindBottom.h 之间:

template <class...>
struct pack;

// Stuff in bindTop.h

#define BIND(T, U) \
    pack<struct T, struct U>,

using AllBindings = pack<

// End of bindTop.h, beginning of the binding file(s)

    BIND(Cat, Dog)
    BIND(Cat, Tree)
    BIND(Cat, Rat)
    BIND(Dog, Tree)

// End of the binding file(s), beginning of bindBottom.h

    void // Pairs up with the last comma,
         // will be silently ignored in further processing
>;

#undef BIND

// Stuff in bindBottom.h

现在我们在 AllBindings 中有了我们的绑定列表。

下一步:我们如何将成员注入 class?我放弃了宏,而是使用成员继承。所以 class 定义如下:

struct Cat  : WithBindings<Cat,  AllBindings> { };

... 将最终继承自定义成员 Right<Dog> dogsRight<Tree> treesRight<Rat> rats 的几个结构,因此几乎可以像访问它们一样访问它们他们是它的。

但是如何声明类型Right<Dog>的成员必须被调用dogs呢?当然是宏!让我们为左右制作基地制作空模板classes:

template <class T, class Binding>
struct MakeLeftMember { };

template <class T, class Binding>
struct MakeRightMember { };

然后我们将使用一个宏为我们的每个 classes 专门化这些,使用 class 的名称和相应成员的名称:

#define BINDING_MEMBER_NAME(type_, memberName_) \
    template <class T> struct MakeLeftMember<T, pack<type_, T>> { \
        Left<type_> memberName_; \
    }; \
    template <class T> struct MakeRightMember<T, pack<T, type_>> { \
        Right<type_> memberName_; \
    }

Binding 应该是我们用 BIND 定义的 pack<L, R> 之一。现在实例化例如MakeLeftMember<T, pack<L, R>> 仅当 TR 时才会分派到特化,也就是说,绑定确实是 T 的左绑定。然后特化将生成适当命名的 Left<L> 成员将由 T 继承。在其他情况下,基本模板被选中,没有任何反应。

最后缺少的 link 当然是 WithBindings<T, AllBindings>,它只是将所有绑定分派给成员制造者并继承生成的成员:

template <class T, class... Bindings>
struct WithBindings<T, pack<Bindings...>>
: MakeLeftMember <T, Bindings>...
, MakeRightMember<T, Bindings>... { };

我们开始了。 See it live on Coliru!