Click here to Skip to main content
15,884,094 members
Articles / Web Development / HTML

TypeLists and a TypeList Toolbox via Variadic Templates

Rate me:
Please Sign up or sign in to vote.
4.83/5 (16 votes)
16 Feb 2016CPOL34 min read 26.2K   775   13   3
This article is about metaprogramming with compile time type lists.

Introduction

A typelist is a compile-time collection of types. It is a powerful construct used in metaprogramming to generate code in compile-time. I learned about typelists from Andrei Alexandrescu's book, "Modern C++ Design" [1]. The book gives a definition of typelist, some tools to work with it, and examples of using the list.

With help of this book I was able to use typelists to register Creator functions in C++ Object Factory Pattern, to check the types of function parameters, and even to initialize a combobox in compile-time.

Inherently, the typelist is a template with variable number of template parameters (elements), no matter how you would define it. So when ISO introduced variadic templates in C++ 11, it was very tempting to try to overwrite typelist and related tools using these templates.

Then a gift from Microsoft arrived: Free Visual Studio 2015 Community Edition with the C++ 14 compiler. All needed elements have fallen in place, so I decided to try.

First of all, I have googled the term "typelist." I found the field rather overcrowded. A lot of authors have written about the lists and developed tools to work with them. Naturally, each author used his own name and definition for the typelist, as well as his own set of tools to work with the lists. Of course, all tools worked just fine, and there were plenty of bright and interesting things along the way.

It seemed that the only thing left to me was to gather it all together. I began to work, and it all turned out to be not so simple. Indeed, some tools needed only a name change. But I found no prototypes at all for other tools I needed, and some tools must be overwritten completely.

 So here you are going to see the results.

Acknowledgements

You can see that, because of multitude of authors, the question of who said "Aha!" first is very prickly. For example, you introduced a new partial specialization for a borrowed template structure. Without this specialization the borrowed template does not work with your definition of typelist. How will you refer to this thing? What is borrowed and what is new?

Anyway, the inventory of the TypeList's Toolbox was very much influenced by sources I am listing below. If I saw some tool in a source, I was saying to myself, "I will include something similar into my toolbox." But my code was sometimes different. Of course, all tools borrowed from other authors are marked appropriately.

The list of sources I used:

[1]" Modern C++ Design" by Andrei Alexandrescu, Addison-Wesley. Chapter III is dedicated to TypeLists. It was written before C++ 11, but it helped me to set an inventory of a Typelist Toolbox.

[2] <a href="http://fbb-git.github.io/cppannotations/">"C++ Annotations v. 10.3.0</a>" by Frank B. Brokken, Center for Information Technology, University of Groningen, Aug. 16, 2015. It is the web page where you can select the version and download a C++ Annotations pdf file. In the chapter "Advanced Template Use" there are a subchapters "Template TypeList processing" and "Using a TypeList." Basically, the author overwrites Andrei Alexandrescu's tools using variadic templates. I used it for adding tools to the Typelist Toolbox inventory of templates. Also some templates in my Toolbox are borrowed from these chapters as prototypes.

[3] "Meta User Manual" and "Tiny Metaprogramming Library" by Eric Niebler. It is very good and comprehensive library that has typelists (named 'lists') in its data types. I borrowed the definition of the typelist from there. I also used this library as a source to add tools to the Toolbox inventory and as a prototypes for some templates in the Toolbox. I do not know why, but my VC++ compiler in Visual Studio 2015 Community refuses to accept most constructs in the library's LAZY namespace, but I was able to read the code.

[4] "Simple C++ 11 Metaprogramming" by Peter Dimov. I have borrowed templates to convert TypeList to std::tuple and std::tuple to TypeList.

[5] "Effective Modern C++" by Scott Meyers. The first four chapters of this book you can download as a pdf file for free (click for URL here.) These chapters include very helpful information about rules to deduce template parameters. I needed it for writing tool templates and examples of usage of tools.

[6] "Applying Ant Colony Optimization Algorithms to Solve the Traveling Salesman Problem" by geoyar, on CodeProject. It is my article, and I used excerpts from it to show how to use Typelist to register classes with object factory.

Background

Some knowledge of C++ 11 and C++ 14 templates and variadic templates would help. The C++ reference might be very useful. Also you will need C++ 14 compiler. I used Microsoft Visual Studio 2015 Community Visual C++. It is great and it is free.

Using the code

All code is in the header file TypeList.hpp. You have to include it in your project and in your source (.cpp) files. The header includes <type_traits> header from C++14.

All templates are in the namespace "typelist", so you have to include "using namespace typelist;", or to use fully qualified names of templates in your source files.

I named my typelist "tlist." In text below (but not in the code)I also use terms "typelist," "list," and "TypeList." All these terms mean the type "tlist."

First of all, you have to define your specialization of the typelist, like using MyList = tlist<char, int, MyClass>;.

After that you can do what you want and need to do with this list, using tools from the header file.

All the tools are metafunctions - template structures, partially specialized for typelists.

