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?
CodePudding user response:
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> );
}