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

时间:2017-04-18 09:12:50

标签: c++ c++11 macros x-macros

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

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

enter image description here

以上是相关图表。 (在实际情况中,有100多个班级。)
箭头(红色)是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 宏魔法对于维持具有最终性能的对象之间的关系非常有用。

我猜X-macro是一种可能的解决方案,但我对它的体验相对较低 我也读过: -

只是一个粗略的指导/想法表示赞赏。 (完全代码不需要,但我不介意)

修改:在实际情况中,BIND分散在许多标题中  这是#include流量(较低#include上限)的示例: - enter image description here

1 个答案:

答案 0 :(得分:1)

在这个解决方案中,我倾向于使用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内有我们的绑定列表。

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

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

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

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

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

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

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

#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应该是我们使用pack<L, R>定义的BIND之一。现在实例化例如仅当MakeLeftMember<T, pack<L, R>>T时,R才会调度到专门化,也就是说,绑定确实是T的左边绑定。然后专门化将生成适当命名的{{1}要由Left<L>继承的成员。在其他情况下,选择基本模板,没有任何反应。

最后一个缺失的链接当然是T,它只是将所有绑定发送到成员制造商并继承生成的成员:

WithBindings<T, AllBindings>

然后我们走了。 See it live on Coliru