HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Typelist with extractor

Submitted by: @import:stackexchange-codereview··
0
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?

#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;
};

#endif


I 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 std::aligned_storage to std::add_pointer); the two most common names being:

  • A type alias member when the type trait produces a type result.



  • A value static data member to represent constexpr values.



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.