Search code examples
c++c++20template-meta-programming

Type chooser trait


I need a type_chooser trait that would look like

using chosen_type = type_chooser<bool1, T1, bool2, T2, ....., boolN, TN, Telse>;

and would work like

if      (bool1) return T1;
else if (bool2) return T2;
...
else if (boolN) return TN;
else            return Telse;

How can this be implemented (c++20)?

It would be nice if it could accept even number of arguments as well, in which case it would work like:

if      (bool1) return T1;
else if (bool2) return T2;
...
else if (boolN) return TN;
else            COMPILE_TIME_ERROR;

Solution

  • Mixed non-type/type parameters like that just aren't available for an arbitrary number of arguments.

    You can try changing the syntax. Here are some options:

    // One argument for the bool and the type
    template<bool B, typename T>
    struct Case;
    template<typename T>
    struct Else;
    
    using chosen_type = type_chooser<Case<bool1, T1>, Case<bool2, T2>, ....., Case<boolN, TN>, Else<Telse>>;
    
    // Maybe written as
    using chosen_type = first_with_member_type<std::enable_if<bool1, T1>, std::enable_if<bool2, T2>, ..., std::enable_if<boolN, TN>, std::type_identity<TElse>>;
    
    // Type-only arguments
    using chosen_type = type_chooser<std::bool_constant<bool1>, T1, std::bool_constant<bool2>, T2, ..., std::bool_constant<boolN, TN>, Telse>;
    
    // Or non-type-only arguments
    using chosen_type = type_chooser<bool1, std::type_identity<T1>{}, bool2, std::type_identity<T2>{}, ..., boolN, std::type_identity<TN>{}, std::type_identity<TElse>{}>;
    
    // Move all the bools to a single parameter
    using chosen_type = type_chooser<std::integer_sequence<bool,
        bool1, bool2, bool3, ..., boolN
    >,     T1,    T2,    T3, ...,    TN, TElse
    >;
    
    // Write out your if/else chain
    using chosen_type = decltype([]{
      if constexpr      (bool1) return std::type_identity<T1>{};
      else if constexpr (bool2) return std::type_identity<T2>{};
      ...
      else if constexpr (boolN) return std::type_identity<TN>{};
      else                      return std::type_identity<Telse>{};
    }())::type;
    
    // If you have a known max number of types
    template<
        bool B1 = false, typename T1 = void, bool B2 = false, typename T2 = void,
        bool B3 = false, typename T3 = void, ...,
        bool BN = false, typename TN = void, bool B(N+1) = false, typename T(N+1) = void
    >
    struct type_chooser;
    
    using chosen_type = type_chooser<bool1, T1, bool2, T2, ..., boolN, TN, true, TElse>;