I have a general purpose class which is used in different contexts - sometime as static variable, and sometime as a normal variable on the stack/heap.
When it is used as a normal variable the destructor must be called when it goes out of scope - as normal. The executable is used in an embedded target where flash is a limited resource and which will never exit, and for this I would like this "exit" code to be disabled.
Following is an example to illustrate the problem. A
is the class where the
destructor is needed for normal circumstances, but is not needed for static
variables.
struct Abstract {
virtual ~Abstract() {}
};
struct A : public Abstract {
int i = 0;
};
static A a;
static A b;
Following is the assembler code generated (compiled with -Os -std=c++11
-fno-exceptions -fno-rtti
) generated by: http://goo.gl/FWcmlu
Abstract::~Abstract():
ret
A::~A():
ret
A::~A():
jmp operator delete(void*)
Abstract::~Abstract():
jmp operator delete(void*)
pushq %rax
movl $__dso_handle, %edx
movl a, %esi
movl A::~A(), %edi
call __cxa_atexit
popq %rcx
movl $__dso_handle, %edx
movl b, %esi
movl A::~A(), %edi
jmp __cxa_atexit
vtable for Abstract:
vtable for A:
b:
.quad vtable for A+16
.long 0
.zero 4
a:
.quad vtable for A+16
.long 0
.zero 4
As seen in the assembler code above a fair amount of instructions is issued to do this clean up code.
Is there anything which can be done to disable this unneeded cleanup code? It does not need to portable - as long as it works in recent versions of GCC. Attributes, linker scripts, altering the object files, and other tricks are mostly welcome.
A simple solution is to use placement new - instantiating the objects on appropriately sized static arrays. You can also use a reference variable to access the objects via the instance rather than the pointer.
In placement objects, the destructor must be explicitly called so you have complete control over whether and when it is called.
Just use a reference to a heap allocated variable. It will leak, but I guess that's what you want.
The answer is by creating a wrapper:
This wrapper can be used to wrap classes which destructor should not be called:
The way it works is - we reserve (statically) some memory big enough to contain the object with proper alignment. Then we use an in-place new operator to create the actual object in the reserved memory and forward potential arguments to its constructor. We can access the object from our wrapper using operator ->. The destructor will never be called because, from the compiler perspective, there is no object of class T anywhere - only an array of bytes. We just use those bytes to hold the object.
In a bare-metal embedded system you normally have access to the run-time start-up code (usually in assembler); this code performs global static initialisation including calling constructors before calling
main()
. It also determines what happens ifmain()
terminates; that is where static destructors will be called - this code can be removed (if it even exists already), so that the destructor is not explicitly called on termination - this may allow linker optimisation to then remove the unused code.You should check the map file to determine whether the destructor is included in the build rather than looking at the compiler assembler output - the compiler has no option but to generate the code since it does not know whether it will be externally referenced or not. You may need to set specific linker options to remove unused code.