Passing method to method in c++ - c++

I have faced with a problem of passing method to method in C++. What I am trying to do is to reduce amount of code, which is almost the same for several methods.
For example, I have next code:
class F_test {
public:
void f1() {
std::cout << "f1" << std::endl;
}
void f2() {
std::cout << "f2" << std::endl;
}
void foo_main(std::string str, std::function<void(void)> const &f) {
std::cout << "some actions before" << std::endl;
std::cout << str << std::endl;
f();
std::cout << "some actions after" << std::endl;
}
void f1(std::string s1) {
std::function <void(void)> m1 = F_test::f1;
foo_main("processing f1", m1);
}
void f2(std::string s2) {
std::function <void(void)> m2 = F_test::f2;
foo_main("processing f2", m2);
}
};
As for processing f1 and f2 methods I need to perform some the same operations, I have decided to created one method (foo_main) with these operations and pass needed function (f1 or f2) to it, rather than create two separate methods with code duplication.
But, these code is failed on compilation with:
'F_test::f1': function call missing argument list; use '&F_test::f1' to create a pointer to member
'F_test::f2': function call missing argument list; use '&F_test::f2' to create a pointer to member
If to write &F_test::f1 and &F_test::f2, another compilation error is happened:
'void std::_Func_class<_Ret,>::_Set(std::_Func_base<_Ret,> *)' : cannot convert argument 1 from '_Myimpl *' to 'std::_Func_base<_Ret,> *'

Non-static member functions need an object to be called with. You can't just call f1() without an instance of F_test. The compile error results from you trying to do that.
To create a null ary function out of member function, you need to bind an instance to it, whether via std::bind:
std::function <void()> m1 = std::bind(&F_test::f1, this);
or a lambda:
std::function <void()> m1 = [this]{ f1(); };
Note that bind doesn't work in this case since you have overloaded the name f1. It would work in the general case though. The lambda will work regardless.

The simpler would be to use lambda:
F_test test;
test.foo_process("string", [&](){test.f1();});
Else to select (in std::bind) overload f1() whereas other overloads exist (as f1(std::string)), you have to specify which one you want:
static_cast<void (F_test::*)()>(&F_test::f1)

If you don't want to use lambda by some reasons, then you should rewrite the code like:
class F_test {
public:
void f1impl() const {
std::cout << "f1" << std::endl;
}
void f2impl() const {
std::cout << "f2" << std::endl;
}
void foo_process(std::string str, std::function<void(const F_test&)> const &f) {
std::cout << "some actions before" << std::endl;
std::cout << str << std::endl;
f(*this);
std::cout << "some actions after" << std::endl;
}
void f1(std::string s1) {
std::function<void(const F_test&)> f = &F_test::f1impl;
foo_process("processing f1", f);
}
void f2(std::string s2) {
std::function <void(const F_test&)> m2 = &F_test::f2impl;
foo_process("processing f2", m2);
}
};

you can't use a member function pointer to initialize a std::function. Use a lambda instead.
void f1(std::string s1) {
auto m1 = [this](){ f1(); };
foo_main("processing f1", m1);
}

Related

Can I store bound functions in a container?

Consider the following code:
void func_0()
{
std::cout << "Zero parameter function" << std::endl;
}
void func_1(int i)
{
std::cout << "One parameter function [" << i << "]" << std::endl;
}
void func_2(int i, std::string s)
{
std::cout << "One parameter function [" << i << ", " << s << "]" << std::endl;
}
int main()
{
auto f0 = boost::bind(func_0);
auto f1 = boost::bind(func_1,10);
auto f2 = boost::bind(func_2,20,"test");
f0();
f1();
f2();
}
The above code works as intended. Is there any way I can store f0, f1, f2 in a container and execute it like this:
Container cont; // stores all the bound functions.
for(auto func : cont)
{
func();
}
Will this example work for you?
std::vector<std::function<void()>> container{f0, f1, f2};
for (auto& func : container)
{
func();
}
You can read here about std::function:
Instances of std::function can store, copy, and invoke any Callable
target ...
So the template argument for this class template (void() in our case) is merely the signature of the Callable. What bind() returned in all your calls to it is exactly a Callable of the form void().
std::bind is not guaranteed to return the same type for functions with the same final interface (final = after binding). So, no, you won't be able to store functions bound with std::bind in a container. You will have to use some sort of type-erasure technique to bring them all to the same type, like std::function. A container of std::function<void()> will be able to store your bound functions (at the expense of type-erasure-related overhead).
I don't know whether it applies to boost::bind, but I suspect it is the same as std::bind in that regard.