If the result of the operation on the typelist is a type (like a type of a list's element), you access the result by the tool's  member 'type' like this:

using type_at_4 = tlist_type_at<4, MyList>::type;

If the result is an integer number (like an index of a type in the typelist), the member 'type' is std::integral constant template. Additional members 'value_type' and 'value' refer to value_type and value of the result and are copies of std::integral_constant template members. All value members are declared as static constexpr value_type value = type::value.

There is a table of all tools in the "TypeList.hpp":>

Table 1. TypeList Tools
Tool Definitions Members Usage Note
TypeList Definition
[3]
template <typename... Ts> tlist

type,
size()

using MyList =
tlist<char, char, int, long, long, int, float, double>;

constexpr auto szMyList = MyList::size();

type is the TypeList itself
size() returns a number of elements in the list
Check whether the List is empty template <class List> struct tlist_islistempty

type,
value_type
value

constexpr auto bEmpty = tlist_islistempty<<MyList>::value;

type is std::true_type or std::false_type
value_type is bool
value is true if the list is empty

Check whether the type is a TypeList template <class List> struct is_list type,
value_type
value
constexpr auto bIsList = is_list<<MyList>::value;

type is std::integral_constant<bool, value>,
value_type is bool
value is true if the type is TypeList

Check whether a list is the list of lists template <typename List>
is_listoflists
type,
value_type,
value
constexpr auto bLL =
is_listoflists<MyList>::value
 type is std::integral_constant<bool, value>,
value_type is bool,
value
is true or false.
Access the element by index [2]

template <size_t idx, class List>
struct tlist_type_at

type

using type_at_3 =
tlist_type_at<3, MyList>::type;

type is a type of the list's element at the index idx. If index is out of list's bounds, static_assert is raised.

Search a TypeList for the first occurrence of a type T

template <typename T, class List>
struct tlist_index_of

type,
value_type
value

constexpr auto idx =
tlist_index_of<<double, MyList>::value;

type is std::integral_constant<int, value>
value_type is int
value is the index of the first occurrence of the type T in the TypeList or -1 if the type is not found

Search a TypeList for the first occurrence of not a Type T template <typename T, class List>
 struct tlist_index_of_not
type,
value_type,
value
constexpr auto idx =
tlist_index_of_not<char, MyList>::value
type is std::integral_constant<int, value>
value_type is int
value is the index of the first occurrence of the type not equal to T in the TypeList or -1 if the TypeList does have only elements of type T or is empty.
Get the first element in the TypeList

template <class List>
struct tlist_front

type

using front_type = tlist_front<<MyList>::type;

Returns the first element of the List. Raises static_assert if the List is empty

Get the last element in the TypeList template <class List>
struct tlist_back

type

using back_type = tlist_back<MyList>::type;

Returns the last element of the List. Raises static_assert if the List is empty

Push an element to front template <typename T, class List>
struct tlist_push_front
type using NewList =
tlist_push_front<double, MyList>::type
type is the appended list
Push an element to back template <typename T, class List>
struct tlist_push_back
type using NewList =
tlist_push_back<long, MyList>::type
type is the appended list
Remove an element of type T from a list
[2]
template <typename T, class List>
struct tlist_erase_type
type using NewList =
tlist_erase_type<double, MyList>::type;
Erases first occurrence of the type T from the list. If the type is not in the list, returns the original list.
Remove the element at the given index
[2]
template <size_t idx, class List>
struct tlist_erase_at
type using NewList =
tlist_erase_at<3, MyList>::type;
Erases an element at the index idx;
If the index is out of List's bounds, static_assert is raised.
Remove the front element from a list template <class List>
struct tlist_pop_front
type using NewList = tlist_pop_front<MyList>::type; type is a the TypeList without the front element (a result might be an empty list) or empty list if called on the empty list
Remove the last element from a list template <class List>
struct tlist_pop_back
type using NewList = tlist_pop_back<MyList>::type; type is a the TypeList without the last element (a result might be an empty list) or empty list if called on the empty list
Remove all elements
of a type T
template <typename T, class List>
struct tlist_erase_all

template <typename T, class List>
struct tlist_deeperase_all
type using NewList =
tlist_erase_all<char, MyList>::type;
using NewList =
tlist_deeperase_all<char, MyList>::type
If MyList is a list of lists, the first template processes the list's elements as a whole types; the second version removes all types T from each list of a list of lists.
Remove all duplicates template <class List>
struct tlist_erase_dupl

template <class List>
struct tlist_deeperase_dupl
type using NewList =
tlist_erase_dupl<MyList>::type;
using NewList =
tlist_deeperase_dupl<MyList>::type
If MyList is a list of lists, the first template processes the list's elements as a whole types; the second version removes duplicates from inside the list of lists elements (lists)
Remove first k elements from the List template <size_t k, class List>
struct tlist_erase_firsts
type using NewList =
tlist_erase_firsts<4, MyList>::type;
Removes maximum k elements from the front of TypeList, Returns an empty list if k ≥ list size.
If k == 0 static_assert is raised
Remove last k elements from the List template <size_t k, class List>
struct tlist_erase_lasts
type using NewList<4, MyList>::type Removes maximum k elements from the tail of TypeList, Returns an empty list if k ≥ list size.
If k == 0 static_assert is raised
Replace the first occurrence of the element T with the element R template <typename R, typename T, class List> struct tlist_replace_first type using NewList =
tlist_replace_first<long long, float, MyList>::type;
Does nothing if the element T is not in the TypeList
Replace the element at index k with the element R template <size_t k, typename R, class List>
struct tlist_replace_at
type using NewList =
tlist_replace_at<4, double, MyList>::type;
If the index is out of bounds, static_assert is raised.
Replace all occurrences of the element T with the element R template <typename R, typename T, class List> struct tlist_replace_all

template <typename R, typename T, class List struct tlist_deepreplace_all
type using NewList =
tlist_replace_all<int, char, MyList>::type
using NewList =
tlist_deepreplace_all<MyList>::type
The first template replaces all occurrences of T
with R; it processes Ts as a whole types. The second template accepts both plain lists and lists of lists. It processes plain lists similar to the first template but if MyList is a list of lists, the second version replaces all elements T inside the list's elements (lists)
Count all occurrences of the type T
in the TypeList
template <typename T, class List> struct tlist_count_type type,
value_type
value
constexpr auto occurrence_float =
tlist_count_type<float, MyList>::value;
If the type T is not in the List, value = 0;
type = std::integral_constant<size_t, value>
value_type is size_t
Count all occurrences of type T in plain TypeList or inside each element of a list of lists template <typename T, class List>
struct tlist_deepcount_type
type using res_cnt =
tlist_deepcount_type<int, MyList>::type
The type is std::integer_constant<size_t, cnt> for a plain TypeList or std::integer_sequence<size_t, cnts...> for a list of lists
Get all indexes of a type T in the TypeList template <typename T, class List> struct tlist_all_idx

template <typename T, class List> struct tlist_all_deepidx
type using idx_seq = tlist_all_idx<double, MyList>::type;
using idx_seqs = tlist_all_deepidx< long, MyList>::type
The first template processes list's elements as a whole types, and type is std::integer_sequence<size_t, Is...>.
If there are no elements of type T, the sequence is empty.
The second template accepts plain lists and lists of lists. It processes each list of a list of lists as a separate list, and the resulting std::integer_sequences are packed in the TypeList.
For lists of mixed elements that include some lists and other types, the second tool returns the same result as the firs tool.
Test whether a list is the list of integer_sequnces template <class List>
is_listofiseqs
type,
value_type,
value
constexpr auto bLSeq =
is_listofiseqs<MyList>::value
The type is std::integral_constant<bool, value>.
Reverse the order of elements in the TypeList template <class List>
struct tlist_reverse
type using NewList = tlist_reverse<MyList>::type;  
Convert a list of lists into plain TypeList template <class List>
struct tlist_flatten_list
type using lst_flattened =
tlist_flatten_list<MyListOfLists>::type
Raises static_assert if the argument is nor list of lists
Concatenate TypeLists template <class... Lists>
struct tlist_concat_lists<Lists...>
type using NewList =
tlist_concat_lists<List1, List2, List3>::type;
If not all Lists are of type tlist, static_assert is raised.
Add to the front of a TypeList first k elements from the
front of a second
TypeList
template
<size_t k, class List1, class List2> struct tlist_move_firsts
type using MyIncreasedList =
tlist_move_firsts<4, MyList1,
MyList2>>::type
A number of elements to transfer must be
> 0; static_assert will be raised other way
Split the TypeList at many pivots template
<class List, size_t... Ls>
struct tlist_split
type using NewList =
tlist_split<MyList, 2, 3, 1>::type;
Ls... are numbers of elements in new lists. They do not include the number of elements in the last list.
type
is a list of resulting lists. In the example of the usage the result is the list of lists of sizes 2, 3, 1, and the rest of MyList elements;
static_assert is raised if sum of the sizes is ≥ the number of elements in the original list. It also is raised if the sequence of sizes includes zeros.
Get size of max TypeList's elements template <class List>
struct tlist_max_type_size
 
type,
value_type,
value
constexpr auto max_sz =
tlist_max_type_size<MyList>::value;
 
The ftemplate analyzes list's elements as a whole types..
type is std::integral_constant<size_t, sizeof(max element)>, value_type = size_t.
If templates are called on an empty list, value = 0.
Get size of max elements in each list of a list of lists template <class List>
struct tlist_max_type_deep_size
type using iseq_max_sz = tlist_max_type_deep_size<MyListOfLists>::type; If templates are called on an empty list, value = 0.
The tool applies the previous template to each list in the list of lists, and packs results into type = std::integer_sequence.
If the argument is not a list of list, but still tlist, the tool works as a previous template
and returns std::integral_constant<size_t, value>.
Get the first element of given size template <size_t k, class List>
struct tlist_firsttype_of_size

template <size_t k, class List>
struct tlist_firsttypedeep_of_size
type using type_sz_2 =
tlist_type_of_size<2, MyList>::type;

using types_sz_8 =
tlist_firsttypedeep_of_size<8, MyListOfLists>::type
The first template analyzes elements of a list as a whole types; it returns the first type of size k. If there is no such type, type = std::false_type.
The second template has a special treatment for a list of lists: it searches for first elopement of size k inside each list of list of lists, and type is tlist with the types found in each lists If some particular list of list of lists does not have elements of size k, std::false_type is inserted in result.
Get all elements of given size k template <size_t k, class List>
struct tlist_all_types_of_size

template <size_t k, class List> struct tlist_all_typesdeep_of_size
type using types_of_size_4 =
tlist_all_types_of_size<4, MyList>::type

using types_of_size_4 =
tlist_all_typesdeep_of_size<MyList>::type
The first template analyzes elements of a list as a whole types; it returns the list of types of size k. If there is no types of size k, the result is an empty list..
For the list of lists the second template applies the search to each list in the list of lists; the result is a list of lists of types found. All duplicates are removed from resi;ts.
Get all types greater or equal to
size k
template
<bool bEqual, size_t k, class List> struct tlist_types_greater

template <bool bEqual, size_t k, class List>
struct tlist_typesdeep_greater
type using all_types_greater_2 =
tlist_types_greater<true, 2,MyList>::type;

using all_types_greater_4 =
tlist_typesdeep_greater<false, 4, MyList>::type
type is a TypeList of elements of size > k
if bEqual == false or size ≥ k if bEqual == true.
For a list of lists the second template returns a list of lists where the elements of the result are lists of types greater than k in each of elements of original list of lists. All duplicates are erased from the results
Get all types lesser of equal to
size k
template
<bool bEqual, size_t k, class List>
struct tlist_types>less

template <bool bEqual, size_t k, class List> struct tlist_typesdeep_less
type using all_types_less_4 =
tlist_types_less<true, 4, MyList>::type

using all_types_less_5 = tlist_typesdeep_less<false, 5, MyList>::type
As in the previous template, but for types of sizes less or equal to k
Convert TypeList to std::tuple and back
[4]
template <class Src, template<typename...> class Trg>
using convert = typename convert_impl<Src, Trg>::type
  using MyTuple =
convert<MyList, std::tuple;
using MyList =
convert<MyTuple, tlist>;
Borrowed from Peter Dimov [4]

You have to remember that all these types, value_types, and values are compilation-time entities. You have to add some code to access them in run time, if you want make them visible.

Also keep in mind that if the tool returns a type, a value_type and a value, and you need, for example, both type, and value, it might be wiser first to get the type, that is std::integral_constant, and use its members 'value_type' and 'value' instead of tool's members to avoid extra recursions using the tool again to get value.

The code uses static_assert to prevent use of invalid parameters.

A tool might return an empty TypeList, or empty std::integer_sequence as legitimate results. For example, if you call tlist_all_idx <float, MyList>::type on plain TypeList without such elements, you will get empty std::integer_sequence<size_t> as a result. TypeList can have empty std::integer_sequence as a legitimate element too. So it is up to you to decide how to distinguish them.

And the last thing: tlist_all_idx<element_type, TypeList>::type returns std::integer_sequence of related indexes. tlist_all_idxdeep returns TypeList of std::integer_sequences. Be careful and check the result type if you are not sure what argument was passed.

You will find some tools to work with std::integer_sequence in the file TypeList.hpp.

The example of usage of TypeList Toolbox members is there:

C++
#include "type_traits"
#include "TypeList.hpp"

using namespace std;                                
using namespace typelist;

int main()
{
  using MyTypeList = tlist<char, char, int, long, float, double>; // Specialization for your List
  constexpr auto sz = MyTypeList::size();                         // Number of elements in your List (5)

  using type_at_2 = tlist_type_at<2, MyTypeList>::type;           // The type is long 
  constexpr auto bSame = is_same<type_at_2, long >::value;        // true			

  using type_at_12 = tlist_type_at<12, MyTypeList>::type;         // Raises static_assert "tlist_type_at: Index out of bounds..."    	
//.................................................................
   return 0;
}

You find more examples in the file "TypeList.cpp."

Points of Interest

To work with the TypeList Toolbox you need the C++ 14 compatible compiler. I have used the following C++14 features:

  • typename for parameter pack instead of class in C++ 11
  • Extended constexpr keyword
  • std::integer_sequence

I used VC++ from Microsoft Visual Studio 2015 Community Edition Update 1. It is free and it is very good

TypeList Definition: Head and Tail or just Parameter Pack?

Andrei Alexandrescu in [1] gave us the following definition of TypeList:

C++
template <class T, class U>
struct TypeList
{
  typedef T Head;
  typedef U Tail;
};

Of course, it is easy to rewrite it in C++ 14 terms:

C++
template <typename T, typename... Ts>
struct TypeList
{
  using Head = T;
  using Tail = TypeList<Ts...>;
};

But there is a problem: the TypeList defined in this way cannot be truly empty, it has to include type T at least. If you would try to instantiate TypeList<>, the compiler will complain "there is not enough template parameters."

Andrei has introduced a special type NullType that is always present at the end of the TypeList. It means that the empty list will always have one element. Variadic templates allow us to have truly empty lists because the empty parameter packs are OK. I would like to use a definition of the List without Heads and Tails:

C++
template <typename... Ts>
struct tlist{};

Still there is a minor problem: with this definition you have to have an additional meta function to get a number of elements in the list. If you name this function tlist_size you have to call tlist_sise<MyList>::value to get the number. This is different from a C++ use of function size() to get a number of elements in a STL containers. So I decided to go with STL and borrowed the definition of TypeList from [3]:

C++
template <typename... Ts>
struct tlist
{
  using type = tlist;
  static constexpr size_t size() noexcept {return sizeof...(Ts);}
};

The member "type" is a sort of syntactic sugar: sometimes it is more convenient to use MyList::type than just MyList.

Please note that now the empty list is a legitimate member of the family, because the parameter pack with zero parameters is allowed.

Keep in mind that the number of elements in the list is sizeof...(Ts), not sizeof(MyList). On my PC sizeof(MyList) = 1 for any tlist.

We still need NullType but not as a mandatory element of TypeList.

General Design Considerations

All tools in TypeList Toolbox are implemented as metafunctions (structure templates.) To get some results we have to iterate over TypeList's elements or element indexes. In metaprogramming (in compile-time) iteration is recursion. Recursion has to stop somewhere. So, for each tool we have to have template definition or declaration and a specializations for recursion and stop conditions.

First, we have to choose between forward declaration of the tool, like template <size_t k, class List> struct tlist_index_of; or definition of an empty class like template <size_t k, class List> struct tlist_type_at {}. We also can use variadic templates from the beginning or declare or define some general template and after that use partial specializations for parameter packs. I find more convenient to declare or define a general template and after that use partial specialized templates for TypeLists.

Because tools in the header, TypeList.hpp are always using template specializations to work with TypeLists, the compiler is trying to instantiate tool's general definition only if the tool is called on a wrong class. We will get error messages, but the messages are different whether we are using declaration or definition.

For example, if we have a class TC that is not a TypeList, and call tlist_type_at<4, TC>::type, the compiler cannot find the specialization for TC and will try to instantiate the general template. It falls, and we will get an error message. For declaration the error message is: "use of undefined type , typelist::tlist_type_at<4,TC>'', and for definition of the empty class it is: "type': is not a member of 'typelist::tlist_type_at<4,TC>."

I like the first message more, so in TypeList.hpp I am mostly using forward declaration of templates before specializations. There are some exceptions. I will discuss then later.

Of course after the first error message you might get more of them, but the very first messages are as described.

Members of the tool structure templates

The next problem is what members we have to provide to access results of applying the tools.

The results are either types as in tlist_type_at or numbers, as in tlist_index_of. With the types all is clear: the type is a type, so the member of the structure template is "type." But how to define a type and a value of a number that is returned by the tool like tlist_index_of. The value must be a compile-time constant. There are two ways to define these constants: static member of the structure/class or enumeration value. Because enum is its own types there might be a problem when you need a type of the returned number. So I chose static member like this:

C++
template <typename... Ts>
struct tlist_index_of<tlist<Ts...>
{
   .........................................................
   using type = std::integral_constant<size_t, idx>;
   using value_type = typename type::value_type;
   static constexpr value_type value  = type::value;
};

As you see, I am using functionality provided by C++ standard.

If the result of an operation on a TypeList is a type, the tool's member is just that: ' type':

C++
template <size_t k, typename... Ts>
struct tlist_type_at<tlist<Ts...>>
{
 . .........................................
  using type = .......;
};

Initial Conditions

Sometimes you have to set initial condition for recursion. For example, tlist_type_at might start from the index zero, tlist_split needs an empty list to begin to pack spitted lists. To make developers' life easy, I wrote the implementations of such tools first and wrappers that include these implementations and provide initial types or values and are using these implementation like :

C++
template <size_t idx, typename T class List>       // Implementation
struct tlist_index_of_impl ;

......................................

template <typename T, class List>
struct tlist_index_of;

template <typename T, typename H, typename... Ts>  // Wrapper of an implementation with initial index zero
struct tlist_index_of<T, tlist<H, Ts...>>
{
  using type = typename tlist_index_of_impl<0, T, tlist<H, Ts...>>::type; // index zero is provided
  using value_type = typename type::value_type;
  enum {value = value};
};

I am not hiding implementations in a nested namespace (e.g. 'details') or inside wrappers for sake of simplicity.

What if... : Abnormal Situations, static_assert,, the empty list, and empty std::integer_sequence

There never is a guarantee that template arguments you are supplying to a tool are of correct types or values. We have discussed what happen if you call a tool on a class that is not a typelist, but you also could look for an element that does not exist in your TypeList. Or index for the search the list is way beyond the number of elements in the list. Things happen.

Of course, these are mistakes. Sometimes you ask a compiler to do something it just cannot do. For example, you request a compiler to split TypeList with 6 elements into two TypeLists: one with 12 elements and a second with remnants. The compiler has no clue what you really want, and does not know what to do. On this occasion it is appropriate to raise static_assert as I do:

C++
// Real code is more complicated
template <size_t k, typename... Ts >
struct tlist_split<k, tlist<Ts...>>
{
  std::static_assert(k < sizeof... (Ts), "Pivot point is not inside an original list");
  std::static_assert(k != 0, "Nothing to split: the pivot point is at zero");
  .....................................................................................
};

A different kind of mistake is when an operation proceeds, but does not deliver an expected result. Let's assume we are trying to get a type at some index and that index is outside of TypeList' s bounds. What to do? We can proceed beginning from index zero, and recurs up to the end of the list. After that we can stop recursion and return some special type like NullType. The result is ambiguous, because NullType is a type and also might be an element of this or other typelists. The other option is to raise static_assert, but it will stop compilation altogether. I decided to follow an example of C++ where reading beyond container's bounds raises run-time assert and choose the second option.

Sometimes all is running just fine but results are not what was expected. For example, you are searching for types of size 4 bytes in your list that has only char's and doubles. No such types are found. The tool returns the empty TypeList. The empty TypeLists, std::integer_sequences, and indexes -1 are legitimate negative results in this Tool Box.

Now let us discuss some particular tools and topics.

What is a size of NullType?

Sometimes you need a special type of size zero, for example, to start recursion with an element of size zero. But any type you define takes some memory. There are no types of size zero in C++.

NullType is a special and is declared as an empty class. On my PC sizeof(NullType) = 1, as is sizeof(tlist<typename... Ts>). So we need some way around. I had to introduce template:

C++
class NullType
{};

template <typename T> 
struct Sizeof
{
  static constexpr size_t value = std::is_same<T, NullType>::value ? 0 : sizeof(T);
};

Is a type T a TypeList?

To check whether some compile-time type is a TypeList I wrote a variant of well-known template (e.g. see [1], 2.7):

C++
template <typename T>
struct type_to_type
{
  using origin = T;
  type_to_type() {}
};

template <typename T>
struct is_tlist
{
private:
  typedef char (&yes)[1];
  typedef char (&not)[2];
  
  template <typename... Ts>
  static yes Test(type_to_type<tlist<Ts...>>) {}
  
  static no Test(...) {}

public:
  using type = std::integral_constant<bool, sizeof(Test(type_to_type<T>())) == 1>;
  using value_type = typename type::value_type;
  static constexpr value_type value = type::value;
};

Similar template is to test whether a type is a std::integer_sequence.

Split TypeList into Multiple TypeLists

This tool splits a TypeList into multiple lists. You supply number of elements in spitted lists as a parameter pack of size_t numbers. The pack does not include number of elements in the last list of the result: last list includes remainder of the original list's elements. So the number of elements (lists) in the resulting list of lists is the size of parameter pack plus one. You receive a list of lists as a result (type).

Obviously, the sum of numbers in the parameter pack must be less than the number of elements in the original list because we have to left some non-zero remainder for the last spitted element. Also, the parameter pack cannot include zeros, because zero means an empty list. In context of the list splitting the list with zero length means no splitting at this point, because the pivot point does not change after zero elements would remove from the original list.

If these conditions are not satisfied, we cannot perform the splitting. The tool must raise static_assert.

To work with template arguments, we have to somehow organize them. We do this by inserting arguments in std::integer_sequence<size_t, lengths...>. We provide metafunctions iseq_sum<class ISeq> and iseq_index_of<typename T, T nmb, class ISeq> to check template arguments.

Below is the code.

First, we need a helper that moves first l elements from one list to the end of the second list in the list of two lists:

C++
// Helper: move first l elements from one list to the tail of the second list
template <size_t l, class List1, class List2>
struct tlist_move_firsts;

template <typename H, typename... T1s, typename... T2s>                // Stop condition, last element is being moved
struct tlist_move_firsts<1, tlist<T1s...>, tlist<H, T2s...>>
{
  using type = tlist<tlist<T1s..., H>, tlist<T2s...>>;
};

template <size_t l, typename H, typename... T1s, typename... T2s>      // Recursion
struct tlist_move_firsts<l, tlist<T1s...>, tlist<H, T2s...>>
{
  static_assert(l > 0, "Number of elements to transfer must be > 0");  // Do not move nothing
  using type = typename tlist_move_firsts<l - 1, tlist<T1s..., H>, tlist<T2s...>>::type;
};

Next, we have to use this helper. The idea is to take an empty list, move the first k elements, as requested by an appropriate template argument, from the original list into the empty list, add the new list to resulting list of list, and repeat this operation with remainders of the original list:

C++
struct tlist_split_impl;
                   
template <size_t l, typename... Lists, typename... Tails>                                     // At the end of the original list
struct tlist_split_impl <std::integer_sequence<size_t, l>, tlist<Lists...>, tlist<Tails...>>  // The list before remainders
{
private:
  using lst_split_2 = typename tlist_move_firsts<l, tlist<>, tlist<Tails...>>::type;         // Fill an empty list with l elements
  using first = typename tlist_type_at<0, list_split_2>::type;                               // Get elements of lst_split_2
  using second = typename tlist_type_at<1, lst_split_2>::type;
public: 
  using type = tlist<Lists..., first, second>;					 // Add to result
};
	
template <size_t l, size_t... Ls, typename... Lists,  typename... Tails>                    // Recursion
struct tlist_split_impl<std::integer_sequence<size_t, l, Ls...>, tlist<Lists...>, tlist<Tails...>>
{
private:
  using lst_split_2 = typename tlist_move_firsts<l, tlist<>, tlist<Tails...>>::type;
  using first = typename tlist_type_at<0, lst_split_2>::type;                              // We operating on list of two lists
  using second = typename tlist_type_at<1, lst_split_2>::type;
  using lst_res = tlist<Lists..., first>;
public:
  using type = typename tlist_split_impl<std::integer_sequence<size_t, Ls...>, lst_res, second>::type;  
};

Here we made a temporary list of two lists with the empty list and remainder of the original list (at first step the remainder is the original list itself.) We transferred first l elements from the remainder of the original list into the empty list. After that we added the first list from the temporary list of two lists to the would be resulting list of lists, and passed the temporary result and new remainder to next step in recursion. To get needed components we used tlist_type_at.

Now it is a time to wrap it all together with initial conditions (empty lists) and argument tests. Please note that you do not have to bother yourself with building std::integer_sequence. You have to provide only set of lengths, the tool will take care about all other things:

C++
// Wrap the list lengths into integer_sequence, test its parameter pack, and split the original list

template <class List, size_t... Ls>                // List is the original list to split, Ls are lengths of components of result
struct tlist_split;

template <typename... Ts, size_t... Ls>
struct tlist_split<tlist<Ts...>, Ls...>
{
// Check sum of lengths; because the last list in the result cannot have zero elements, the sum of lengths of all previous
// elements of resulting list of lists must be less than the number of elements in the original list
  using lengths = std::integer_sequence<size_t, Ls...>;        // Prepare integer_sequence
  using lengths_sum = typename iseq_sum<lengths>::type;        // Get sum of lengths
  static_assert<lengths_sum::value < tlist<Ts...>::size(), "Split: Check sum of component lengths"); ;
// Test for the first zero in the sequence; if it is here raise static_assert (don't care about other zeros
  static_assert(iseq_index_of<size_t, 0, lengths>::value == -1, "Split: No zero lengths allowed");
// Go
  using type = typename tlist_split_impl<std::integer_sequence<size_t, Ls...>, tlist<>, tlist<Ts...>>::type; 
};

