Code: Select all
template<typename...Ts>
struct list {
using type = list<Ts...>;
static constexpr auto size = sizeof...( Ts );
using empty = std::conditional_t<size == 0, std::true_type, std::false_type>;
static constexpr bool is_empty_v = empty::value;
struct front {
static_assert( size > 0, "Cannot get front(), list is empty." );
template<typename U, typename...Us> struct helper {
using type = U;
};
using type = helper<Ts...>::type;
};
template<typename T> struct push_back {
using type = list<Ts..., T>;
};
template<typename T> struct push_front {
using type = typename list<T, Ts...>;
};
struct pop_back {
template<typename List, typename U, typename...Us> struct copy_pop_ {
static constexpr auto count = sizeof...( Us );
using next = typename List::template push_back<U>;
using type = std::conditional_t<
count == 0,
List,
typename copy_pop_<typename next::type, Us...>::type
>;
};
using type = copy_pop_<list<>, Ts...>::type;
};
struct pop_front {
static_assert( size > 0, "List is empty, cannot pop_back." );
template<typename U, typename...Us> struct copy_after_front_ {
using type = list<Us...>;
};
using type = copy_after_front_<Ts...>::type;
};
template<typename Other> struct cast {};
template<template<typename...> typename Other> struct cast<Other<>> {
using type = Other<Ts...>;
};
template<typename T>
struct holds_type {
static constexpr bool value = std::disjunction_v<std::is_same<T, Ts>...>;
};
template<std::size_t idx_>
struct get_member {
static_assert( idx_ < sizeof...( Ts ), "Index out of range." );
template<std::size_t count, typename...Us> struct helper {};
template<std::size_t count, typename U, typename...Us> struct helper<count,U,Us...> {
using type = std::conditional_t<
count == idx_,
U,
typename helper<count+1, Us...>::type
>;
};
template<std::size_t count, typename U>
struct helper<count, U> {
using type = std::conditional_t<count == idx_, U, void>;
};
using type = helper<0, Ts...>::type;
};
};
- Check for empty
- Get the size of the list
- Get the front or first element in the parameter pack
- Extend the list using push_front/push_back
- Shrink the list using pop_front/pop_back
- Cast list<T...> to another template type that allows multiple types ( std::variant for instance )
- Check if a type is in the parameter pack using holds_type
- Get the type of a parameter using an index
- There are no data objects
- All the functions are structs
Well, I can't leave well enough alone and decided to learn how these templates worked and figure out if there is a way to 'cheat'. Here it is 2022 and I'm back at it.
With this list class, you can describe the vertex you intend to use.
using ColorVertexMembers = list<Vec3, Vec4>;
using TextureVertexMembers = list<Vec3, Vec2>;
The C++ standard library has something a little similar called std::tuple. One thing std::tuple does different is it actually creates storage space for each of the types in it's template parameter list. You even access it's 'members' using indices like my list<> class. What I've done different besides not creating storage is adding functionality to the class itself. Unfortunately, there is a drawback...and it's ugly. Say you wanted to get element 0 from an std::tuple:
Code: Select all
using my_tuple_struct = std::tuple<Vec3, Vec4>;
auto& first_member = std::get<0>( my_tuple_struct );
Code: Select all
using my_list_struct = list<Vec3, Vec4>;
using first_member_type = typename my_list_struct:: template get_member<0>::type;
Code: Select all
template<std::size_t idx_, typename List>
using list_get_member_t = typename List::template get_member<idx_>::type;
Code: Select all
using first_member_type = list_get_member_t<0, my_list_struct>;
So what's the point? This just tells me the type of each member in some hypothetical struct. Well, I made a companion class that utilizes this list class.
Out of fear something goes wrong, I'm going to break up posts.