How to call a C++ class method, which is given as a parameter?

I'm trying to pass a method as a parameter to other method.
Magner.h:
Class Manager{
public:
timeCount(void (Manger::*function)(void));
void passedFuction();
}
In Manager.cpp, I'm trying to call timeCount by
timeCount(&Manager::passedFuction());
TimeCount Body:
void Manager::timeCount(void(Manager::*function)(void))
{
std::cout << "It works";
(*function)(); // here is error
}
ViusalStudio says:
void*Manager::*function)() operand of '*' must be a pointer
How should i correct it?
The example i was learing by was : http://www.cplusplus.com/forum/beginner/6596/
A pointer-to-member-function (pmf) is not a pointer. Let me repeat that:
A pointer-to-member-function is not a pointer.
To call a pmf, you have to provide it with the object you want to call it on. You probably want:
(this->*function)();
If you had another object obj of the right type, you could also use:
(obj.*function)();
The void (Manger::*function)(void) syntax is for member functions of Manager class, which cannot be used with functions outside the Manager class.
To fix this shortcoming, pass std::function<void(void)> instead, which would let you invoke itself using the regular function invocation syntax:
void Manager::timeCount(std::function<void(void)> f) {
std::cout << "It works";
f();
}
Here is a complete demo of how to call timeCount with member and non-member functions:
struct Manager {
string name;
void timeCount(std::function<void(void)> f) {
std::cout << "This is " << name << " manager" << endl;
f();
}
};
void foo() {
cout << "I'm foo" << endl;
}
struct Test {
int x;
void bar() {
cout << "I'm bar " << x << endl;
}
};
int main() {
Manager mgr {"time"};
mgr.timeCount(foo);
Test tst = {234};
mgr.timeCount(std::bind( &Test::bar, tst));
return 0;
}
Demo.
Since c++17, we have std::invoke:
std::invoke(function, this);
or
std::invoke(function, *this);
are both ok. Minimal demo:
#include <functional>
#include <iostream>
class Manager
{
public:
void timeCount(void (Manager::*function)(void));
void passedFuction()
{
std::cout << "call passedFunction\n";
}
};
void Manager::timeCount(void (Manager::*function)(void))
{
std::cout << "It works\n";
std::invoke(function, *this);
// (*function)(); // here is error
}
int main()
{
Manager a;
a.timeCount(&Manager::passedFuction);
}
It works
call passedFunction
live demo

Converting a lambda to function pointer typedef