There is an example of applying tlist_split :

C++
using lst_msplit = tlist_split<tlist<char, char, int, long, int, float, float, int, double>, 2,2,3>::type;
constexpr auto bLL = 
        std::is_same<lst_msplit, tlist<tlist<char, char>, tlist<int, long>, tlist<int, float, float>, tlist<int, double>::value;
// bLL == true

Please note that we have introduced a special kind of TypeList, list of lists: all elements of it are lists. Sometimes we will want to do some special processing for this type of lists. I am going to discuss it momentarily.

Erasing elements from TypeList

I am going to discuss some subtleties of working with lists of lists using an example of tool for removing duplicates from TypeList.

In the Toolbox there are several tools to remove elements from TypeLists. The first four are borrowed from [2]: tlist_erase_type, tlist_erase_at, tlist_erase_all, tlist_erase_dupl. The additional four are tlist_pop_front, tlist_pop_back, tlist_erase_firsts, and tlist_erase_lasts.

tlist_pop_front and tlist_pop_back just call tlist_erase_at on index of first(0) or index of last element in the list. tlist_erase_firsts and tlist_erase_lasts are deleting no more than k elements from the front or the tail of the list. They are recursively using tlist_pop_front or tlist_pop_back.

If an index supplied to tlist_erase_at is out of list's bounds, static_assert is raised. Templates that are using tlist_erase_at also might raise it. For example, tlist_pop_front called on an empty list will assert because there is no element at index zero (no element at all.)

