我正在尝试创建一个宏,该宏生成指向另一个类的实例的指针以表示方向关系。
//#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多个班级。)
箭头(红色)是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是一种可能的解决方案,但我对它的体验相对较低 我也读过: -
只是一个粗略的指导/想法表示赞赏。 (完全代码不需要,但我不介意)
答案 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.h
和bindBottom.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> dogs
,Right<Tree> trees
和Right<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!