I have a 3rd party API that has a function that takes a function pointer as an argument (sample types):
// defined in "Api.h"
typedef int (*Callback) (int x);
unsigned char ApiCall(int p, Callback cb);
int ApiCall(int p, Callback cb) {
return cb(p);
}
And I'm trying interact with this API using a class instance method; here's a sample class:
class ApiWrapper {
public:
int ApiCallback(int x) { return this->factor_ * x; }
static int WorkingApiCallback(int x) { return 3 * x; }
ApiWrapper(int f) { this->factor_ = f; }
private:
int factor_;
}
And a main function to test:
#include <iostream>
#include <functional>
int main() {
ApiWrapper a(2);
using std::placeholders::_1;
std::function<int(int)> lambda = std::bind( &ApiWrapper::ApiCallback, a, _1 );
std::cout << "Class: " << a.ApiCallback(21) << std::endl;
std::cout << "Lambda: " << lambda(21) << std::endl;
std::cout << "Static ApiCall:" << ApiCall(21, ApiWrapper::WorkingApiCallback) << std::endl;
// NOT WORKING
std::cout << "Static ApiCall:" << ApiCall(21, lambda) << std::endl;
std::cout << "Static ApiCall:" << ApiCall(21, this.ApiCallback) << std::endl;
return 0;
}
Is there a way to achieve this without using the static member; as I need to associate/use state each time the callback is invoked by the 3rd party lib.
Online Runnable Source: https://ideone.com/7nVSi8
Thanks!
You are going to have to somehow associate a non-member with the function pointer, as it does not take any sort of context argument. You are going to have a non-member function at some point, no way around that. And that function cannot be a capturing lambda.
One option is to have a (possibly thread local) global pointer that holds the instance.
i.e. you do:
myType * glbInst;
..void someFunc()
{
myType myInst;
glbInst = &myInst;
apiCall([] (int) glbInst->member(x); });
}
Another option is to use libffi's closure system to create a function at run-time and have the instance be part of the saved data. I use this pattern very often but it's a bit complicated to paste here.
Is there a way to achieve this without using the static member;
There's a hackish solution.
Define a non-member function.
Store a pointer to an object that can be accessed by the non-member function.
Call the member function of the object from the non-member function.
ApiWrapper* objectForCallback = NULL;
int NonMemberFunction(int x)
{
assert(objectForCallback != NULL);
return objectForCallback->ApiCallback(x);
}
and the setup for the above to work:
ApiWrapper a(2);
objectForCallback = &a;
Now you can use:
std::cout << "Static ApiCall:" << ApiCall(21, NonMemberFunction) << std::endl;

Call a std::function class member with std::mem_fn

My plan is to build several listener classes which own predefined "callback hooks".
In the example below class Foo has a "callback hook" called onChange. It will be set to a default callback function during construction. It can also be set to an arbitrary function which provides the correct interface, like shown with the object f1 and the function callback().
The problem is when I want to call the object member onChange inside the for-loop the compiler says that I provide to much arguments. i am aware of the problem that i don't provide a member function to the std::mem_fn but instead an object member which is a function wrapper.
How do I manage to pass the argument to the std::function member object of class Foo without using std::bind and use std::mem_fn instead like shown in the example?
struct Foo
{
Foo()
{
// default callback
onChange = [](int value)
-> void { std::cerr << "Foo::onChange( " << value << " )" << std::endl; };
}
// class destructor
virtual ~Foo() {}
std::function<void(int value)> onChange;
};
void callback(int value)
{
std::cerr << "callback( " << value << " )" << std::endl;
}
int main()
{
Foo f0;
Foo f1;
f1.onChange = callback;
auto vec = std::vector<Foo>();
vec.push_back(f0);
vec.push_back(f1);
auto func_wrapper = std::mem_fn( &Foo::onChange );
for (auto f : vec)
{
func_wrapper(f, 42);
}
}

std::bind and overloaded function

Please refer the following code snippet. I want to use the std::bind for overloaded function foobar. It calls only the method with no arguments.
#include <functional>
#include <iostream>
class Client
{
public :
void foobar(){std::cout << "no argument" << std::endl;}
void foobar(int){std::cout << "int argument" << std::endl;}
void foobar(double){std::cout << "double argument" << std::endl;}
};
int main()
{
Client cl;
//! This works
auto a1 = std::bind(static_cast<void(Client::*)(void)>(&Client::foobar),cl);
a1();
//! This does not
auto a2= [&](int)
{
std::bind(static_cast<void(Client::*)(int)>(&Client::foobar),cl);
};
a2(5);
return 0;
}
You need to use placeholders for the unbound arguments:
auto a2 = std::bind(static_cast<void(Client::*)(int)>(&Client::foobar), cl,
std::placeholders::_1);
a2(5);
You can also perform the binding with a lambda capture (note that this is binds cl by reference, not by value):
auto a2 = [&](int i) { cl.foobar(i); };