Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function taking pointer-to-method regardless of constness

I want to implement a generic function that will take reference to object and pointer to its member function and invoke it. However I'm not able to do so when my class has both const and non-const methods as I need to provide two overloads:

template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...))
{
    return (object.*method)(Us{}...);
}

template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...) const)
{
    return (object.*method)(Us{}...);
}

Is there some way to write only 1 template function that will accept both const and non-const method pointers so I don't have to write my code twice? I'm using C++14.

For a broader picture, what I want to ultimately achieve is pass a 3rd parameter, a data buffer from which method arguments will be extracted - hence the template function to handle it as generically as possible.

like image 936
flamasterrr Avatar asked Aug 19 '20 10:08

flamasterrr


People also ask

How to pass a pointer to a function in C?

Passing pointers to functions in C. C programming allows passing a pointer to a function. To do so, simply declare the function parameter as a pointer type.

Why are pointers to const values always on the far left?

Just remember that the type of value the pointer points to is always on the far left: Pointers to const values are primarily used in function parameters (for example, when passing an array to a function) to help ensure the function doesn’t inadvertently change the passed in argument. We will discuss this further in the section on functions.

What are the differences between function pointers and normal pointers?

Typically a function pointer stores the start of executable code. 2) Unlike normal pointers, we do not allocate de-allocate memory using function pointers. 3) A function’s name can also be used to get functions’ address. For example, in the below program, we have removed address operator ‘&’ in assignment.

What are pointers to const values in JavaScript?

Pointers to const values are primarily used in function parameters (for example, when passing an array to a function) to help ensure the function doesn’t inadvertently change the passed in argument. We will discuss this further in the section on functions.


Video Answer


3 Answers

The short answer is, don't implement this yourself, it has already been done for you in the form of std::invoke:

#include <functional>

struct A {
    void foo(int x);
    void bar(int x) const;
};

void example() {
    A a;
    std::invoke(&A::foo, a, 3); 
    std::invoke(&A::bar, a, 3); 
}

Seeing that you have added a C++14 tag in retrospect, the documentation of std::invoke has a sample implementation which you can use in your project.

like image 112
Jan Schultke Avatar answered Nov 07 '22 04:11

Jan Schultke


Here's a C++14 alternative without using std::function.

what I want to ultimately achieve is pass a 3rd parameter, a data buffer from which method arguments will be extracted - hence the template function to handle it as generically as possible

What you use at the call site will be perfectly forwarded here:

template<typename Class, typename Func, typename... Args>
decltype(auto) callMethod_impl(Class& object, Func method, Args&&... args) {
    return (object.*method)(std::forward<Args>(args)...);
}


template<typename Ret, typename Class, typename... Us, typename... Args>
Ret callMethod(Class& object, Ret(Class::*method)(Us...), Args&&... args) {
    return callMethod_impl(object, method, std::forward<Args>(args)...);
}

template<typename Ret, typename Class, typename... Us, typename... Args>
Ret callMethod(const Class& object, Ret(Class::*method)(Us...) const, Args&&... args) {
    return callMethod_impl(object, method, std::forward<Args>(args)...);
}

Demo

If you need Ret in callMethod_impl, just add it as a template parameter and call it like callMethod_impl<Ret>(...) from the callMethod overloads (Demo).

like image 29
Ted Lyngmo Avatar answered Nov 07 '22 06:11

Ted Lyngmo


Without std::invoke, you can only do some more or less generic workarounds, based on type erasure. Using std::function allows you to create a proxy-function to treat your functions on an equal footing:

#include <iostream>
#include <functional>

template<typename Ret, typename ...Us>
Ret callMethod_impl(std::function<Ret(Us...)> f) {
    // common implementation here
    return f(Us{}...);
}

template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...)) {
    return callMethod_impl(std::function<Ret(Us...)>(std::bind(method, object)));
}

template<typename Ret, typename Class, typename ...Us>
Ret callMethod(const Class &object, Ret (Class::*method)(Us...) const) {
    return callMethod_impl(std::function<Ret(Us...)>(std::bind(method, object)));
}

struct Foo {
    int bar() const { return 1; }
    float bar() { return 2.1f; };
};

int main() {
    Foo f;
    std::cout << callMethod(f, &Foo::bar) << std::endl;
}

Note that volatile functions (+ combinations) are not treated here, but maybe you don't need full generality but rather a pragmatic solution.

like image 28
Jodocus Avatar answered Nov 07 '22 04:11

Jodocus