How to get the type of function pointer of templated function from invocation

Viewed 112

Let's imagine I have some complicated templated function and I want to test that it works as expected. In particular that when invoked with certain arguments that type of function is determined only by first argument. For example:

template<typename T>
bool less(const T& x, const std::type_identity_t<T>& y){
    return x < y;
}

Here I would like to check something (with static_assert or some test framework expect) that only first argument determines the signature of the function, something like:

  std::is_same_v<decltype(less(short{1}, long{2})) , decltype(less(short{1}, double{2}))>;

But obviously this does not work since decltype will give me the result of function invocation, not the type of the function that is instantiated when I give it arguments I gave it.

Is there a way to do this(assume I can not modify the function, e.g. make it a functor with typedefed T or change it any other way)?

I tried searching for this, failed, I guess somebody asked this already but I could not find the question.

note: I know about testing behaviors, not implementation. Example is just tiny example, not something realistic.

2 Answers

This isn't a full answer, but something like this should_convert_to might work to test the less function:

#include <type_traits>

template <typename expected>
struct should_convert_to{
    template <typename actual>
    operator actual() {
        static_assert(std::is_same_v<expected, actual>);
        return {};
    }
};
auto result = less(int{1}, should_convert_to<int>());
static_assert(std::is_same_v<decltype(result), bool>);

// error: static_assert failed due to requirement 'std::is_same_v<short, int>'
// less(int{1}, should_convert_to<short>()); 

You could use a wrapper class that gives you the signature:

// ...

template <typename, typename, typename = void>
struct less_fptr;

template <typename X, typename Y>
struct less_fptr<X, Y, std::enable_if_t<!std::is_convertible_v<Y, X>>> {
    using type = less_fptr<X, Y>;
};

template <typename X, typename Y>
struct less_fptr<X, Y, std::enable_if_t<std::is_convertible_v<Y, X>>> {
    using type = bool(*)(const X&, const std::type_identity_t<X>&);
};

// ...

static_assert(std::is_same_v<typename less_fptr<decltype(short{1}), decltype(long{2})>::type, typename less_fptr<decltype(short{1}), decltype(double{2})>::type>);
Related