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.
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.
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.
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.
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.
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.
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).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With