Two nitpicks with that: first, anything with a virtual function really should get a virtual destructor as well. I once ran into really annoying leaks because someone else did not stick to that rule. Thankfully, many modern compilers will warn you about it.

Second, that's an implementation of the observer pattern. While it can be useful if you need more than one function to be called in response to different types of events but for just a single function I find it more like an anti-pattern imported from languages such as Java where you have no other choice.

Two nitpicks with that: first, anything with a virtual function really should get a virtual destructor as well. I once ran into really annoying leaks because someone else did not stick to that rule. Thankfully, many modern compilers will warn you about it.

Second, that's an implementation of the observer pattern. While it can be useful if you need more than one function to be called in response to different types of events but for just a single function I find it more like an anti-pattern imported from languages such as Java where you have no other choice.

Both valid points. However, if you need one callback, you may soon need multiple. In this case you might want to support on_hover, on_select, and on_pressed (if it's possible to use the keyboard to select and press the button), so the observer pattern may be viable.

Share this post

Link to post

Share on other sites

True, however in a lot of cases it's not immediately obvious that you will ever need more than one callback type. In these cases I would strongly recommend against the polymorphism-way in C++.

In Java for example creating a quick anonymous inner class is extremely simple. It even has access to data from its enclosing scope without any extra work.In C++ you need to define your (possibly local) observer implementation. If you need access to any data from an enclosing scope you have to transfer it there somehow which is some extra boilerplate. You might even be in a bit of a pickle regarding lifetime management of the observer.Especially with C++11 these kind of problems don't really exist with lambdas. But even before C++11 you could reduce the complexity a lot with, for example, Boost's function objects, bind and lambdas.

Share this post

Link to post

Share on other sites

I would suggest against using std::bind, it has some caveats and it keeps getting worse and worse the more advanced usage you need for it. It is also particularly opaque to the compiler. It seems I'm not the only one. I mean seriously.

Share this post

Link to post

Share on other sites

The reason was that I felt std::function was a sort of 'god class' that can hold all the functions, including capturing lambda state. Except I didnt think I would ever need to capture lambda state. So all the std::function logic for doing heap allocations and probably some if branches here and there seemed like useless overhead to me.

What I wanted was simple: store a pointer to an object, and a function pointer to a member function. Leave object pointer nullptr if its a free function.

(for lambdas I take the pointer of the operator() of the lambda object - I dont capture the state, the user must keep ownership of the lambda state and ensure it doesnt disappear, like with any other C++ object)

Didnt turn out to be so simple.

I eventually got it working by creating some template stuff to 'wrap'/convert a member function pointer into a regular free function (with a 'this' pointer parameter). I didnt test this with virtual functions, but it compiles fine and works with regular, member, and existing lambdas.

Share this post

Link to post

Share on other sites

There are already a numberofimplementations for C++ delegates out there that have been more heavily tested and scrutinized, so it's probably worth giving one of them a try first before rolling your own. This is exactly the kind of non-trivial code where subtle bugs love to breed ;)

0

Share this post

Link to post

Share on other sites

I'd tend to agree with the very first reply suggesting to use std::function<>. This avoids boxing you in if you decide the function calls should be defined as methods of a class instead. Function pointers are pretty simple to do though so they are worth some consideration as to whether you'll want to move things around or provide additional types of calls that can be made.

Anything beyond the following seems like you'd be taking on learning complicated syntax for the same semantics. Although I did see a suggestion about Lambdas which would be used with the function pointers and would be able to call object methods. A wrapper function for a method in other words.