The importance of std::function
by Malte Skarupke
In C++11 there is a new class in the standard library called std::function. It allows you to store anything that is callable. For example
#include <functional> #include <iostream> void print_world() { std::cout << "World!" << std::endl; } int main() { std::function<void ()> hello_world = []{ std::cout << "Hello "; }; hello_world(); hello_world = &print_world; hello_world(); }
Prints “Hello World!” as you would expect. The assignment to and storage of the two different types is handled internally by the std::function.
The amazing thing is that calling a std::function is fairly cheap: It’s one virtual function call. That has wide implications for how you will use (or not use) virtual functions in the future. In many cases where you used to have to use a virtual function it is now better to use a std::function. For example for an update loop.
Classically an update loop would be implemented something like this:
struct Updateable { virtual void update(float dt) = 0; virtual ~Updateable(); }; struct Mover : Updateable { virtual void update(float dt) override; }; struct Jumper : Updateable { virtual void update(float dt) override; }; int main() { std::vector<std::unique_ptr<Updateable>> updateables; updateables.emplace_back(new Mover); updateables.emplace_back(new Jumper); while (true) { for (auto & updateable : updateables) { updateable->update(0.016f); } } }
And this is a good solution at first. But you’ve got that inheritance in there, and that doesn’t scale. You will probably want to add a render loop and maybe a separate update loop for the editor. Once you’ve got that many base classes it makes sense to combine them into one. And now you’ve just started on the way of having a big base class that gets used everywhere. Soon enough adding something different to the update loop involves a whole lot of unnecessary work.
Here is an alternate update loop using std::function:
struct Wanderer { explicit Wanderer(std::vector<std::function<void (float)>> & update_loop) { update_loop.emplace_back([this](float dt) { update(dt); }); } void update(float dt); }; struct Diver { explicit Diver(std::vector<std::function<void (float)>> & update_loop) { update_loop.emplace_back([this](float dt) { update(dt); }); } void update(float dt); }; int main() { std::vector<std::function<void (float)>> update_loop; Wanderer wanderer{update_loop}; Diver diver{update_loop}; while (true) { for (auto & function : update_loop) { function(0.016f); } } }
This has no inheritance. Meaning I can add anything to the update loop now. If I create a new object that is unrelated to anything I had before and is managed completely differently, I can still add it just as easily as anything else. And I can use the same idea for a render loop or for implementing an observer pattern. It makes everything immensely easier.
So how does this perform?
edit: My initial measurements were incorrect for the debug version. I have new measurements in this post. Spoilers: std::function is faster than a virtual function in optimized code, and slower than a virtual function in debug code.
There are downsides to std::functions: They use more space, they may have to perform a heap allocation on construction (depending on the size of your functor) and they may add to your compile and link time because you’re compiling a new template for everything. I don’t consider these to be big problems. The heap allocation disqualifies it from a few use cases, but you probably don’t care in most situations. For the build time problem it is probably enough if you are aware of it and keep std::function construction and assignment out of your headers.
The speed of std::function has big implications. It means that you can get rid of a lot of virtual functions and a whole lot of inheritance. Look at every single instance where you are using virtual functions or function pointers and think about if you can not use a std::function instead. You will find that fairly often you can, and you should. It’s going to take some time to figure out the new rules for this, but for now my rules are this: If I have a base class which has a derived class that has different behavior: Use a virtual function. If I have a function that has several different implementations: Use a std::function. You can also combine the two and use both (which is then a bit slower than using a virtual function only).
Very nice idea! It does make a lot of things easier. So, tell me the answer: How do you remove objects from this update loop?
I’ve finally shared a solution to that problem:
https://probablydance.com/2015/09/07/a-surprisingly-useful-little-class-twowaypointer/
That one turned out to be complicated. I ended up at the simplest possible design where I assign each added function an ID and you can remove it using that ID.
The complicated part was making that convenient. My idea is to return a struct that removes the object in it’s destructor. So if you add a class member function to the update loop you just store the returned struct inside the class and whenever it gets destroyed the function automatically gets removed.
And I was going to write a bit about that and about a very clean signal/slots implementation where everything is nicely decoupled and I allow you to sort by type, so that you can say “I need to update after that type has updated.”
Except then you’re running into problems because objects tend to remove themselves or other objects from the update loop in their update function. Or they add new objects. Which you don’t want to happen while you’re iterating through the loop. And with that struct those objects don’t have control over when the removal happens.
So you’d need some delayed remove code but that’s not easy if you’re dealing with all kinds of different objects. So I had to move the delayed remove code into the signal itself because it was the only place where you know what’s going on. And it turns out that that has it’s own problems because it makes the container more complicated.
Basically it’s no longer a nice and clean implementation, so it’s not as much fun to write about that.
But if you just do the “here’s an ID, you can remove yourself again if you give me the ID” approach, you should get pretty far, even if it is a bit annoying to the user.
What about using a std::list instead of std::vector, and take advantages of the list iterators as an “ID”.
It doesn’t solve a everything, more work is needed to manage the deletion at the right time but it’s quite straightforward.
Yes, that would make erasing very easy. The downside would be that everything gets it’s own allocation on the heap. Because of that iterating over a list is much slower than iterating over a vector. And I don’t think that this is a place where it would be a good trade-off to make erasing easier if that makes iterating over the list slower.
What’s not so good about it: hidden memory allocations and massive linker bloat.
I wish C++ actually had proper first-class delegates, so that we would have to rely on some overly complex template machinery.
For the linker bloat I am still hopeful that it won’t be too bad, because std::function is not a bad template. You can implement it without too much template magic and sometimes templates actually compile pretty quickly. I haven’t used std::function in a big project, so I’d be interested if you have experience for example with boost::function.
My expectation at the moment is that memory allocations won’t be a big problem because most functor objects are small, and I don’t expect that to change. And hopefully everyone will implement a small functor optimization soon.
First-class delegates would be cool, but I wouldn’t know how to implement them without template machinery. In C++ you have to remain type safe and call copy/move constructors and destructors properly. Other languages that have first-class delegates just do pointer assignment and then rely on the garbage collector for cleaning up. That’s actually one of the things that annoyed me about the D programming language: You can’t use delegates without the garbage collector.
So even if we had first class delegates, you would still have to generate all of that code. You might be able to do that faster if you didn’t have to go through templates, but I’m not convinced that it would be a big gain.
I personally find myself using std::function all over the place, to the extend I feel guilty about it!
Basically almost every class, except for the very basic ones, have few of these.
This way I can delegate part of the class functionality, which is not strictly part of the class purpose, to others to “implement” (or not).
And I also noticed, I can use std::function in the place of inheritance, not only that, but in many ways it can be more powerful, because different part of the “interface” can be “implemented” by different, completely unrelated objects. And you can even change all that at runtime by hooking different “implementors”.
“If I have a base class which has a derived class that has different behavior: Use a virtual function. If I have a function that has several different implementations: Use a std::function. ”
I didn’t get this. Different behavior is different implementation? Or you meant that you can configure your class to call THAT ONE specific function when you have func1, func2, func3 and then u make a std::function func and init it with 1 of those 3 in class constructor?
It doesn’t make sense not to use virtual fucntions in your code example, since you break design and make it harder to understand what’s going on in code. Mover and Jumper both are renderable objects so it makes sense to inherit from IRenderer or something
^Updatable not Renderable, sorry
It’s about complex code. I have often found that inheritance based code doesn’t scale well to complex systems. You always end up wanting to use something that doesn’t fit into your inheritance hierarchy.
That is difficult to illustrate in a simple example that fits into a blog post though…
I think it’s worth mentioning that the lambdas in the c’tors of `Wanderer` and `Diver` both capture `this` but neither of them can guarantee their instances’ lifetimes; it would be up to the user to manage this. A safer solution would be to also capture a `shared_ptr` obtained by `shared_from_this()`
struct Wanderer : std::enable_shared_from_this
{
…
void add_to_update_loop(std::vector<std::function>& update_loop)
{
auto self(shared_from_this());
update_loop.emplace_back([this, self](float dt) { update(dt); });
}
You wouldn’t be able to do this in the constructor anymore and there would be an additional allocation to make a `shared_ptr`.
Correct. There is a problem with the shared_ptr solution though and that is that in that case the update loop would keep all objects alive that live inside of it. So you’d have to use a weak_ptr, which has performance overhead. Another solution would be to make sure that the class unregisters in its destructor. That would have zero overhead.