I would like to have a reference to the call of an object's method. Is this possible in C ? What is the technical name I should be searching for? Can we supply some arguments with predetermined values?
The following code highlights what I would like to use
struct Foo {
void barNoArgs();
void barMultArgs(int, float, bool);
};
Foo f;
auto& refToBarNoArgs = f.barNoArgs;
refToBarNoArgs(); // should call f.barNoArgs()
auto& refToBarMultArgs = f.barMultArgs(?, 3.14f, ?);
refToBarNoArgs(42, true); // should call f.barMultArgs(42, 3.14f, true)
CodePudding user response:
What is the technical name I should be searching for?
This is called Argument Binding, or sometimes a Partial Function
But first, you should know that both of your examples are the exact same problem. Methods are essentially functions with a hidden first parameter called this
after all.
So refToBarNoArgs
is a function taking 0 arguments that calls Foo::barNoArgs
with the first argument pre-populated. And refToBarNoArgs
is a function that takes 2 arguments, and calls Foo::barMultArgs
with the first and third arguments pre-populated, and the second and fourth with its own arguments.
The key point here is that your "reference" can't be a reference in the C sense of the term. References don't actually exist (as in there is no object associated with the reference itself). Here, in both cases, we need to store the values of the pre-populated arguments, as well as manage their lifetimes. It has to be an actual object, with storage and lifetime, that behaves like a function. This is called a Functor.
The language provides us with a convenient bit of syntactic sugar to facilitate creating both the type and a single instance of such a functor all at once: Lambdas.
auto refToBarNoArgs = [&f](){f.barNoArgs();};
refToBarNoArgs();
auto refToBarMultArgs = [&f](int i, bool b){f.barMultArgs(i, 3.14f, b);};
refToBarNoArgs(42, true);
If you are not familiar with lambdas, be wary of the [&f]
in my examples. It means that the lambda captures f
by reference. Because of this, they should not be allowed to live (for example, stored in an std::function<>
) longer than the lifetime of f
.
You can also use std::bind
to get effectively the same thing. In fact, the end result is very close to your original code:
#include <functional>
struct Foo {
void barNoArgs();
void barMultArgs(int, float, bool);
};
int main() {
using namespace std::placeholders; // for _1, _2, _3...
Foo f;
auto refToBarNoArgs = std::bind(&Foo::barNoArgs, &f);
refToBarNoArgs();
auto refToBarMultArgs = std::bind(&Foo::barMultArgs, &f, _1, 3.14f, _2);
refToBarNoArgs(42, true);
}
Personally, I find lambdas much clearer.
CodePudding user response:
Technically you can get a pointer to a member function (member function pointer) and then call the function for an object.
struct Foo {
void barNoArgs();
void barMultArgs(int, float, bool);
};
void foo() {
Foo f;
auto refToBarNoArgs = &Foo::barNoArgs;
(f.*refToBarNoArgs)();
}
Fixing (binding) parameters to create a new function which you can then call with the missing arguments is a bit more challenging. The easiest way IMHO is to use lambda functions to create an unnamed function wrapping the member and capturing the object. An alternative is to use std::bind
.
#include <functional> // for std::bind
struct Foo {
void barNoArgs();
void barMultArgs(int, float, bool);
};
void foo() {
using namespace std;
using namespace std::placeholders;
Foo f;
auto b = bind( &Foo::barMultArgs, f, _1, 3.14f, _2);
auto l = [&f](auto const& x, auto const& y) {return f.barMultArgs(x,3.14f,y);};
b(42, true); // should call f.barMultArgs(42, 3.14f, true)
l(42, true); // should call f.barMultArgs(42, 3.14f, true)
}