Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using std::function/mem_fn in C++11 with member functions

Long story short, is there a simple/defined way to treat function objects/lambdas and member functions in a streamlined way?

If I understand right, if I use std::mem_fn, I need to pass an object of the correct type to the function call, i.e.

Object o;
ftncall std::mem_fun(&Object::function);
ftncall(o);

Ideally, there would be some way to 'attach' o to that function object, perhaps as a std::weak_ptr, so we know if o got deleted. For example, if there were a way to do something vaguely like this:

Object o;
ftncall std::mem_fn(&Object::function, o); // Or maybe std::mem_fn<Object>
ftncall();

Now, obviously, this doesn't exist (to my knowledge). Is there a way to wrap std::mem_fn in such a way that I don't loose the generality (and niceness) of std::mem_fn, but can 'attach' o, AND still play nicely with the other function type, like std::function? Ideally, I'd still be able to use operator() in the way that I do with std::function.

Currently the best solution I can think of is a class that looks like this:

template<class T>
class MemFunWrapper {
public:
    MemFunWrapper(T* t, std::mem_fun funct) : m_t(t), m_function(funct) {}

    std::mem_fun operator*() { return m_function; }
    T* get() { return m_t; }
private:
    T* m_t;
    std::mem_fun m_function;
}

Then you could use it thusly:

(*MemFunWrapper)(MemFunWrapper->get(), args...);

But that seems pretty unwieldy to me. Also, I'd have to create an equivalent class for std::function for that to be usable in the analogous way, and that seems silly since I can already just use std::function. Ideally, I'd also be able to use the end product without knowing if I'm calling a member function or a regular function. I know I'm asking a lot - any direction would be helpful. Many thanks!

like image 310
Max Feldkamp Avatar asked Sep 03 '13 04:09

Max Feldkamp


People also ask

How do you declare a pointer to a member function in C++?

The pointer to member operators . * and ->* are used to bind a pointer to a member of a specific class object. Because the precedence of () (function call operator) is higher than . * and ->* , you must use parentheses to call the function pointed to by ptf .

What is C++ std :: function?

Class template std::function is a general-purpose polymorphic function wrapper. Instances of std::function can store, copy, and invoke any Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.

Why did we need to use an std :: function object?

It lets you store function pointers, lambdas, or classes with operator() . It will do conversion of compatible types (so std::function<double(double)> will take int(int) callable things) but that is secondary to its primary purpose.

Does std :: function allocate?

Many std::function implementations will avoid allocations and use space inside the function class itself rather than allocating if the callback it wraps is "small enough" and has trivial copying.


2 Answers

What you're looking for is std::bind instead of std::mem_fn:

#include <iostream>
#include <functional>

struct Object
{
  void member()
  {
    std::cout << "yay!" << std::endl;
  }
};

int main()
{
  Object o;
  auto fn = std::bind(&Object::member, o);
  fn();
}

FWIW: There's a proposal for adding an overload to std::mem_fn accepting the object for inclusion in C++14.

like image 79
goji Avatar answered Oct 06 '22 02:10

goji


In C++11, I usually find this is easiest done with lambdas:

std::shared_ptr<Object> o = std::make_shared<Object>();
auto ftncall = [o](){ if (o) o->function(); }
ftncall();

or, with no shared lifetime:

Object o;
auto ftncall = [&o](){ o.function(); }
ftncall();

or with a weak ptr:

std::shared_ptr<Object> o = std::make_shared<Object>();
std::weak_ptr<Object> wo = o;
auto ftncall = [ow](){ if (auto o = wo.lock()) o->function(); }
ftncall();

If you need type erasure, just stuff it into a std::function<void()>.

Lambdas in C++11 can do much the same job as bind does without having to rely on library functions to do it, and I personally find them much more readable.

There are some situations where bind can do a better job in C++11, but they are corner cases at this point (in C++1y, more corners will be cut off as well).

like image 36
Yakk - Adam Nevraumont Avatar answered Oct 06 '22 03:10

Yakk - Adam Nevraumont