Chef Gladiator
Chef Gladiator

Reputation: 1008

How to create a function pointer having a function signature, possibly at compile time

I do know about this.

But, I do not have two functions to compare, I have a function signature and a function pointer

template<typename T, typename ... A>
using RequiredSignature = bool(T&, A ... a);

template<typename T, typename ... A>
using RequiredFP = bool(*)(T&, A ... a);

Question: How do I make sure function pointer, I have received does confirm to the required signature? At compile time.

Upvotes: 1

Views: 179

Answers (4)

Chef Gladiator
Chef Gladiator

Reputation: 1008

3

Vote me down if you have too, but here is answer number 3 :) I might remove one of the previous two but having them all 3 + Bens concept & answer, gives nice chronological view.

As in my second answer, I am using std::common_type. This variant is just more modern and cleaner.

    template< class, class = void_t<> >
struct
    has_type : false_type { };

template< class T >
struct
    has_type<T, void_t<decltype(declval<T::type>())>> : true_type { };

template<typename SIG, typename FP >
constexpr inline bool signature_fp_match(FP)
{
    using c_t = common_type< SIG, FP >;

    return has_type<c_t>{}();
}

Here is the usage

    template<typename T, typename ... A>
      using RequiredSignature = bool(T&, A ... a);

bool ok_fun(int& ) { return true; }

static_assert( signature_fp_match< RequiredSignature<int> >(ok_fun) );

Or the less snazzy variation:

static_assert( signature_fp_match< bool(int&) >(ok_fun) );

For a more generic utility rename signature_fp_match to have_common_type or whatever you fancy.

Upvotes: 0

Chef Gladiator
Chef Gladiator

Reputation: 1008

1

The solution is (of course) to 1. define and 2. initialize the function pointer from the given function signature.

    using required_sig = RequiredSignature<int>;
    /*
      actually define and initialize the required FP
     */
    required_sig* rfp{};

    if (inner::fp_matches_fp(rfp, ok_fun))
    {
        // ... MATCH ...
    }

Too obvious really, thanks Ben Voight.

Upvotes: 0

Chef Gladiator
Chef Gladiator

Reputation: 1008

2

I am adding this as "another" answer so that each one is clearer.

    // https://stackoverflow.com/a/18682805/10870835
template<typename T>
class has_type
{
    typedef struct { char c[1]; } yes;
    typedef struct { char c[2]; } no;

    template<typename U> static constexpr yes test(typename U::type);
    template<typename U> static constexpr no  test(...);

public:
    static constexpr bool result = sizeof(test<T>(nullptr)) == sizeof(yes);
};

template<typename SIG, typename FP >
constexpr bool signature_fp_match(FP)
{
    using c_t = common_type< SIG, FP >;
    if (has_type<c_t>::result) return true;
    return false;
}

// usage
 template<typename T, typename ... A>
using RequiredSignature = bool(T&, A ... a);

  bool ok_fun(int& ) { return true; }

  static_assert( signature_fp_match<RequiredSignature<int> >(ok_fun) ) ;

It is just, I thought the previous one is (much) simpler.

Upvotes: 0

Ben Voigt
Ben Voigt

Reputation: 283614

The better solution is not to use template parameter deduction plus a dummy parameter, when you mean to control the type. Just pass the type to the template parameter that wants it...

template<typename T, typename U>
constexpr inline bool fp_matches_fp(U y)
{
    T* x{};
    return (sizeof(is_same_helper(x, y))) == (sizeof(yes));
}

You use it like

fp_matches_fp<required_sig>(ok_fun)

The caller doesn't have to create a dummy function pointer, the type trait takes care of it. But we don't need the dummy function pointer at all...

template<typename T>
yes& is_compatible_helper(T*);  //no need to define it now!

template<typename T>
no& is_compatible_helper(...); //no definition needed!

template<typename T, typename U>
constexpr inline bool fp_matches_fp(U y)
{
    return (sizeof(is_compatible_helper<T>(y))) == (sizeof(yes));
}

Upvotes: 1

Related Questions