4

在 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<or的东西decltype(。这样就不可能在两者之间添加新定义。

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

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

4

3 回答 3

3

您可以直接使用 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>;
于 2014-06-06T20:32:38.157 回答
1

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

一个小的限制是它使用__COUNTER__宏,它不是starndard 的一部分,但它得到了 gcc、clang 和 MSVC 的良好支持,因此可移植性在这里不是大问题。另一件事是对于函数范围,它必须使用单独的宏START_LIST_FUNC,并且ADD_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> > > >
于 2014-06-06T23:21:53.340 回答
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, "");
于 2014-06-07T00:06:17.903 回答