在C ++中,有没有一种方法可以按以下模式逐步构建编译时间列表?


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(之类的东西。这将使得不可能在两者之间添加新的定义。

请注意,解决方案也可以采用“参数包”(变量模板)列表的形式。我只关心它遵循上面显示的增量定义模式。

可以在这里使用专业化吗?如果上述模式无法完全实现,是否可以增加一些样板?

评论

我认为这将取决于编译器。您正在使用哪个编译器?我确实在上一份工作中看到了类似的内容;可悲的是,我不记得细节了。

@abelenky我希望有一个标准的C ++ 11解决方案,但我想我会选择适用于G ++ 4.8的任何东西。

您可能喜欢我的回答:stackoverflow.com/questions/18701798 / ...

主要问题在于,下一行无法引用任何上一行,因为它们都具有相同的名称和范围。是否可以为每行指定前一行的名称,以便我们可以根据先前的结果进行构建?

我不确定您要达到的目标。但请查看:journal.stuffwithstuff.com/2012/01/24/higher-order-macros-in-c,这使您可以在实例时间分配宏,从而可能解决您的问题。

#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