vendredi 27 octobre 2017

Recursive template argument removals

This seemingly strange question is meant to tackle a general type of recursion that I cannot seem to figure out. remove_unit_packs<Pack>::type shall be the Pack with all unit packs within it removed, where a unit pack is defined to be any pack of the form P<T> (only one element in the pack, though that element can itself be a pack too). Thus

 `remove_unit_packs< std::tuple<int, P<float>, char> >::type`

shall be

 `std::tuple<int, char>`

Here is my implementation for this, which uses recursion:

#include <type_traits>
#include <tuple>

template <typename T>
struct is_unit_pack : std::false_type { };

template <template <typename> class P, typename T>
struct is_unit_pack<P<T>> : std::true_type { };

template <typename T, typename Output = std::tuple<>>
struct remove_unit_packs { using type = T; };

template <template <typename...> class P, typename... Output>
struct remove_unit_packs<P<>, std::tuple<Output...>> {
    using type = P<Output...>;
};

template <template <typename...> class P, typename First, typename... Rest, typename... Output>
struct remove_unit_packs<P<First, Rest...>, std::tuple<Output...>> : std::conditional_t<is_unit_pack<First>::value,
    remove_unit_packs<P<Rest...>, std::tuple<Output...>>,
    remove_unit_packs<P<Rest...>, std::tuple<Output..., typename remove_unit_packs<First>::type>>  // We use 'typename remove_unit_packs<First>::type>' instead of simply 'First' in case 'First' contains a unit pack despite not being a unit pack (in which case all unit packs must be removed from within 'First').
> { };

// Test
template <typename...> struct P;

int main() {
    static_assert(std::is_same<
        remove_unit_packs< std::tuple<int, P<float>, char> >::type,
        std::tuple<int, char>
    >::value);
}

Note, however, that

 `remove_unit_packs< std::tuple<int, P<float, P<bool>>, char> >::type`

is

 `std::tuple<int, P<float>, char>`

But this output type also contains a single pack (P<float>), which I would like it to be removed as well. But my code above doesn't do this, because it does only one pass of removals. I could call two passes to get the job done, but then there will be cases that will require who knows how many passes to remove all unit packs completely. But I'm stuck figuring out how to do this. Can anyone shed some ideas here, which hopefully addresses how this type of thing is done in general?

Aucun commentaire:

Enregistrer un commentaire