These tools are doing just that: Remove an element or elements from the list. But this functionality is not always enough if we have lists of lists.

Let us look at the result of splitting of the list in previous chapter. There are four TypeLists, two of them have duplicate elements inside, char in the first and float in the third. If you want to erase all duplicates in each elements (TypeList) of this list of lists and would call tlist_erase_dupl, the tool will consider elements (lists) of the result as whole entities. It will never go inside these lists. It might be not what you wanted. Of course, you can access each element individually by using tlist_type_at and call tlist_erase_dupl on each list individually. It is rather cumbersome. So I decided to automate this process and similar cases, and to have some universal solution that automatically selects right tool for plain TypeLists or for a lists of lists. For the list of lists it will always look inside the elements of it. On other occasions we will still need to process elements of list of list as whole types. For example, to remove duplicates of lists in the list of lists.

There is a code.

First, we need a tool to remove duplicates from a plain TypeList (borrowed from [2]):

C++
// Erase all duplicates from the list, borrowed from [2]
template <class List> struct tlist_erase_dupl;

template <>                        // Specialization for an empty list
struct tlist_erase_dupl<tlist<>>
{
  using type = tlist<>;
};

template <typename H, typename... Ts>
struct tlist_erase_dupl<tlist<H, Ts...>>
{
private:
  using unique_t = typename tlist_erase_dupl<tlist<Ts...>>::type;
  using new_t = typename tlist_erase_type<H, unique_t>::type;
public:
  using type = typename tlist_push_front<H, new_t>::type; 
};

