If I do this :-
class Thing
{
...
void function (const std::string& message);
};
std::list<std::function<void()>> work;
and in some member of "Thing"
work.push_back(std::bind(&Thing::function, this, "Hello"));
Does either the call to std::bind or the use of std::function<> cause any dynamic memory allocation using new or otherwise? Or is all the storage allocated at compile time? If the standard doesn't say anything, what about in visual studio 2012 as my program will only need to build on there, and for efficiency I probably need to avoid dynamic memory allocations in the place where I am thinking of using this mechanism.
C malloc() method The “malloc” or “memory allocation” method in C is used to dynamically allocate a single large block of memory with the specified size. It returns a pointer of type void which can be cast into a pointer of any form.
As it happens, std::function is guaranteed to not allocate if constructed from a function pointer, which is also one word in size. So constructing a std::function from this kind of lambda, which only needs to capture a pointer to an object and should also be one word, should in practice never allocate.
To dynamically allocate memory in C++, we use the new operator. De-allocation: Deallocation is the "clean-up" of space being used for variables or other data storage.
Inside every std::string is a dynamically allocated array of char .
The standard doesn't specify, but in general it's easy to see that std::function
must allocate memory at least in some cases:
struct huge { char c[10000]; };
void foo(const huge &);
std::function<void()>{std::bind(foo, huge{})};
On the other hand it's possible for it to avoid allocation in at least some cases by siting its function object inside a preallocated buffer inside the function
object's footprint; obviously there is a tradeoff as this could make other uses take more stack memory. A good implementation would be able to avoid memory allocation when storing a raw function pointer in a function
object, and possibly also for a mem_fn
, but it's less likely that it'd do so for a bind
.
For example, libstdc++ (g++) inlines (functor) object pointers, function pointers, and (non-virtual) member function pointers, as well as anything else that'd fit in the same footprint, e.g. stateless functors (union _Nocopy_types
).
If you can, by inverting your control flow to accept templated functor objects instead of function
you can avoid any extra memory allocation:
template<typename F>
void my_algorithm(const F &);
my_algorithm(std::bind(foo, huge{}));
I just did some research on this for the case of g++.
When it comes to std::function and dynamic memory allocation there are two key points.
The implementation of std::function in gccs libstd+++ will store without dynamic memory allocation other things with size/alignment requirements less than or equal to the size/alignment requirements of the things it must store.
The largest thing it must store without dynamic memory allocation is a pointer to member function. On compilers based on the "itanium c++ ABI"* this is twice the size of a normal pointer. So you can store anything up to two pointers in size in a std::function in g++ without triggering dynamic memory allocation.
As far as I can tell std::bind just concatenates stuff together into an object, so binding anything to a member function will result in an object that is at least three pointers in size. Assigning this object to a std::function will result in dynamic memory allocation.
A better option is to use a lambda. This refers to the member function statically, giving you space to capture up to two pointers without triggering dynamic memory allocation.
To demonstrate I wrote some test code loosely based on yours. I got rid of the string and list and used a const char * (to avoid std::string related memory allocations) and placement new (this code was only intended to be built, not to be run) instead and fed it into godbolt.
#include <functional>
using namespace std;
class Thing
{
void foo();
void bar();
void function (const char * message);
};
char baz[1024];
void Thing::foo() {
new (baz) std::function<void()>(std::bind(&Thing::function, this, "Hello"));
}
void Thing::bar() {
const char * s = "Hello";
new (baz) std::function<void()>([this,s](){function(s);});
}
The results were.
Thing::foo():
mov r3, #0
push {r4, r5, r6, lr}
ldr r4, .L34
mov r6, r0
sub sp, sp, #16
mov r0, #16
str r3, [r4, #8]
bl operator new(unsigned int)
ldr r2, .L34+4
mov r1, #0
mov r3, r0
str r2, [sp]
mov r2, sp
ldr r5, .L34+8
ldr lr, .L34+12
ldr ip, .L34+16
str r1, [sp, #4]
str r6, [r0, #12]
str r0, [r4]
str r5, [r3, #8]
ldm r2, {r0, r1}
str lr, [r4, #12]
stm r3, {r0, r1}
str ip, [r4, #8]
add sp, sp, #16
pop {r4, r5, r6, pc}
ldr r3, [r4, #8]
cmp r3, #0
beq .L27
ldr r1, .L34
mov r2, #3
mov r0, r1
blx r3
.L27:
bl __cxa_end_cleanup
.L34:
.word .LANCHOR1
.word Thing::function(char const*)
.word .LC0
.word std::_Function_handler<void (), std::_Bind<void (Thing::*(Thing*, char const*))(char const*)> >::_M_invoke(std::_Any_data const&)
.word std::_Function_base::_Base_manager<std::_Bind<void (Thing::*(Thing*, char const*))(char const*)> >::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
Thing::bar():
ldr r2, .L38
sub sp, sp, #8
stm sp, {r0, r2}
add r2, sp, #8
ldr r3, .L38+4
ldmdb r2, {r0, r1}
ldr ip, .L38+8
ldr r2, .L38+12
stm r3, {r0, r1}
str ip, [r3, #12]
str r2, [r3, #8]
add sp, sp, #8
bx lr
.L38:
.word .LC0
.word .LANCHOR1
.word std::_Function_handler<void (), Thing::bar()::{lambda()#1}>::_M_invoke(std::_Any_data const&)
.word std::_Function_base::_Base_manager<Thing::bar()::{lambda()#1}>::_M_manager(std::_Any_data&, std::_Function_base::_Base_manager<Thing::bar()::{lambda()#1}> const&, std::_Manager_operation)
We can clearly see there is memory allocation in the bind case, but not in the lambda case.
* Which despite the name is used by g++ and clang++ across many different architectures.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With