在C ++中逐步构建编译时列表

时间:2014-06-06 18:23:55

标签: c++ list templates template-meta-programming cons

在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>>>;

我还有一个限制,即宏之间的空间需要处于整个事物的任何范围。我计划定义事物并使用宏同时在列表中注册它们,如下所示:

#define DEFINE_ELEMENT(Name, Value) \
using Name = SomeTemplate<Value>; \
ADD_TO_LIST(ListOfElements, Name)

换句话说,不允许将START_LIST定义为SomeTemplate<decltype(。这样就无法在两者之间添加新的定义。

请注意,解决方案也可以是&#34;参数包&#34; (可变参数模板)列表。我只关心它遵循如上所示的增量定义模式。

这里可以使用专业吗?如果不能完全采用上述模式,是否可以使用更多样板?

3 个答案:

答案 0 :(得分:2)

您可以直接使用C ++ 11可变参数模板,它们允许以比经典功能head:tail方法更加花哨的方式编写类型列表:

template<typename... Ts>
struct list{};

using l = list<int,char,bool>;

另一方面,如果你喜欢头尾方式,你可以从一种格式转换为另一种格式。在这种情况下(Variadic到功能):

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>;

答案 1 :(得分:1)

在OP自己的解决方案中,它仅适用于全局范围,不适用于类范围,也不适用于函数范围。我的实现适用于所有全局,类和函数范围。 OP解决方案的另一个优点是我的解决方案允许多个列表START_LIST / END_LIST对重叠,即不同的列表结构可以交错。

一个小的限制是它使用__COUNTER__宏,它不是星标的一部分,但它得到了gcc,clang和MSVC的良好支持,因此可移植性在这里不是一个大问题。另一个是函数范围,它必须使用单独的宏START_LIST_FUNCADD_TO_LIST_FUNC,因为我使用函数重载解析但在函数范围内它不能声明static函数,而在类级别必须使用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;
}

在g ++ - 4.8下编译:

[hidden]$ g++ -std=c++11 x.cpp && c++filt -t `./a.out`
Cons<int, Cons<float, Cons<int, Cons<double, Cons<double, Cons<char, Nil> > > > > >
Cons<float, Cons<int, Cons<float, Nil> > >
Cons<int, Cons<float, Cons<double, Cons<int, Nil> > > >
Cons<char, Cons<float, Cons<int, Cons<char, Nil> > > >

答案 2 :(得分:0)

我找到了解决方案!虽然它有点受限;你需要为每个元素提供一个唯一的名称,元素的数量需要有一个上限(这里是100)。

#include <type_traits>

// Good old Cons and Nil.
template <typename THead, typename TTail>
struct Cons {
    using Head = THead;
    using Tail = TTail;
};
struct Nil {};

// Helper template which builds a list from a template
// providing elements at given indices.
template <template<int> class ElemAt, int Offset, int Length>
struct BuildListFromElemAt {
    using Result = Cons<typename ElemAt<Offset>::Elem, typename BuildListFromElemAt<ElemAt, (Offset + 1), (Length - 1)>::Result>;
};
template <template<int> class ElemAt, int Offset>
struct BuildListFromElemAt<ElemAt, Offset, 0> {
    using Result = Nil;
};

// This is where the abuse begins.
// We use these classes to keep the current length
// of the list, in combination with function overloads.
// Each time we add an element, we will provide a more
// specific overload of a helper function.
template <int N>
struct Counter : public Counter<(N - 1)> {
    static int const Num = N;
};
template <>
struct Counter<0> {
    static int const Num = 0;
};

// At the beginning, we define the initial size function
// taking Counter<0>, and declare the ElemAt template.
#define BEGIN_LIST(ListName) \
Counter<0> ListName##_Size (Counter<0>); \
template <int Index> struct ListName##_ElemAt;

// For each element, we retrieve the current length of the
// list by checking the return type of calling the size function
// with a very large Counter.
// Then we overload the size function for one greater Counter,
// which ensures that we get an incremented length at the next
// step. We also define the ElemAt for this index to return the
// given Value.
#define ADD_TO_LIST(ListName, Name, Value) \
static int const ListName##_##Name##_PrevLen = decltype(ListName##_Size(Counter<100>()))::Num; \
static int const ListName##_##Name##_Len = ListName##_##Name##_PrevLen + 1; \
Counter<ListName##_##Name##_Len> ListName##_Size (Counter<ListName##_##Name##_Len>); \
template <> struct ListName##_ElemAt<ListName##_##Name##_PrevLen> { \
    using Elem = Value; \
};

// At the end, we retrieve the final length of the list,
// and build the list using the BuildListFromElemAt template.
#define END_LIST(ListName) \
static int const ListName##_Len = decltype(ListName##_Size(Counter<100>()))::Num; \
using ListName = typename BuildListFromElemAt<ListName##_ElemAt, 0, ListName##_Len>::Result;

// Here we go...
BEGIN_LIST(List)
ADD_TO_LIST(List, First, int)
ADD_TO_LIST(List, Second, float)
ADD_TO_LIST(List, Third, double)
END_LIST(List)

// And it works :)
static_assert(std::is_same<typename List::Head, int>::value, "");
static_assert(std::is_same<typename List::Tail::Head, float>::value, "");
static_assert(std::is_same<typename List::Tail::Tail::Head, double>::value, "");
相关问题