Search code examples
c++templatesmetaprogrammingtypedeftemplate-meta-programming

Dealing with inconsistent typedefs in generic code


I routinely come across code in large codebases that do not follow the standard convention for typedefs e.g. ThisType instead of this_type.

Writing generic code where I can no longer rely on this_type means I have to provide some scaffolding code for each type that does not have this_type.

I suppose both this_type and ThisType can be defined. However, in a large codebase that adds extra noise and is something that reviews will need to routinely check.

Is there a way to wrap it in a type_trait such that I can write something along the lines of: this_type<SomeType>::value_type OR some other generic solution?


Solution

  • Maybe can be done in a simpler way... anyway, I propose a tag dispatching / SFINAE solution.

    First of all, a simple recursive tag struct

    template <std::size_t N>
    struct tag : public tag<N-1u>
    { };
    
    template <>
    struct tag<0u>
    { };
    

    to avoid ambiguities in cases more that one of the possible type names are defined.

    Then a template function (only declared) for every type you want extract from the possible types; one for type

    template <typename T, std::void_t<typename T::type>* = nullptr>
    typename T::type getType (tag<0u>);
    

    one for this_type

    template <typename T, std::void_t<typename T::this_type>* = nullptr>
    typename T::this_type getType (tag<1u>);
    

    one for ThisType

    template <typename T, std::void_t<typename T::ThisType>* = nullptr>
    typename T::ThisType getType (tag<2u>);
    

    and one (to be a little silly) for MySillyTypeName

    template <typename T, std::void_t<typename T::MySillyTypeName>* = nullptr>
    typename T::MySillyTypeName getType (tag<3u>);
    

    Observe that the number of the tag are differents: this avoid the possible ambiguity and give a priority order for the names.

    Now a trivial struct that uses getType() to extract the required type

    template <typename T, typename U = decltype(getType<T>(tag<100u>()))>
    struct GetType { using type = U; };
    

    The following is a full compiling C++17 example

    #include <type_traits>
    
    template <std::size_t N>
    struct tag : public tag<N-1u>
    { };
    
    template <>
    struct tag<0u>
    { };
    
    template <typename T, std::void_t<typename T::type>* = nullptr>
    typename T::type getType (tag<0u>);
    
    template <typename T, std::void_t<typename T::this_type>* = nullptr>
    typename T::this_type getType (tag<1u>);
    
    template <typename T, std::void_t<typename T::ThisType>* = nullptr>
    typename T::ThisType getType (tag<2u>);
    
    template <typename T, std::void_t<typename T::MySillyTypeName>* = nullptr>
    typename T::MySillyTypeName getType (tag<3u>);
    
    template <typename T, typename U = decltype(getType<T>(tag<100u>()))>
    struct GetType { using type = U; };
    
    struct foo1 { using type = short; };
    struct foo2 { using this_type = int; };
    struct foo3 { using ThisType = long; };
    struct foo4 { using MySillyTypeName = long long; };
    
    int main()
    {
      static_assert( std::is_same_v<short,     GetType<foo1>::type> );
      static_assert( std::is_same_v<int,       GetType<foo2>::type> );
      static_assert( std::is_same_v<long,      GetType<foo3>::type> );
      static_assert( std::is_same_v<long long, GetType<foo4>::type> );
    }