Reputation: 446
Is there any way to prevent std::function
in gcc from dynamically allocating memory for larger function objects?
I would have expected the following code to work without dynamic allocation:
#include <functional>
#include <iostream>
// replace operator new and delete to log allocations
void* operator new (std::size_t n) {
std::cout << "Allocating " << n << " bytes" << std::endl;
return malloc(n);
}
void operator delete(void* p) throw() {
free(p);
}
class TestPlate
{
private:
int value;
public:
int getValue(){ return value; }
void setValue(int newValue) { value = newValue; }
int doStuff(const std::function<int()>& stuff) { return stuff(); }
};
int main()
{
TestPlate testor;
testor.setValue(15);
const std::function<int()>& func = std::bind(&TestPlate::getValue, &testor);
std::cout << testor.doStuff(func) << std::endl;
testor.setValue(25);
std::cout << testor.doStuff(func) << std::endl;
}
However it allocates 24 bytes. As far as I am able to tell this is because the pointer to method requires 16 bytes and the pointer to the class instance another 8 bytes. This seems to be either A larger than the internal memory available to the function object or B a plain bug.
I was wondering whether there are is any way to circumvent this type of behavior without changing the signature of the std::function
or creating a lot of additional wrapper code.
Upvotes: 6
Views: 1677
Reputation: 171403
Unfortunately GCC's function
only has space for a pointer to member function stored internally, so the result of your bind expression doesn't fit.
You can use a lambda expression instead though:
std::function<int()> f = [&]{ return testor.getValue(); };
This only requires space for a closure type containing a reference to testor
(which is half the size of a pointer to member, and a third of the size of the bind result), and GCC defines that closure so it can be stored in a std::function
.
Upvotes: 6
Reputation: 1376
Looking through the exceptionally skin-crawling functional
header of libstdc++ (the default C++ library of GCC), I found out that it is in fact impossible to avoid heap allocations with its present implementation. It seems to have a special manager and invoker member that's heap-allocated, and it is required for the class to work. If you really want to look at the source, here you go, but there are definitely black arts and witchcraft going on there.
There is some functionality in the header that implies passing in custom allocators are planned, but they don't seem to be implemented at this time.
In the meantime, you could try Boost.function.
Upvotes: 0