- 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
#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
Create a typelist with
using tl = typelist<TYPE_1, TYPE_2, ...>;
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>
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>
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
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 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.
This can be used to implement more complex filters.
See set
for an example.
True if the list contains the provied type:
constexpr auto has_int = typelist<int, float, char*>
::contains<int>;
// true
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)
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 }
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*>
Returns the type at the specified index
using first_type = typelist<int, float, char*>::at<0>; // int
Returns a slice of the typelist
using subset = typelist<int, float, char*>
::slice<0,2>;
// typelist<int, float>
Returns the number of types in the typelist
constexpr static auto size = typelist<int, float, char*>::size // 3
#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));
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>