Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/graphitemaster/0xABAD1DEA

Static global objects with constructors and destructors made useful in C++
https://github.com/graphitemaster/0xABAD1DEA

Last synced: 3 months ago
JSON representation

Static global objects with constructors and destructors made useful in C++

Awesome Lists containing this project

README

        

# 0xABAD1DEA

This little header provides an alternative way to construct and destruct static
global objects.

# How to use
Add `0xabad1dea.cpp` and `0xabad1dea.h` to your project.

# Examples

## Trivial
```c++
struct A {
A() { printf("A::A()\n"); }
~A() { printf("A::~A()\n"); }
};
StaticGlobal gMyA0("MyA0"), gMyA1("MyA1"); // uninitialized

int main() {
StaticGlobals::initialize(); // calls the constructors objects are now initialized

// typically best to use atexit(StaticGlobals::deinitialize)
StaticGlobals::deinitialize(); // calls the destructors
}
```

## Calling individuals
```c++
int main() {
StaticNode *node = StaticGlobals::find("MyA1");
if (node) node->construct(); // manually initialize MyA1
// ...
if (node) node->destruct(); // manually destruct MyA1
}
```

## Removing individuals
```c++
int main() {
StaticNode *node = StaticGlobals::find("MyA1");
if (node) StaticGlobals::remove(node); // remove MyA1 from initialization and destruction
}
```

## Real world example
Have a memory allocator which needs to be allocated before all other static globals,
you could lazily construct it with a singleton pattern or you could use something like:
```c++
StaticGlobal gAllocator("Allocator");
/// ...
int main() {
atexit(StaticGlobals::deinitialize); // be sure to call destructors of statics at exit
StaticNode *node = StaticGlobals::find("Allocator");
if (node) {
node->initialize(); // call the constructor
StaticGlobals::remove(node); // remove from the statics list
}
StaticGlobals::initialize(); // initialize all other statics

// ... typical code

if (node)
node->deinitialize(); // deinitialize the allocator
}
```

Of course the other statics may depend on the allocator to exist (e.g free memory)
so we cannot deinitialize in main, what we could do is something like this which
uses atexit to register a lambda function which destroys the statics before the
other atexit handler for all other static global objects runs.
```c++
StaticNode *node;
int main() {
node = StaticGlobals::find("Allocator");
if (node) {
node->initialize(); // call the constructor
atexit([](){ node->deinitialize(); }); // this atexit will be called first
StaticGlobals::remove(node); // remove from the statics list
}
StaticGlobals::initialize(); // initialize all other statics
atexit(StaticGlobals::deinitialize); // be sure to call destructors of statics at exit

// normal code
}
```

# How it works
Using an intrusive linked list to thread a doubly linked list of each global
using static storage and then iterating that linked list to actually call
placement new on uninitialized storage to construct the static objects.
The same is done for destruction except the tail end of the linked list is
used so things get destroyed in reverse order as they were constructed.

The intrusive use of the node makes it convienent to do type-erasure, avoid heap
allocations and most importantly get the associated data backing the actual
object. Take a look at the definition of `StaticGlobal`. It uses a technique to
carry over the template type into node's constructor which the node then uses
to get the address of two wrapper functions for constructing and destructing
the object. The layout of `StaticGlobal` is such that immediately after the
node object there is the data we'll be using to construct the object. So
by going one past a `StaticNode` pointer we're effectively in the data for that
node.

# Thread safety
Since this system does depend on existing C++ static constructors to function
and C++ permits an implementation to thread the initialization of static objects
a global lock is used on the linked list. The lock is implemented in terms of
`pthread_mutex_t` but can be replaced with a more suitable mutex or locking
primitive of your choosing (it's only ~8 lines or so in the source file.)

# License
```
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to
```