Awesome
- Use
- Moving types in and out of a typelist
- Modifying a typelist
- Example - Generate a variant, with no duplicate types, that holds any value from a tuple
- API Samples
cpp-typelist <!-- omit in toc -->
#include <tuple>
#include <string>
#include <utility>
#include <variant>
#include "dhagedorn/types/typelist.hh"
using namespace dhagedorn::types;
using record = std::tuple<std::string, int, std::string, int>;
using record_field = typelist<>
::from<record>
::set
::push_front<std::monostate>
::as<std::variant>;
// std::variant<std::monostate, std::string, int>
A functional-style typelist for C++20.
Requires a C++20 compiler. GCC 10.1.0 and newer are known to work
Use
Create a typelist with
using tl = typelist<TYPE_1, TYPE_2, ...>;
Moving types in and out of a typelist
Create a typelist by extracting the types from another type with from
:
using number = std::variant<int, float, double>;
using number_types = typelist<>::from<number>;
// typelist<int, float, double>
Convert a typelist to a usable type with as
:
using number_types = typelist<>::from<number>;
using number = typelist<number_types>::as<std::variant>;
// std::variant<int, float, double>
Modifying a typelist
Supported operations are:
any_of
all_of
none_of
find_if
filter
contains
sort
transform
transform_with
transform_v
set
push_back
push_front
at
slice
size
trait_adapter
I've tried to follow names from the STL and the excellent range-v3.
Unless otherwise stated, all algorithms accept a predicate of type iter_predicate
:
[]<typename T>(){ return /** true/false based on T */; }`
An overloaded form is also available:
[]<typename T, std::size_t /*or auto*/ I>(){ ...; };
std type_traits can also be converted to predicates:
using only_ints = typelist<int, float, short>
::filter<trait_adapter<std::is_integral>>;
// typelist<int, short>
any_of, all_of, none_of
Equivalents of the std library's
constexpr auto is_integral = typelist<int, float, char*>
::any_of<[]<typename T>(){ return std::is_integral_v<T>; }>;
// true
find_if
Equivalent to std library's find_if
using is_floating_point = typelist<int, float, char*>
::find_if<[]<typename T>(){ return std::is_floating_point<T>; }>;
// float
filter
Filter out unwanted types:
using only_numeric = typelist<int, float, char*>
::filter<[]<typename T>(){ return std::is_numeric_v<T>; }>;
// typelist<int>
An overloaded predicate can also be used:
[]<typename T, std::size_t I, is_typelist CURRENT_LIST>[]( ...; );
CURRENT_LIST
is the current value of the filtered output list.<br>
This can be used to implement more complex filters.<br>
See set
for an example.
contains
True if the list contains the provied type:
constexpr auto has_int = typelist<int, float, char*>
::contains<int>;
// true
sort
Sorts the typelist using a predicate implementing strict weak ordering:
[]<typename A, typename B>(){ return sizeof(A) < sizeof(B); };
sort<>
is equivalent to calling:
using sorted = typelist<int, float, char*>::sort<[]<typename A, typename B>(){ return sizeof(A) < sizeof(B); };
// typelist<float, int, char*>
// (assuming 32 bit float, 64 bit int and pointers)
transform, transform_with, transform_v
Convert one list into another
transform
expects a target templated type:
using pointy_types = typelist<int, float, char*>
::transform<std::shared_ptr>;
// typelist<shared_ptr<int>, shared_ptr<float>, shared_ptr<char>>
transform_with
uses the return type of its predicate as
an expression to generate the new type:
using unsafe_pointy_ints = typelist<int, float, char*>
::transform_with<typename T>() -> T* {}>;
// typelist<int*, float*, char**>
transform_v
converts the typelist into a std::array:
constexpr std::array sizes = typelist<int, float, char*>
::transform_v<[]<typename T>{ return sizeof(T); }>;
// std::array{ 4, 4, 8 }
set
Generates a set (no duplicate types):
using record = std::tuple<int, float, char*, char*, float, int>;
using any_tuple_value = typelist<>
::from<record>
::set
::push_back<std::monostate>
::as<std::variant>;
// std::variant<std::monostate, int, float, char*>
at
Returns the type at the specified index
using first_type = typelist<int, float, char*>::at<0>; // int
slice
Returns a slice of the typelist
using subset = typelist<int, float, char*>
::slice<0,2>;
// typelist<int, float>
size
Returns the number of types in the typelist
constexpr static auto size = typelist<int, float, char*>::size // 3
Example - Generate a variant, with no duplicate types, that holds any value from a tuple
#include <tuple>
#include <utility>
#include <variant>
#include "typelist.hh"
using namespace dhagedorn::types;
using record = std::tuple<std::string, int, std::string, int>;
using record_field = typelist<>
::from<record>
::set
::push_front<std::monostate>
::as<std::variant>; // std::variant<std::monostate, std::string, int>
record_field tuple_runtime_get(std::size_t index, const std::tuple& rec) {
record_field out;
[&]<auto ...INDICES>(std::index_sequence<INDICES...>) {
auto test_element = [&]<auto I>() {
if (index == I) {
out = std::get<I>(rec);
}
};
((test_element.template operator()<INDICES>()) , ...);
}(std::make_index_sequence<std::tuple_size_v<record>>());
return out;
}
record row{"a", 1, "b", 2};
std::string value = std::get<std::string>(tuple_runtime_get(0, row));
API Samples
see sample.cc
using list = types::typelist<double, float, int, char, int, char, float, double>;
constexpr auto size = list::size;
constexpr auto has_double = list::any_of<[]<typename T>(){ return std::is_same_v<T,double>; }>;
constexpr auto is_mathy = list::any_of<[]<typename T>(){ return std::is_integral_v<T> || std::is_floating_point_v<T>; }>;
constexpr auto is_not_stringy = list::none_of<[]<typename T>(){ return std::is_same_v<T, std::string>; }>;
constexpr auto has_int = list::contains<int>;
constexpr auto has_int2 = list::any_of<types::trait_predicate<std::is_integral>>;
using with_string = list::push_back<std::string>;
using with_void = list::push_front<void>;
using set = list::set;
using no_floats = list::filter<[]<typename T>(){ return !std::is_floating_point_v<T>; }>;
using no_floats2 = list::filter<types::trait_predicate<std::is_integral>>;
using odds = list::filter<[]<typename T, auto I, types::is_typelist list>(){ return I%2 == 1; }>;
using sliced = list::slice<0,3>;
using first_integral = list::find_if<[]<typename T>(){ return std::is_integral_v<T>; }>;
using first_type = list::at<0>;
constexpr auto sizes = list::transform_v<[]<typename T>(){ return sizeof(T); }>;
constexpr auto indices = list::transform_v<[]<typename T, auto I>(){ return I; }>;
using pointy = list::transform_with<[]<typename T>() -> T* { return nullptr; }>;
using safe_pointy = list::transform<std::shared_ptr>;
using sorted = list::sort<>;
using sorted_backwards = list::sort<[]<typename A, typename B>(){ return sizeof(B) < sizeof(A); }>;
using variant = list::as<std::variant>;
using from_variant = list::from<variant>;
Evaluates to:
list: dhagedorn::types::typelist<double, float, int, char, int, char, float, double>
size: 8
has_double: true
is_mathy: true
is_not_stringy: true
has_int: true
has_int2: true
with_string: dhagedorn::types::typelist<double, float, int, char, int, char, float, double, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >
with_void: dhagedorn::types::typelist<void, double, float, int, char, int, char, float, double>
set: dhagedorn::types::typelist<double, float, int, char>
no_floats: dhagedorn::types::typelist<int, char, int, char>
no_floats2: dhagedorn::types::typelist<int, char, int, char>
odds: dhagedorn::types::typelist<float, char, char, double>
sliced: dhagedorn::types::typelist<double, float, int>
first_integral: double
first_type: double
sizes: 8, 4, 4, 1, 4, 1, 4, 8
indices: 0, 1, 2, 3, 4, 5, 6, 7
pointy: dhagedorn::types::typelist<double*, float*, int*, char*, int*, char*, float*, double*>
safe_pointy: dhagedorn::types::typelist<std::shared_ptr<double>, std::shared_ptr<float>, std::shared_ptr<int>, std::shared_ptr<char>, std::shared_ptr<int>, std::shared_ptr<char>, std::shared_ptr<float>, std::shared_ptr<double> >
sorted: dhagedorn::types::typelist<char, char, float, int, int, float, double, double>
sorted_backwards: dhagedorn::types::typelist<double, double, float, int, int, float, char, char>
variant: std::variant<double, float, int, char, int, char, float, double>
from_variant: dhagedorn::types::typelist<double, float, int, char, int, char, float, double>