Added etl::type_list_remove, etl::type_list_remove_if, etl::type_list_unique, etl::type_list_pop_front, etl::type_list_pop_back

This commit is contained in:
John Wellbelove 2026-02-09 10:56:23 +00:00
parent f25e6b9d07
commit 43972b416e
2 changed files with 343 additions and 6 deletions

View File

@ -79,6 +79,7 @@ namespace etl
private:
// A type_list cannot be instantiated, so delete the constructor and assignment operators.
type_list() ETL_DELETE;
type_list(const type_list&) ETL_DELETE;
type_list& operator =(const type_list&) ETL_DELETE;
@ -109,6 +110,7 @@ namespace etl
private:
// A type_list cannot be instantiated, so delete the constructor and assignment operators.
type_list() ETL_DELETE;
type_list(const type_list&) ETL_DELETE;
type_list& operator =(const type_list&) ETL_DELETE;
@ -418,10 +420,10 @@ namespace etl
private:
ETL_STATIC_ASSERT((etl::is_type_list<TTypeList>::value), "TTypeList must be an etl::type_list");
ETL_STATIC_ASSERT(Index <= etl::TTypeList::size, "Index out of range");
ETL_STATIC_ASSERT(Index <= TTypeList::size, "Index out of range");
using index_sequence_for_prefix = etl::make_index_sequence<Index>;
using index_sequence_for_suffix = etl::make_index_sequence_with_offset<Index, etl::TTypeList::size - Index>;
using index_sequence_for_suffix = etl::make_index_sequence_with_offset<Index, TTypeList::size - Index>;
using prefix = etl::type_list_select_from_index_sequence_t<TTypeList, index_sequence_for_prefix>;
using suffix = etl::type_list_select_from_index_sequence_t<TTypeList, index_sequence_for_suffix>;
@ -437,6 +439,173 @@ namespace etl
using type_list_insert_t = typename etl::type_list_insert<TTypeList, T, Index>::type;
#endif
//***************************************************************************
/// Remove a type at an index in a type_list.
//***************************************************************************
template <typename TTypeList, size_t Index>
struct type_list_remove
{
private:
ETL_STATIC_ASSERT((etl::is_type_list<TTypeList>::value), "TTypeList must be an etl::type_list");
ETL_STATIC_ASSERT(Index < TTypeList::size, "Index out of range");
using index_sequence_for_prefix = etl::make_index_sequence<Index>;
using index_sequence_for_suffix = etl::make_index_sequence_with_offset<Index + 1, TTypeList::size - Index - 1>;
using prefix = etl::type_list_select_from_index_sequence_t<TTypeList, index_sequence_for_prefix>;
using suffix = etl::type_list_select_from_index_sequence_t<TTypeList, index_sequence_for_suffix>;
public:
// Concatenate the prefix and suffix to create the new type list with the Index element removed.
using type = etl::type_list_cat_t<prefix, suffix>;
};
#if ETL_USING_CPP11
template <typename TTypeList, size_t Index>
using type_list_remove_t = typename etl::type_list_remove<TTypeList, Index>::type;
#endif
//***************************************************************************
// Remove types that satisfy a predicate from a type_list.
//***************************************************************************
namespace private_type_list
{
template <typename TTypeList, template <typename> class TPredicate>
struct type_list_remove_if_impl;
template <template <typename> class TPredicate>
struct type_list_remove_if_impl<etl::type_list<>, TPredicate>
{
using type = etl::type_list<>;
};
template <typename Head, typename... Tail, template <typename> class TPredicate>
struct type_list_remove_if_impl<etl::type_list<Head, Tail...>, TPredicate>
{
private:
using rest = typename type_list_remove_if_impl<etl::type_list<Tail...>, TPredicate>::type;
public:
using type = typename etl::conditional<TPredicate<Head>::value,
rest,
etl::type_list_prepend_t<rest, Head>>::type;
};
}
//***************************************************************************
/// Remove types that satisfy a predicate from a type_list.
/// Predicate must be: template <typename T> struct Pred : etl::bool_constant<...> {};
//***************************************************************************
template <typename TTypeList, template <typename> class TPredicate>
struct type_list_remove_if
{
private:
ETL_STATIC_ASSERT((etl::is_type_list<TTypeList>::value), "TTypeList must be an etl::type_list");
public:
using type = typename private_type_list::type_list_remove_if_impl<TTypeList, TPredicate>::type;
};
#if ETL_USING_CPP11
template <typename TTypeList, template <typename> class TPredicate>
using type_list_remove_if_t = typename etl::type_list_remove_if<TTypeList, TPredicate>::type;
#endif
//***************************************************************************
/// Removes the first type from a type_list.
//***************************************************************************
template <typename TTypeList>
struct type_list_pop_front
{
private:
ETL_STATIC_ASSERT((etl::is_type_list<TTypeList>::value), "TTypeList must be an etl::type_list");
ETL_STATIC_ASSERT(TTypeList::size > 0U, "Cannot pop_front from an empty type_list");
public:
using type = typename TTypeList::tail;
};
#if ETL_USING_CPP11
template <typename TTypeList>
using type_list_pop_front_t = typename etl::type_list_pop_front<TTypeList>::type;
#endif
//***************************************************************************
/// Removes the last type from a type_list.
//***************************************************************************
template <typename TTypeList>
struct type_list_pop_back
{
private:
ETL_STATIC_ASSERT((etl::is_type_list<TTypeList>::value), "TTypeList must be an etl::type_list");
ETL_STATIC_ASSERT(TTypeList::size > 0U, "Cannot pop_back from an empty type_list");
public:
using type = typename etl::type_list_remove<TTypeList, TTypeList::size - 1U>::type;
};
#if ETL_USING_CPP11
template <typename TTypeList>
using type_list_pop_back_t = typename etl::type_list_pop_back<TTypeList>::type;
#endif
//***************************************************************************
// Remove duplicate types from a type_list, preserving the first occurrence.
//***************************************************************************
namespace private_type_list
{
template <typename TTypeList, typename TResult>
struct type_list_unique_impl;
// Base case: empty list, return the result.
template <typename TResult>
struct type_list_unique_impl<etl::type_list<>, TResult>
{
using type = TResult;
};
// Recursive case: Check if the head is already in the result, if not add it, then recurse on the tail.
template <typename Head, typename... Tail, typename TResult>
struct type_list_unique_impl<etl::type_list<Head, Tail...>, TResult>
{
private:
using next_result = etl::conditional_t<etl::type_list_contains<TResult, Head>::value,
TResult,
etl::type_list_append_t<TResult, Head>>;
public:
using type = typename type_list_unique_impl<etl::type_list<Tail...>, next_result>::type;
};
}
//***************************************************************************
/// Defines a new type_list by removing duplicate types from a given type_list, preserving the first occurrence.
//***************************************************************************
template <typename TTypeList>
struct type_list_unique
{
ETL_STATIC_ASSERT((etl::is_type_list<TTypeList>::value), "TTypeList must be an etl::type_list");
using type = typename private_type_list::type_list_unique_impl<TTypeList, etl::type_list<>>::type;
};
#if ETL_USING_CPP11
template <typename TTypeList>
using type_list_unique_t = typename etl::type_list_unique<TTypeList>::type;
#endif
//***************************************************************************
/// Checks that two type lists are convertible.
/// Static asserts if the type lists are not the same length.

