创建宏以将令牌(参数)一个一个地收集到列表中
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 是一个可能的解决方案,但我对此的经验相对较少。
我也读过:-
- Creating a string list and an enum list from a C++ macro
- Real-world use of X-Macros
粗略的 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.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
中有了我们的绑定列表。
下一步:我们如何将成员注入 class?我放弃了宏,而是使用成员继承。所以 class 定义如下:
struct Cat : WithBindings<Cat, AllBindings> { };
... 将最终继承自定义成员 Right<Dog> dogs
、Right<Tree> trees
和 Right<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>>
仅当 T
为 R
时才会分派到特化,也就是说,绑定确实是 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!
我正在尝试创建一个生成指向另一个实例的指针的宏 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 是一个可能的解决方案,但我对此的经验相对较少。
我也读过:-
- Creating a string list and an enum list from a C++ macro
- Real-world use of X-Macros
粗略的 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.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
中有了我们的绑定列表。
下一步:我们如何将成员注入 class?我放弃了宏,而是使用成员继承。所以 class 定义如下:
struct Cat : WithBindings<Cat, AllBindings> { };
... 将最终继承自定义成员 Right<Dog> dogs
、Right<Tree> trees
和 Right<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>>
仅当 T
为 R
时才会分派到特化,也就是说,绑定确实是 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!