Michael B
Michael B

Reputation: 446

Deducing parameter list of a member function

We want to be able to deduce the argument list of a member function call in a template. This is part of a larger project that performs ultimately a type conversion that allows a target function to be called.

Our research resulted in How to Deduce Argument List from Function Pointer? and that helped a lot.

We are now stuck at finding a similar concept for member functions. The key elements are in the following Source 1 code example:

See the second code block that demonstrates our current research: We know the difference between a function and a member pointer and that a calling a member requires an object identity. But we are not able to express our knowledge in code. The second code block does not compile.

What is the correct notation and template deduction structure to achive the shown notation?

Source 1

#include <iostream>

void free_funct(int p1, double p2) {
    std::cout << "p1=" << p1 << " p2=" << p2 << endl;
}

struct object {
    void object_funct(double p1, int p2) const {
        std::cout << "p1=" << p1 << " p2=" << p2 << endl;
    }
    int exec(double p1, int p2) {
        // Ultimate goal:
        // Wrapper<(???::object_funct> functor3;
        // functor_3(p1, p2;)
    }
};

template <auto F>
class Wrapper {};

template <typename R, typename... Args, auto (F)(Args...)->R>
struct Wrapper<F>
{
    auto operator()(Args... args) const -> R {
        return F(args...);
    }
};

int main() {
    // Free funct.
    Wrapper<free_funct> functor1{};
    functor1(313, 3.141592);
    object object;
    // Extrinsic call to member function.
    object.object_funct(3.1415926, 313);
    // Problem 1:
    Wrapper<&object::object_funct> functor2;
    // How to express this below
    // functor2(3, 1415926, 313);
    object.exec(2.718281, 121);
    return 0;
}

Source 2 (Does not compile)

template <auto F>
struct Wrap;
template <typename T, typename R, typename ... Args, R(T::* F)(Args...)>
struct Wrap<F> {
    auto operator()(Args... args) {
        return F(args...);
    }
};

struct foo {
    int bar(float) { 
        return 1; };
};

int main() {
    foo x;
    Wrap<&x::bar> f;
    f(3.14159265);
    return 0;
}

Upvotes: 0

Views: 57

Answers (1)

RedFog
RedFog

Reputation: 1015

now that you expect Wrapper to wrap a member function pointer by template parameter, you have to know how member function pointer to be called. usually we call a member function pointer using (obj.*mfptr)(args...) or (optr->*mfptr)(args...), it's clearly that the object itself is also necessary for the call operation. so in the Wrapper::operator(), we need not only each of arguments, but also the object.

template <auto F>
struct Wrapper;
template <typename T, typename R, typename ... Args, R(T::* F)(Args...)>
struct Wrapper<F> {
    // it seems you don't take care of the cvref qualifiers, so I won't write all the overloads.
    auto operator()(T obj, Args... args) {
        return (obj.*F)(args...);
    }
    auto operator()(T* obj, Args... args) {
        return (obj->*F)(args...);
    }
};

actually, a member function call is similar (not equal) to a function call which treat the object as the first argument. so you have to call it as following:

Wrapper<&foo::bar> f;
f(foo{}, 3.14);

EDIT: now that you want the instance to be wrapped as well, the Wrapper have to construct with the instance:

template <auto F>
struct Wrapper;
template <typename T, typename R, typename ... Args, R(T::* F)(Args...)>
struct Wrapper<F> {
    // I also omit the overloads of pass by reference semantics.
    Wrapper(T obj) :instance(std::move(obj)){}

    auto operator()(Args... args) {
        return (instance.*F)(args...);
    }
private:
    T instance;
};

int main(){
    foo x;
    Wrapper<&foo::bar> f{x};
    f(3.14);
}

Upvotes: 1

Related Questions