Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile time type detection of parameter types

I do know several methods to detect whether a given class has a function with a specified signature. What I would like though is to deduce the signature at compile-time. Consider:

struct test_class
{
    void test( int a );
    void test( float b );
};

I can use decltype and SFINAE to detect the presence of a specified test() with simple syntax like has_test<test_class,int>();. What I would want however is something like test_types<test_class>::types -> mpl::list< int, float >. Anyone has a sane idea how to do that? The requirement is that the list of detectable types cannot be prepared (so it'll detect any test( T ), not just the ones we "register".

like image 313
Kornel Kisielewicz Avatar asked Oct 18 '22 00:10

Kornel Kisielewicz


1 Answers

If you can afford decorating test() overloads in a way equivalent to (it's ugly I know, maybe you can come out with something prettier):

struct test_class
{
    param<int> test( int a, param_id<0> ={} );
    param<float> test( float a, param_id<1> ={} );
};

then something like this should work (godbolt conformance view):

template<typename T> struct param{ using type = T; };
template<int I> struct param_id{};
template<typename... T> struct type_list{};

struct anything{ template<typename T> operator T&&(); };

template<int I>
struct matcher
{
  template<typename T, typename E = std::enable_if_t<std::is_same<T,param_id<I>>::value> >
  operator T();
};

template<typename T,int I,typename = std::void_t<>,typename... Ts>
struct test_types_impl{ using type = type_list<Ts...>; };

template<typename T,int I,typename... Ts>
struct test_types_impl<T,I,std::void_t<decltype(std::declval<T>().test( anything{}, matcher<I>{} ))>,Ts...>:
  test_types_impl<T,I+1,void,Ts...,typename decltype(std::declval<T>().test( anything{}, matcher<I>{} ))::type>
{
};

template<typename T>
struct test_types{ using type = typename test_types_impl<T,0>::type; };

struct test_class
{
    param<int> test( int a, param_id<0> ={} );
    param<float> test( float a, param_id<1> ={} );
};

static_assert( std::is_same_v<test_types<test_class>::type, type_list<int,float>> );

the above requires at least move-constructible argument types and C++17 (but it I think it can be made to work in C++11 as well, and with any type).

param_id may be omitted if you manage to get a total ordering on the set of allowed parameter types. Maybe, we can even omit the param<T> somehow, not sure though (waiting OP feedback for this :))

like image 117
Massimiliano Janes Avatar answered Oct 21 '22 00:10

Massimiliano Janes