For a list of lists, we will apply this tool to work inside elements of list of lists (if we want to):

C++
// Remove duplicates from each element of the list of list (not borrowed)
template <class List>
struct tlist_erase_dupl_ll
{
  using type = tlist<>;
};

template <typename... Hs, typename... ListEls>
struct tlist_erase_dupl_ll<tlist<tlist<Hs...>, ListEls...>>
{
private:
  using lst_clean = typename tlist_erase_dupl<tlist<tlist<Hs...>>>::type         // Process an individual list
public:
  using type = typename tlist_push_front<lst_clean, typename tlist_erase_dupl_ll<tlist<ListEls...>>::type>::type;
};

It just applies tlist_erase_dupl to each element of a list of lists and pushes the cleaned lists into resulting list of lists.

Now let us assemble a final template:

C++
template <class List>
struct tlist_deeperase_dupl;

template <typename... ListEls>
struct tlist_deeperase_dupl<tlist<ListEls...>>
{
private:
  static constexpr bool bLL = is_listoflists<tlist<ListEls...>>::value;                         // Selector
public:
  using type = typename conditional<bLL, typename tlist_erase_dupl_ll<tlist<ListEls...>>::type, // Is called on list of lists
               typename tlist_erase_dupl<tlist<ListEls...>>::type>::type;                       // Is called on general list
};

