Martin
Martin

Reputation: 9369

SFINAE on pointers to overloaded functions

Consider the following program. I use h() as an helper to resolve ambiguities with pointers to overloaded functions from cmath:

#include <cmath>

typedef double(*PF1)(double);
typedef double(*PF2)(double, double);
typedef double(*PF3)(double, double, double);
// etc...

auto h(PF1 p) -> decltype(p) {return p;}
auto h(PF2 p) -> decltype(p) {return p;}
auto h(PF3 p) -> decltype(p) {return p;}

int f(int) {return 0;}; // my math function

int main() {
    //auto s_ = std::sin; // won't work as std::sin is overloaded
    auto s = h(std::sin); // works, type of s is a double(*)(double)
    auto p = h(std::pow); // OK.
    // auto my_aim = h(f); // my aim is to make h() work with f too
}

Is there a smarter or more generic helper to deduct the type of a pointer to (a possibily) overloaded function given a pointer to the function itself, so that the deduction would "prefer" either the overload with only double types involved (as return type and arguments) if available, or one of other overloads otherwise.

Upvotes: 4

Views: 225

Answers (3)

Jarod42
Jarod42

Reputation: 218088

The following may help:

constexpr auto h(double(*p)(double)) -> decltype(p) {return p;}
constexpr auto h(double(*p)(double, double)) -> decltype(p) {return p;}
constexpr auto h(double(*p)(double, double, double)) -> decltype(p) {return p;}

template <typename Return, typename ... Args>
constexpr auto h(Return (*p)(Args...)) -> decltype(p) {return p;}

int f(int) {return 0;}; // my math function

int main(int argc, char *argv[])
{
    //auto s_ = std::sin; // won't work as std::sin is overloaded
    auto s = h(std::sin); // works, type of s is a double(*)(double)
    auto p = h(std::pow); // OK.
    auto my_aim = h(f); // works too

    return 0;
}

As long as h arguments is in one provided (double(*p)(double..)) or there is no overload (as for f) (so template can deduce its type).

EDIT

Add a more generic class to handle that:

template<typename Sign, typename ... Signs>
struct identity : identity<Signs...>
{
    using identity<Signs...>::h;
    static constexpr auto h(Sign p) -> decltype(p) {return p;}
};

template<typename Sign>
struct identity<Sign>
{
    static constexpr auto h(Sign p) -> decltype(p) {return p;}

    template <typename T>
    static constexpr auto h(T p) -> decltype(p) {return p;}
};

Let's use it with your example:

typedef identity<double(*)(double),
                 double(*)(double, double),
                 double(*)(double, double, double)> MyIdentity;

int f(int) {return 0;}; // my math function

int main(int argc, char *argv[])
{
    auto s = MyIdentity::h(std::sin); // works : double(*)(double)
    auto p = MyIdentity::h(pow);      // works : double(*)(double, double)
    auto my_aim = MyIdentity::h(f);   // works : (int)(*)(int)

    return 0;
}

Upvotes: 3

Manu343726
Manu343726

Reputation: 14184

The Boost libraries have the Boost.Functional/OverloadedFunction library, which is dessigned to wrap different overloads into a single function object:

boost::overloaded_function<
      const std::string& (const std::string&)
    , int (int)
    , double (double)
> identity(identity_s, identity_i, identity_d);

// All calls via single `identity` function.
BOOST_TEST(identity("abc") == "abc");
BOOST_TEST(identity(123) == 123);
BOOST_TEST(identity(1.23) == 1.23);

I think your problem could be solved easily with this library. Even if this solution does not answer your question directly, I think using boost::overloaded_function is a better way to bind different functions into one object and calling that different functions through one object only, which is your final goal, isn't? (I have deduced that following your previous question).

Upvotes: 0

Trevor Hickey
Trevor Hickey

Reputation: 37914

This is the closest thing I can get to what you are trying to do:

#include <cmath>
#include <functional>

template<typename R, typename ...A>
auto h(R f(A...)) -> std::function<R(A...)>
{
    return std::function<R(A...)>(f);
}

int main() {

    auto s = h<double, double>(std::sin);
    auto p = h<double, double, double>(std::pow);
}

You still have to specify the return type and parameters of the function you are passing in, but that is because it would be ambiguous without specifying an overload.

if you had a function whose deduced parameters would be unambiguous, it would simply be:

int only_one_overload(int a,  int b) {
    return a + b;
}
auto x = h(only_one_overload);

I would recomend just using function pointers though:

double (*s)(double) = std::sin;
double (*p)(double, double) = std::pow;

Upvotes: 0

Related Questions