patterncppMinor
Typelist with extractor
Viewed 0 times
withextractortypelist
Problem
The purpose of the code is to store arbitrary number of types. The type can be extracted from it using Extract template, which uses template recursion to get the needed type. There will be compilation error if user will try to get out of bounds. I think it supports both fully defined types and only declared types.
In which ways the code below can be improved?
I doubted about whether or not make
I was thinking about SFINAE on out of range indexes, but thought the behavior will be surprising.
My another concern was creating objects of such classes. I couldn't figure out the way to restrict users from creating the objects. The only thing that came to mind is deleting all constructors, but it is strange, thus I decided to refrain from deleting constructors.
Example usage:
In which ways the code below can be improved?
#ifndef TYPELIST_H
#define TYPELIST_H
#include
struct NullType;
template
struct TypeList
{
using head = Head;
using tail = typename TypeList;
};
template
struct TypeList
{
using head = Single;
using tail = NullType;
};
template
struct Extract
{
using result = typename Extract::result;
};
template
struct Extract
{
using result = typename typelist::head;
};
#endifI doubted about whether or not make
NullType declaration only. In my opinion, leaving definition better says "don't use this" than empty definition. I was thinking about SFINAE on out of range indexes, but thought the behavior will be surprising.
My another concern was creating objects of such classes. I couldn't figure out the way to restrict users from creating the objects. The only thing that came to mind is deleting all constructors, but it is strange, thus I decided to refrain from deleting constructors.
Example usage:
#include "typelist.h"
#include
#include
int main()
{
using MyList = TypeList;
using extracted = Extract::result;
std::cout << (typeid(extracted) == typeid(bool));
}Solution
Naming
Templates depend on a consistent naming scheme for static polymorphism.
We can observe such consistent naming throughout the type traits offered by the standard library (from
This means that naming your extracted type alias as
What is a type list?
There doesn't seem to be a specific purpose for having a type list that is defined in terms of a head and tail when variadic templates can easily represent what a type list is. Thus, we can simply define a type list as:
What is extracting from a type list?
We can define a more general extraction template to extract from a variadic template.
Now that we have that, extracting from our type list is a simple subset operation based on the generalized template for extraction:
For which we can provide a convenience alias:
That we can now use as follows:
Final words
There are non-recursive ways to implement extraction (among other operations); they will most likely (possibly definitely!) require integer/index sequences. You can have a look at my answer for this question to see a non-recursive integer sequence implementation.
There are many more cool tricks to do with templates. Look on this very site or Stack Overflow!
Templates depend on a consistent naming scheme for static polymorphism.
We can observe such consistent naming throughout the type traits offered by the standard library (from
std::aligned_storage to std::add_pointer); the two most common names being:- A
typealias member when the type trait produces a type result.
- A
valuestatic data member to representconstexprvalues.
This means that naming your extracted type alias as
result is non-standard and breaks with any templates that assume the same naming convention from the standard library.What is a type list?
There doesn't seem to be a specific purpose for having a type list that is defined in terms of a head and tail when variadic templates can easily represent what a type list is. Thus, we can simply define a type list as:
template
class type_list {};What is extracting from a type list?
We can define a more general extraction template to extract from a variadic template.
template
class extract
{
static_assert( idx
struct extract_impl;
template
struct extract_impl
{
using type = typename extract_impl::type;
};
template
struct extract_impl
{
using type = T;
};
public:
using type = typename extract_impl::type;
};Now that we have that, extracting from our type list is a simple subset operation based on the generalized template for extraction:
template
struct type_list_extract;
template class TypeList, class... Types>
struct type_list_extract>
{
using type = typename extract::type;
};For which we can provide a convenience alias:
template
using type_list_extract_t = typename type_list_extract::type;That we can now use as follows:
int main()
{
using list_t = type_list;
static_assert( std::is_same>::value, "!" );
static_assert( std::is_same>::value, "!" );
static_assert( std::is_same>::value, "!" );
//type_list_extract_t; // static_assert fails: index out of bounds
}Final words
There are non-recursive ways to implement extraction (among other operations); they will most likely (possibly definitely!) require integer/index sequences. You can have a look at my answer for this question to see a non-recursive integer sequence implementation.
There are many more cool tricks to do with templates. Look on this very site or Stack Overflow!
Code Snippets
template <class... Types>
class type_list {};template <std::size_t idx, class... Types>
class extract
{
static_assert( idx < sizeof...( Types ), "index out of bounds" );
template <std::size_t i, std::size_t n, class... Rest>
struct extract_impl;
template <std::size_t i, std::size_t n, class T, class... Rest>
struct extract_impl<i, n, T, Rest...>
{
using type = typename extract_impl<i + 1, n, Rest...>::type;
};
template <std::size_t n, class T, class... Rest>
struct extract_impl<n, n, T, Rest...>
{
using type = T;
};
public:
using type = typename extract_impl<0, idx, Types...>::type;
};template <std::size_t idx, class TypeList>
struct type_list_extract;
template <std::size_t idx, template <class...> class TypeList, class... Types>
struct type_list_extract<idx, TypeList<Types...>>
{
using type = typename extract<idx, Types...>::type;
};template <std::size_t idx, class TypeList>
using type_list_extract_t = typename type_list_extract<idx, TypeList>::type;int main()
{
using list_t = type_list<char, bool, void>;
static_assert( std::is_same<char, type_list_extract_t<0, list_t>>::value, "!" );
static_assert( std::is_same<bool, type_list_extract_t<1, list_t>>::value, "!" );
static_assert( std::is_same<void, type_list_extract_t<2, list_t>>::value, "!" );
//type_list_extract_t<3, list_t>; // static_assert fails: index out of bounds
}Context
StackExchange Code Review Q#127925, answer score: 6
Revisions (0)
No revisions yet.