View File

@ -40,6 +40,11 @@ namespace
struct B { static constexpr int id = 1; };
struct C { static constexpr int id = 2; };
template <typename T>
struct is_type_b : etl::bool_constant<std::is_same<T, B>::value>
{
};
// Convenience comparator for types that expose a constexpr integral ID (ascending)
template <typename T1, typename T2>
struct by_asencding_id : etl::bool_constant<(T1::id < T2::id)>
@ -376,7 +381,7 @@ namespace
using expected = etl::type_list<A>;
CHECK((etl::is_same<result, expected>::value));
//CHECK_EQUAL(1U, etl::type_list_size<result>::value);
CHECK_EQUAL(1U, etl::type_list_size<result>::value);
}
//*************************************************************************
@ -394,9 +399,172 @@ namespace
CHECK((etl::is_same<result2, expected2>::value));
CHECK((etl::is_same<result3, expected3>::value));
//CHECK_EQUAL(3U, etl::type_list_size<result1>::value);
//CHECK_EQUAL(3U, etl::type_list_size<result2>::value);
//CHECK_EQUAL(3U, etl::type_list_size<result3>::value);
CHECK_EQUAL(3U, etl::type_list_size<result1>::value);
CHECK_EQUAL(3U, etl::type_list_size<result2>::value);
CHECK_EQUAL(3U, etl::type_list_size<result3>::value);
}
//*************************************************************************
TEST(test_type_list_remove_from_empty_list)
{
// Uncomment to generate static_assert error.
//using list = etl::type_list<>;
//using result1 = etl::type_list_remove_t<list, 0>;
}
//*************************************************************************
TEST(test_type_list_remove_from_single_list)
{
using list = etl::type_list<A>;
using result1 = etl::type_list_remove_t<list, 0>;
using expected1 = etl::type_list<>;
CHECK((etl::is_same<result1, expected1>::value));
CHECK_EQUAL(0U, etl::type_list_size<result1>::value);
}
//*************************************************************************
TEST(test_type_list_remove_from_multiple_list)
{
using list = etl::type_list<A, B, C>;
using result1 = etl::type_list_remove_t<list, 0>;
using result2 = etl::type_list_remove_t<list, 1>;
using result3 = etl::type_list_remove_t<list, 2>;
using expected1 = etl::type_list<B, C>;
using expected2 = etl::type_list<A, C>;
using expected3 = etl::type_list<A, B>;
CHECK((etl::is_same<result1, expected1>::value));
CHECK((etl::is_same<result2, expected2>::value));
CHECK((etl::is_same<result3, expected3>::value));
CHECK_EQUAL(2U, etl::type_list_size<result1>::value);
CHECK_EQUAL(2U, etl::type_list_size<result2>::value);
CHECK_EQUAL(2U, etl::type_list_size<result3>::value);
}
//*************************************************************************
TEST(test_type_list_remove_if_from_empty_list)
{
using list = etl::type_list<>;
using result1 = etl::type_list_remove_if_t<list, is_type_b>;
using expected1 = etl::type_list<>;
CHECK((etl::is_same<result1, expected1>::value));
CHECK_EQUAL(0U, etl::type_list_size<result1>::value);
}
//*************************************************************************
TEST(test_type_list_remove_if_from_single_list)
{
using list1 = etl::type_list<A>;
using list2 = etl::type_list<B>;
using result1 = etl::type_list_remove_if_t<list1, is_type_b>;
using result2 = etl::type_list_remove_if_t<list2, is_type_b>;
using expected1 = etl::type_list<A>;
using expected2 = etl::type_list<>;
CHECK((etl::is_same<result1, expected1>::value));
CHECK((etl::is_same<result2, expected2>::value));
CHECK_EQUAL(1U, etl::type_list_size<result1>::value);
CHECK_EQUAL(0U, etl::type_list_size<result2>::value);
}
//*************************************************************************
TEST(test_type_list_remove_if_from_multiple_list)
{
using list = etl::type_list<A, B, C>;
using result1 = etl::type_list_remove_if_t<list, is_type_b>;
using expected1 = etl::type_list<A, C>;
CHECK((etl::is_same<result1, expected1>::value));
CHECK_EQUAL(2U, etl::type_list_size<result1>::value);
}
//*************************************************************************
TEST(test_type_list_pop_front_empty_list)
{
// Uncomment to generate static_assert error.
//using list = etl::type_list<>;
//using result1 = etl::type_list_pop_front_t<list>;
}
//*************************************************************************
TEST(test_type_list_pop_front_from_non_empty_list)
{
using list = etl::type_list<A, B, C>;
using result1 = etl::type_list_pop_front_t<list>;
using result2 = etl::type_list_pop_front_t<result1>;
using result3 = etl::type_list_pop_front_t<result2>;
using expected1 = etl::type_list<B, C>;
using expected2 = etl::type_list<C>;
using expected3 = etl::type_list<>;
CHECK((etl::is_same<result1, expected1>::value));
CHECK((etl::is_same<result2, expected2>::value));
CHECK((etl::is_same<result3, expected3>::value));
CHECK_EQUAL(2U, etl::type_list_size<result1>::value);
CHECK_EQUAL(1U, etl::type_list_size<result2>::value);
CHECK_EQUAL(0U, etl::type_list_size<result3>::value);
}
//*************************************************************************
TEST(test_type_list_pop_back_empty_list)
{
// Uncomment to generate static_assert error.
//using list = etl::type_list<>;
//using result1 = etl::type_list_pop_front_t<list>;
}
//*************************************************************************
TEST(test_type_list_pop_back_from_non_empty_list)
{
using list = etl::type_list<A, B, C>;
using result1 = etl::type_list_pop_back_t<list>;
using result2 = etl::type_list_pop_back_t<result1>;
using result3 = etl::type_list_pop_back_t<result2>;
using expected1 = etl::type_list<A, B>;
using expected2 = etl::type_list<A>;
using expected3 = etl::type_list<>;
CHECK((etl::is_same<result1, expected1>::value));
CHECK((etl::is_same<result2, expected2>::value));
CHECK((etl::is_same<result3, expected3>::value));
CHECK_EQUAL(2U, etl::type_list_size<result1>::value);
CHECK_EQUAL(1U, etl::type_list_size<result2>::value);
CHECK_EQUAL(0U, etl::type_list_size<result3>::value);
}
//*************************************************************************
TEST(test_type_list_unique_for_empty_list)
{
using list = etl::type_list<>;
using result = etl::type_list_unique_t<list>;
using expected = etl::type_list<>;
CHECK((etl::is_same<result, expected>::value));
CHECK_EQUAL(0U, etl::type_list_size<result>::value);
}
//*************************************************************************
TEST(test_type_list_unique_for_non_empty_list)
{
using list = etl::type_list<A, B, A, C, C, B, C>;
using result1 = etl::type_list_unique_t<list>;
using expected1 = etl::type_list<A, B, C>;
CHECK((etl::is_same<result1, expected1>::value));
CHECK_EQUAL(3U, etl::type_list_size<result1>::value);
}
}
#endif