4

I've written an object that stores a std::function<void(void*)>, which is passed in as an argument to the constructor. The object will later call back this std::function at some point in the future. This is implemented and working great.

In each class which uses this object, they call the constructor in the initialization list like so:

mCallbackObj(std::bind(&MyClass::MyFunc, this, _1))

However, I've found that every class which contains this object as a member is increasing my codespace by ~2K. With potentially hundreds of places where this object will be used, and limited codespace options (this is an embedded product), a 2k hit per use isn't acceptable.

One interesting observation is that if a class has a second object:

mCallbackObj2(std::bind(&MyClass::MyOtherFunc, this, _1))

this only increases codespace by some ~150 bytes - very acceptable! It is only when the object is used in different classes that I see the 2K hit. Putting the classes all in one .cpp file doesn't help - still a 2k hit per class that contains this object.

I've played around with using extern template class std::function<void(void*)>;, but this had no impact on ROM size.

I am using gcc 4.8. I'd love to use std::function and std::bind, but am about to give up and switch to class method pointers. They wouldn't be nearly as clean, but hopefully more ROM efficient.

Before I give up on this, are there any other options to help reduce my template codespace bloat?

Brad
  • 5,492
  • 23
  • 34
  • 1
    I wonder whether the situation changes if you use `[this](void* p){ return this->MyFunc(p); }` in place of `bind`. – Igor Tandetnik Nov 04 '15 at 23:40
  • @IgorTandetnik Might be slightly better, but `std::function` still generates plenty of boilerplate by itself. – T.C. Nov 04 '15 at 23:43
  • 1
    std::function is probably the cause of the bloat. (https://www.youtube.com/watch?v=zt7ThwVfap0) You could try replacing the bind with a lambda, but that won't likely reduce binary size. – John Nov 04 '15 at 23:44
  • You could try a lightweight alternative like this [fast delegate](https://codereview.stackexchange.com/questions/14730/impossibly-fast-delegate-in-c11), using the member function pointer template variant: `delegate::from(&foo);` – melak47 Nov 05 '15 at 00:20

1 Answers1

3

I dug into this a bit more and watched @John's video on std::function. In the video, STL recommends using lambdas over std::bind for a few reasons.

I tried converting the code to use a Lambda, which is exactly what @Igor Tandetnik recommended above. It is significantly better.

mCallbackObj(std::bind(&MyClass::MyFunc, this, _1))

Takes an additional 2,888 bytes of codespace. Instead using

mCallbackObj([this](void *info){MyFunc(info);})

Only takes an additional 812 bytes! This is significantly better, and good enough for my use-case. For C++ programmers unfamiliar with the syntax of std::bind, the lambda solution is also easier to read.


Update -

After bumping to GCC 6.0 and tweaking our compiler flags (we weren't always passing in -O2 previously), I've retested this.

Lambda:            216 bytes
std::bind:         221 bytes
function pointer:  228 bytes

All 3 are now much more reasonable. We are mostly using lambdas, as they are the easiest to read once developers are familiar with the syntax.

Brad
  • 5,492
  • 23
  • 34