The tool automatically selects the right implementation for a plain list and a list of lists. It returns list withot duplicates for a plain TypeList, or a list of lists without duplicates in each elements of it.

There is one problem: std::conditional inside the partial specialization. It was defined in <type_traits> as template <bool B, class T, class F>. The template needs one non-type (bool) and two type arguments to be instantiated. When you call tlist_deeperase_dupl on list of lists, all is working just fine. But for a plain list the compiler cannot find appropriate partial specialization for tlist_erase_dupl_ll for the plain TypeList. So to accommodate the plain list I had to use a definition for general template, not a declaration as elsewhere. It is never executed, because for std::conditional::type, the switch in tlist_deeperase_dupl, always selects the right partial specialization.

And what about processing elements of lists of lists as whole types, not going inside? Well, you have to use the first template explicitly. Call tlist_erase_dupl, not tlist_deeperase_dupl.

You find other 'deep' tools in Table 1 above and in TypeList.hpp.

Replace elements of TypeList

The idea is simple: you iterate over the list and examine the first elements at the inspection points. It the element is of type to be replaced, you replace it, and move to the next point. Below I show how it looks for tlist_replace_all :

C++
// Replace all occurrences of a given type T in TypeList ; R is a replacement for type T

template <typename R, typename T, class List> struct tlist_replace_all;;

template <typename T, typename T>                                  // Specialization for an empty list
struct tlist_replace_all<R, T, tlist<>>
{					                                                    
  using type = tlist<>;                                                   
};

template <typename R, typename T, typename... Ts>                 // Replaces the element T with R                                    
struct tlist_replace_all<R, T, tlist<T, Ts...>>
{
  using type = typename tlist_push_front<R, typename tlist_replace_all<R, T, tlist<Ts...>>::type>::type;
};

template <typename R, typename T, typename H, typename... Ts>    // Not the element to replace: go farther
struct tlist_replace_all<R, T, tlist<H, Ts...>>
{
  using type = typename tlist_push_front<H, typename tlist_replace_all<R, T, tlist<Ts...>>::type>::type;
};

How does the instantiation proceed? Let's assume we have tlist<char, int, long> and we want to replace int with double . As a first step, we apply tlist_replace_all to the entire list. The front of the list is type char. It is not int, so we have to return it to the list. At this step we do not have the list to push this element to front of it. To get it we apply the same template to the reminder of the list, tlist<int,long> again. This time we have found the type to replace, so we have added double to waiting char to be pushed front. Because we still do not have a list to push waiting elements, we apply tlist_replace_all to tlist<long> again.

