В 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(
, Это сделало бы невозможным добавление новых определений между ними.
Обратите внимание, что в качестве альтернативы решение может быть в форме списка «пакет параметров» (шаблон переменной). Я только забочусь о том, что он следует инкрементальной схеме определения, как показано выше.
Могут ли здесь использоваться специализации? Если это не возможно точно с вышеупомянутой схемой, возможно ли это с немного большим количеством шаблона?
В собственном решении OP он работает только для глобальной области видимости, но не для области класса или функции. Моя реализация здесь работает для всех глобальных, классовых и функциональных областей. Другим преимуществом по сравнению с решением OP является мое решение, позволяющее перекрывать пары START_LIST / END_LIST нескольких списков, т. Е. Различные конструкции списка могут чередоваться.
Одно небольшое ограничение — это использование __COUNTER__
macro, который не является частью starndard, но хорошо поддерживается gcc, clang и MSVC, поэтому переносимость здесь не является большой проблемой. Другое дело для области видимости функции, она должна использовать отдельный макрос START_LIST_FUNC
а также ADD_TO_LIST_FUNC
как я использую разрешение перегрузки функции, но в области видимости функции не может объявить static
функция, в то время как на уровне класса она должна использовать static
функция.
РЕДАКТИРОВАТЬ: включить идею ListReverseHelper из комментария OP, чтобы сделать его намного проще.
#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> > > >
Вы можете использовать вариационные шаблоны 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>;
Я нашел решение! Хотя это немного более ограничено; Вы должны предоставить уникальное имя для каждого элемента, и должен быть верхний предел количества элементов (здесь 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, "");