Construction incrémentielle d’une liste de temps de compilation en C ++

En C ++, existe-t-il un moyen de construire une liste de temps de compilation incrémentielle, selon le modèle suivant?

START_LIST(List) ADD_TO_LIST(List, int) ADD_TO_LIST(List, float) ADD_TO_LIST(List, double) END_LIST(List) 

Le résultat devrait être équivalent à:

 using List = Cons<int, Cons<float, Cons>>; 

J’ai également une ressortingction selon laquelle l’espace entre les macros doit être à la scope de l’ensemble. Je prévois de définir des éléments et de les enregistrer simultanément avec la liste en utilisant une macro, quelque chose comme ceci:

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

En d’autres termes, il n’est pas autorisé de définir START_LIST à quelque chose comme SomeTemplate< ou decltype( . Cela rendrait impossible l’ajout de nouvelles définitions entre les deux.

Notez que la solution peut également se présenter sous la forme d’une liste de “packs de parameters” (modèle variadique). Je tiens seulement à ce qu’il suive le modèle de définition incrémental indiqué ci-dessus.

Des spécialisations pourraient-elles être utilisées ici? Si ce n’est pas possible exactement avec le modèle ci-dessus, est-ce possible avec un peu plus de passe-partout?

Dans la solution propre à OP, cela ne fonctionne que pour une étendue globale, pas pour une étendue de classe ni pour une étendue de fonction. Mon implémentation ici fonctionne pour toute la scope globale, de classe et de fonction. Un autre avantage par rapport à la solution OP est que ma solution permet le chevauchement des paires START_LIST / END_LIST à listes multiples, c’est-à-dire que différentes constructions de liste peuvent être entrelacées.

Une petite limitation est qu’il utilise la macro __COUNTER__ , qui ne fait pas partie de starndard, mais est bien pris en charge par gcc, clang et MSVC. La portabilité n’est donc pas un gros problème ici. Une autre chose concerne la scope de la fonction, elle doit utiliser une macro distincte START_LIST_FUNC et ADD_TO_LIST_FUNC car je me sers de la résolution de surcharge de la fonction, mais elle ne peut pas déclarer de fonction static au niveau de la classe.

EDIT: incorporez l’idée de ListReverseHelper à partir du commentaire d’OP pour la rendre beaucoup plus simple.

 #include  #include  using namespace std; struct Nil {}; template  struct Cons {}; template  struct ListReverseHelper; template  struct ListReverseHelper { using Type = Reversed; }; template  struct ListReverseHelper, Reversed> { using Type = typename ListReverseHelper>::Type; }; template  struct ListMakerKey : ListMakerKey {}; template  struct ListMakerKey {}; #define START_LIST_(name, modifier) \ struct name##_ListMaker {}; \ modifier Nil list_maker_helper_(ListMakerKey); #define ADD_TO_LIST_(name, type, modifier) \ modifier Cons{}))> \ list_maker_helper_(ListMakerKey); #define END_LIST(name) \ using name = typename ListReverseHelper{})), 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; } 

Compilé sous g ++ - 4.8:

 [hidden]$ g++ -std=c++11 x.cpp && c++filt -t `./a.out` Cons > > > > > Cons > > Cons > > > Cons > > > 

Vous pouvez utiliser directement les modèles variadiques C ++ 11, ils permettent d’écrire des listes de types de manière beaucoup plus sophistiquée que l’approche classique « head:tail » fonctionnelle:

 template struct list{}; using l = list; 

D’un autre côté, si vous aimez la méthode de la queue à la queue, vous pouvez traduire d’un format à l’autre. Dans ce cas (Variadic à fonctionnel):

 template struct list{}; struct nil{}; template struct make_list; template struct make_list { using result = list::result; }; template<> struct make_list<> { using result = nil; }; 

Un exemple:

 //l is list>> using l = typename make_list::result; 

Bien sûr, vous pouvez utiliser des alias de modèles pour clarifier la syntaxe:

 template using make = typename make_list::result; using l = make; 

J’ai trouvé une solution! Bien que ce soit juste un peu plus limité; vous devez atsortingbuer un nom unique à chaque élément et définir un nombre maximal d’éléments (ici 100).

 #include  // Good old Cons and Nil. template  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  class ElemAt, int Offset, int Length> struct BuildListFromElemAt { using Result = Cons::Elem, typename BuildListFromElemAt::Result>; }; template  class ElemAt, int Offset> struct BuildListFromElemAt { 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  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  struct ListName##_ElemAt; // For each element, we resortingeve 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##_Size (Counter); \ template <> struct ListName##_ElemAt { \ using Elem = Value; \ }; // At the end, we resortingeve 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::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::value, ""); static_assert(std::is_same::value, ""); static_assert(std::is_same::value, "");