This time, long is added to types waiting to be pushed front. Now we have a TypeList to operate on: it is an empty list. The specialization for the empty list is instantiated, and it returns the type: the empty list. Now we have a list to push waiting elements into it. The last element waiting to be pushed front is pushed into this empty list. After that the previous element is pushed into resulting list with one element, (long) and so on. In the end, we find ourselves with tlist<char. double, long> .

The process is the same if there are no elements of the type we want to replace in the list. We just get an original list.

If we want to replace all elements of some type inside the elements of a list of lists, we need additional template similar to tlist_deeperase_dupl:

C++
template <typename R, typename T, class List>
struct tliat_replace_all_ll
{
  using type = tlist<>;                     // Anything will go, it never be executed, but is requested by std::conditional
};

template <typename R, typename T, typename... Hs, typename... ListEls>
struct tlist_replace_all_ll<R, T, tlist<tlist<Hs...>, ListEls...>>
{
private:
  using lst_replaced = typename tlist_replace_all<R, T, tlist<Hs...>>::type;
public:
  using type = typename tlist_push_front<lst_replaced, typename tlist_replace_all_ll<R, T, tlist<ListEls...>>::type>::type;
};
  
// Wrapper
template <typename R, typename T, class List>
struct tlist_deepreplace_all;

template <typename R, typename T, typename... ListEls>
struct tlist_deepreplace_all<R, T, tlist<ListEls...>>
{
private:
  static constexpr bool bLL = is_listoflists<tlist<ListEls...>>:value;
public;
  using type = std::conditional<bLL, typename tlist_replace_all_ll<R, T, tlist<ListEls...>>::type,
                                     typename tlist_replace_all<R, T, tlist<ListEls...>>::type>::type;
};

There are also tools to replace the first occurrence of a type in a list, and to replace the type at given index. The last tool raises static_assert if it receives an index out of list's bounds, because, obviously, it is an error.

Get all indexes of a type in TypeList

Again, the idea is simple: you iterate over the TypeList and check the types of elements you meet. If the type of this element is of type you are looking for, you add the element's index to a container you are using as index storage.

But what this container might be? It seems that the only candidate C++ 14 gives to us is std::integer_sequence. So, let us to start with empty sequence and add to it the index of element of type we are looking for each time we meet this element in the list. The code is:

C++
// Get all indexes of the type in the TypeList as a std::integer_sequence

template <size_t k, typename T, class List, class IntSeq> struct tlist_all_idx_impl;    // We have to start from the empty IntSeq
                                                                                      
template <size_t k, typename T, size_t... Idx>                                          // End of recursion
struct tlist_all_idx_impl<k, T, tlist<>, std::integer_sequence<size_t, Ids...>>
{
  using type = std::integer_sequence<size_t, Idx...>; 
};
	
template <size_t k, typename T, typename... Ts, size_t... Idx>                          // Element T is found; add its index to storage
struct tlist_all_idx_impl<k, T, tlist<T, Ts...>, std::integer_sequence<size_t, Idx...>>
{
  using type = typename tlist_all_idx_impl<k + 1, T, tlist<Ts...>, std::integer_sequence<size_t, Idx..., k>>::type;
};                                                  

template <size_t k, typename T, typename H, typename... Ts, size_t Idx>                  // Not the type T
struct tlist_all_idx_implt<k, T, tlist<H, Ts...>, std::integer_sequence<size_t, Idx...>> 
{
  using type = typename tlist_all_idx_impl<k + 1, T, tlist<Ts...>, std::integer_sequence<size_t, Idx...>>::type;
};

// Now let's hide initial conditions
template <typename T, class List> struct tlist_all_idx;

template <typename T, typename H, typename... Ts>        // Start from index zero and empty integer_sequence
struct tlist_all_idx<T, tlist<H, Ts...>>
{
  using type = typename tlist_all_idx_impl<0, T, tlist<H, Ts...>, std::integer_sequence<size_t>>::type;
};

This tool considers TypeList as a list of whole elements. But what about lists of lists? Again, if we want to get indexes of some type of element inside each list of the list of lists we have to apply apply tlist_all_idx to each element of the list of lists, and pack results in TypeList of integer_sequences :

C++
// Get all indexes of a type in each element of the list of lists

template <typename T, class Res, class List>
struct tlist_all_idx_ll
{
  using type = tlist<std::integer_sequence<size_t>>;// Anything will go, it will be never executed, but is requested by std::conditional
};

template <typename T, typename.. Rs, typename...Hs>
struct tlist_all_idx_ll<T, tlist<Rs...>, tlist<tlist<Hs...>>>
{
private:
  using iseq = typename tlist_all_idx_impl<0, T, tlist<Hs...>, std::integer_sequence<size_t>>::type;
public:
  using type = tlist<Rs..., iseq>;
};

template <typename T, typename... Rs, typename... Hs, typename... ListEls>
struct tlist_all_idx_ll<T, tlist<Rs...>, tlist<tlist<Hs...>, ListEls...>>
{
private:
  using iseq = typename tlist_all_idx_impl<0, T, tlist<Rs...>, tlist<Hs....>>::type;
public:
  using type = typename tlist_all_idx_ll<T, tlist<Res..., iseq>, tlist<ListEls...>>::type;
}; 

// Now wrap it all
template <typename T, class List>
struct tlist_all_deepidx;

template <typename T, typename... ListEls>
struct tlist_all_deepidx<T, tlist<ListEls...>>
{
private:
  static constexpr bool bLL = is_listoflists<tlist<ListEls...>>::value;
public:
  using type = typename conditional<bLL,
    typename tlist_all_idx_ll<T, tlist<>, tlist<ListEls...>>::type,
    typename tlist_all_idx_impl<0, T, tlist<ListEls...>, std::integer_sequence<size_t>>::type>::type;
};

Again, if you want to treat the list of list as a plain TypeList use tlist_all_idx.

Please pay attention that types the universal (deep) tool returns are different for a plain list and a list of lists. For the plain list it is std::integer_sequence, for the list of list it is tlist<std::integer_sequence...>.

So far, so good. You got your indexes, but how to work with them? I provided a set of tools to work with integer_sequence, all in the header TypeList.hpp:

  • template <typename T> struct is_iseq
  • template <size_t k, class IntSeq> struct iseq_nmb_at
  • template <typename T, T n, class IntSeq> struct iseq_index_of
  • template <class IntSeq> struct iseq_sum (Sum of all elements of the sequence)
  • template <typename T, T nmb, class IntSeq> struct iseq_remove_nmb
  • template <typename T, T nmb, class IntSeq> struct iseq_push_back

Tools for std::integer_sequence

