START_LIST(List)
ADD_TO_LIST(List, int)
ADD_TO_LIST(List, float)
ADD_TO_LIST(List, double)
END_LIST(List)
我也有一个限制,即宏之间的空间必须处于整个范围之内。我打算定义事物并使用宏在列表中同时注册它们,如下所示:
using List = Cons<int, Cons<float, Cons<double, Nil>>>;
换句话说,这是不允许的将
START_LIST
定义为SomeTemplate<
或decltype(
之类的东西。这将使得不可能在两者之间添加新的定义。请注意,解决方案也可以采用“参数包”(变量模板)列表的形式。我只关心它遵循上面显示的增量定义模式。
可以在这里使用专业化吗?如果上述模式无法完全实现,是否可以增加一些样板?
#1 楼
在OP自己的解决方案中,它仅适用于全局范围,不适用于类范围或函数范围。我的实现在这里适用于所有全局,类和函数范围。相对于OP解决方案的另一个优势是我的解决方案允许多个列表START_LIST / END_LIST对重叠,即可以交错使用不同的列表构造。它由gcc,clang和MSVC很好地支持,因此可移植性在这里不是什么大问题。另一件事是函数范围,它必须使用单独的宏__COUNTER__
和START_LIST_FUNC
,因为我使用函数重载分辨率,但是在函数范围内它不能声明ADD_TO_LIST_FUNC
函数,而在类级别上它必须使用static
函数。 > 编辑:从OP的注释中合并ListReverseHelper的想法,以使其变得更加简单。 />
#include <iostream>
#include <typeinfo>
using namespace std;
struct Nil {};
template <typename T, typename U> struct Cons {};
template <typename List, typename Reversed> struct ListReverseHelper;
template <typename Reversed>
struct ListReverseHelper<Nil, Reversed> {
using Type = Reversed;
};
template <typename Head, typename Tail, typename Reversed>
struct ListReverseHelper<Cons<Head, Tail>, Reversed> {
using Type = typename ListReverseHelper<Tail, Cons<Head, Reversed>>::Type;
};
template <typename T, int N> struct ListMakerKey : ListMakerKey<T, N-1> {};
template <typename T> struct ListMakerKey<T, 0> {};
#define START_LIST_(name, modifier) \
struct name##_ListMaker {}; \
modifier Nil list_maker_helper_(ListMakerKey<name##_ListMaker, __COUNTER__>);
#define ADD_TO_LIST_(name, type, modifier) \
modifier Cons<type, decltype(list_maker_helper_(ListMakerKey<name##_ListMaker, __COUNTER__>{}))> \
list_maker_helper_(ListMakerKey<name##_ListMaker, __COUNTER__>);
#define END_LIST(name) \
using name = typename ListReverseHelper<decltype(list_maker_helper_(ListMakerKey<name##_ListMaker, __COUNTER__>{})), Nil>::Type;
#define START_LIST(name) START_LIST_(name, static)
#define ADD_TO_LIST(name, type) ADD_TO_LIST_(name, type, static)
#define START_LIST_FUNC(name) START_LIST_(name,)
#define ADD_TO_LIST_FUNC(name, type) ADD_TO_LIST_(name, type,)
START_LIST(List)
ADD_TO_LIST(List, int)
int a = 10;
ADD_TO_LIST(List, float)
int b = 10;
START_LIST(List2)
ADD_TO_LIST(List, int)
int c = 10;
ADD_TO_LIST(List2, float)
ADD_TO_LIST(List, double)
ADD_TO_LIST(List2, int)
ADD_TO_LIST(List2, float)
END_LIST(List2)
ADD_TO_LIST(List, double)
ADD_TO_LIST(List, char)
END_LIST(List)
struct A {
START_LIST(List3)
ADD_TO_LIST(List3, int)
int a = 10;
ADD_TO_LIST(List3, float)
int b = 10;
ADD_TO_LIST(List3, double)
ADD_TO_LIST(List3, int)
END_LIST(List3)
};
int main() {
START_LIST_FUNC(List4)
ADD_TO_LIST_FUNC(List4, char)
int a = 10;
ADD_TO_LIST_FUNC(List4, float)
int b = 10;
ADD_TO_LIST_FUNC(List4, int)
ADD_TO_LIST_FUNC(List4, char)
END_LIST(List4)
List x;
List2 y;
A::List3 z;
List4 w;
cout << typeid(x).name() << endl;
cout << typeid(y).name() << endl;
cout << typeid(z).name() << endl;
cout << typeid(w).name() << endl;
}
评论
问题是我不能在ADD_TO_LIST调用之间插入任意定义。
– Ambroz Bizjak
2014年6月6日23:26
这比我的要简单得多:coliru.stacked-crooked.com/a/d7f802f3f225f476
–鸭鸭
2014年6月6日23:31
@AmbrozBizjak:您想在两者之间添加任意定义吗?这很重要,应该在问题中(最好是在示例中)加以澄清。 (此外,这将它带入了不可能的领域)
–鸭鸭
2014年6月6日在23:32
@AmbrozBizjak,在您的答案中,ADD_TO_LIST包含模板特殊化,不能在块范围内声明。即您的宏不能在函数中使用。如果我们只想在全局范围内声明类型,那么在您的解决方案中有很多方法没有限制。
–李侃
2014年6月7日在1:19
@icando是的,我不需要在函数中使用它,而仅在全局范围(也许在类范围内)工作(类范围需要一个虚拟模板参数)。那么,还有什么其他方法呢?
– Ambroz Bizjak
2014年6月7日,下午1:22
#2 楼
您可以直接使用C ++ 11可变参数模板,它们比经典的head:tail
方法更花哨的方式来编写类型列表:template<typename... Ts>
struct list{};
using l = list<int,char,bool>;
,如果您喜欢从头开始,可以将一种格式转换为另一种格式。在这种情况下(可变到功能):
template<typename HEAD , typename TAIL>
struct list{};
struct nil{};
template<typename... Ts>
struct make_list;
template<typename HEAD , typename... TAIL>
struct make_list<HEAD,TAIL>
{
using result = list<HEAD,typename make_list<TAIL...>::result;
};
template<>
struct make_list<>
{
using result = nil;
};
示例:
//l is list<int,list<char,list<bool,nil>>>
using l = typename make_list<int,char,bool>::result;
当然,您可以使用模板别名使语法更清晰:
template<typename... Ts>
using make = typename make_list<Ts...>::result;
using l = make<int,char,bool>;
评论
我知道可变参数模板,但我更喜欢缺点列表,因为它们更易于使用且运行更快。无论如何,您的答案无法解决我的问题。正如您自己指出的那样,您可以在两种列表格式之间进行转换,因此我是否用参数包列表的缺点列表来表述问题都没有关系。
– Ambroz Bizjak
2014年6月6日在22:16
请注意,解决我的问题的方法将包括对START_TO_LIST,ADD_TO_LIST和END_LIST宏的适当定义。
– Ambroz Bizjak
2014年6月6日22:19
@AmbrozBizjak:缺点列表更难使用,而且速度更慢。您只是不习惯杂色。
–鸭鸭
2014年6月6日23:35
@MooingDuck:我之前使用G ++进行的测试表明,可变参数模板的简单递归分解具有二次性能。我的理由是,每次使用参数包实例化模板时,都会复制整个包。对于缺点列表,只需简单地选择尾巴即可。
– Ambroz Bizjak
2014年6月6日在23:44
考虑一下转换元函数(函数程序员的映射)的实现:对于可变参数模板,它只是函数应用程序的一个包扩展,而对于head:tail,则必须手动编写孔递归遍历。 (我对此非常了解,我目前正在为基于可变参数的类型列表和迭代器范围编写算法:P github.com/Manu343726/Turbo/blob/reboot/algorithm.hpp)
–Manu343726
2014年6月7日12:11
#3 楼
我找到了解决方案!虽然只是有限一些;您需要为每个元素提供唯一的名称,并且元素的数量上限(此处为100)。评论
ADD_TO_LIST包含模板特化,不能在块范围内声明。即您的宏不能在函数中使用。如果我们只想在全局范围内声明类型,那么在您的解决方案中有很多方法没有限制。
–李侃
2014年6月7日在1:15
我现在更新了答案,它适用于全局,类和函数范围。 C ++是魔术!
–李侃
2014年6月7日在6:54
评论
我认为这将取决于编译器。您正在使用哪个编译器?我确实在上一份工作中看到了类似的内容;可悲的是,我不记得细节了。@abelenky我希望有一个标准的C ++ 11解决方案,但我想我会选择适用于G ++ 4.8的任何东西。
您可能喜欢我的回答:stackoverflow.com/questions/18701798 / ...
主要问题在于,下一行无法引用任何上一行,因为它们都具有相同的名称和范围。是否可以为每行指定前一行的名称,以便我们可以根据先前的结果进行构建?
我不确定您要达到的目标。但请查看:journal.stuffwithstuff.com/2012/01/24/higher-order-macros-in-c,这使您可以在实例时间分配宏,从而可能解决您的问题。