If a tool for std::integer_sequence needs some number as a template argument and this number is not an index, that number must be of the type of the sequence's  value type. We might supply a wrong type. I solve this problem by using implementations to do all work, and the lot of static_assert in wrappers to check for erroneous types. Below is an example:

C++
// Get the index of the number
template <size_t idx, typename U, U n, class ISeq>                   // Implementation
struct iseq_index_of_impl;

template <size_t idx, typename U, U n>
struct iseq_index_of_impl<idx, U, n, std::integer_sequence<U>>       // The number id not found
{
  using type = std::integral_constant<int, -1>;
};

template <size_t idx, typename U, U n, U... Nmbs>>                   // The number found
struct iseq_index_of_impl<idx, U, n, std::integer_sequence<U, n, Nmbs...>>
{
  using type = std::integral_constant<size_t, n>;
};  
  
template <size_t idx, typename U, U n, U l, U... Nmbs>  
struct iseq_index_of_impl<idx, U, n, std::integer_sequence<U,  l, Nmbs...>>
{
  using type = typename iseq_index_of_impl<idx + 1, U, n, std::integer_sequence<U, Nmbs...>>::type;
};

// Wrapper to supply start index 0, test template arguments, and add members 'value_type' and 'value'
template <<typename U, U n, class ISeq>
struct iseq_index_of
{
private:
  static_assert(is_iseq<ISeq>::value, "Supplied class is not integer_sequence");   // integer_sequence is supplied
  using data_type = typename ISeq::value_type;
  static_assert(std::is_same<data_type. U>::value, "The number must be of the sequence value_type");      // Right ype of number        
public:
  using type = typename iseq_index_of_impl<idx, data_type, n, ISeq>::type;
  using value_type = typename type::value_type;
  static constexpr value_type value = type::value;
};

// Call it like this:
using MyISeq = std::integer_sequence<int, 0, -3, 6, 89>;                          // Looking for number 6 in sequence of int
constexpr auto idx_6 = iseq_index_of<MyISeq::value_type, 6, MyISeq>::value;	// idx_6 = 2

Of course you could just use the specialization of iseq_index_of_impl. The difference will be in error messages. If you would supply wrong type for the number, for example, long for the sequence of int's the first error message for implementation is "use of undefined type." Assertion in the iseq_index_of will output as a first error message text you have provided.

Convert TypeList to std::tuple

TypeList has no value members inside. You can instantiate it, but there is nothing you can do with it at run time (besides getting its size). Sometimes after working on the list in compile-tine you might want to use its elements (types) in some other classes in run time, e.g. transform it into std::tuple. There is a template from [4]:

C++
// Use list's parameter pack with some other class

template <class Src, template <typename...> class Trg>
struct convert_impl;

template <template<typename...> class Src, typename... Ts, template <typename... > class Trg
struct convert_impl<Src<Ts...>, Trg>
{
  using type = Trg<Ts...>;
};

template <class Src, template <typename...> class Trg>
using convert = typename convert_impl<Src, Trg>::type;

// And below we convert list to tuple
using MyList = tlist<char, int, long, float, double, void*>;
using MyTuple = convert<MyList, std::tuple>;
constexpr auto szMyTuple = tuple_size<MyTuple>::value;                                               // 6
constexpr auto bLT = is_same<MyTuple, std::tuple<char, int, long, float, double, void*>>::value;     // true
// Instantiate the tuple somewhere in your executable
MyTuple myTuple;

// Convert MyTuple back to TypeList
using RecList = convert<MyTuple, tlist>;
constexpr auto szList = RecList::size();                                                            // 6            
constexpr auto bTL = is_same<RecList, tlist<char, int, long, float, double, void*>>::value;          // true

Examples of utilizing TypeLists

You find examples of creating class hierarchies from TypeLists in [ 2]. It follows Andrei Alexandrescu path [1].

I will add example of registering classes from TypeList with object factory. The classes are Ant Colony algorithms [6]: CASTravel, CASETravel, CRASTravel, CBWASTravel, CMMASTravel, CACSTravel. Every class has a static member function Register.

First, we define the TypeList:

C++
using AlgTypeList = tlist<CACSTravel, CASETravel, CRASTravel, CBWASTravel, CMMASTravel, CACSTravel>:

We define a template to register classes from TypeList:

template <size_t idx, class List>
struct RegFactoryClasses;

template <typename H, typename... Ts>                                         // Register last class (might be not all list's types
struct RegFactoryClasses<0, tlist<H, Ts...>>
{
  using Result = H;
  static inline void Fn(void)
  {
    Result::RegisterWithFactory();
  }
};

template <size_t k, typename H, typename... Ts>
struct RegFactoryClasses<k, tlist<H, Ts...>>
{
  using Result = typename RegFactoryClasses<k-1, tlist<Ts...>>::Result;
  static inline void Fn(void)
  {
    Result::RegisterWithFactory();
    RegFactoryClasses<idx - 1, tlist<H, Ts...>>::Fn();
  }
};

Now we define:

C++
using RegAlgoritms = RegFactoryClasses<AlgTypeList::size() - 1, AlgTypeList>;

And at run time:

C++
RegAlgoritms::Fn();

What is in the package

As I already noted before, all tool templates are in file TypeList.hpp. The file also contains templates to work with std::integer_sequences. They are used in some TypeList tools. It included in typeListSource.zip.

To play with tools I have provided a file TypeList.cpp. Basically, it defines several TypeLists, applies some tools to them, and checks the results with std::is_same from <type_traits>.

If you build and run this cpp file you will see nothing. All processing is executed in compile-time. Checks also are performed in compile-time. So what use has that cpp file?

I have included a VS 2015 Community Update 1 solution, project files and source files in TypeListProject.zip. You can easily open the solution in VS 2015, build compile the debug configuration of TypeList.exe. You will able to play with tools by setting breakpoints and watching compile-time constants in Watch windows of Visual Studio in debug mode.

I have also included in TypeListProject.zip a doxygen project file, Doxyfile, to make a documentation (Folder Doc). If you want to generate html documentation, download Doxygen from its web site (it is free), start doxywizard.exe, load this file in wizard and after correcting directories of the TypeList.hpp to directories on your PC, run the wizard.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Verizon Internet Services
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionwhat are typelists for? Pin
Alex Osadchenko9-Jul-21 4:19
Alex Osadchenko9-Jul-21 4:19 
GeneralMy vote of 5 Pin
john morrison leon15-Mar-16 0:30
john morrison leon15-Mar-16 0:30 
GeneralGreat Post! Pin
koothkeeper17-Feb-16 6:25
professionalkoothkeeper17-Feb-16 6:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.