Added comments to clarify static intialization...

...and static de-initialization problems.
This commit is contained in:
Thonsew 2011-09-09 14:35:33 +00:00
parent f2cc41ebe3
commit 183069699d
2 changed files with 40 additions and 8 deletions

View file

@ -24,10 +24,15 @@
namespace n_token {
/// Do not inline this enforces static initialization order
/// Do not inline this enforces static initialization order and static de-initialization order
const t_token & t_token::z_empty() {
static t_token z_empty("", false);
return z_empty;
//This is NOTa memory leak
//It is static so it is only allocated once and not de-allocated until the program terminates.
///If changed to a static reference it may cause a static de-initialization
//core dump when the destructor for z_empty is called here before its last use in another file.
//Do not remove the = new t_token("", false);
static t_token *z_empty = new t_token("", false);
return *z_empty;
}
}//end namepace

View file

@ -40,13 +40,35 @@ namespace n_token {
It is typically reference counted unless explicitly requested otherwise upon construction (i.e for static strings)
The copying of tokens is faster than the creation of tokens, because a reference object isn't created.
Hence use static tokens as replacements for constant string literals in the code, as follows:
static const t_token z_some_string("some_string", false);
Hence use static tokens as replacements for constant string literals in the code in a local scope, as follows:
static const t_token z_some_string("some_string", false);
@see below on caveats in Static Initialization
@note It is intentionally inconvenient to convert to std::string. Allowing automatic conversion would allow
the compiler to create std::string temporaries and give up the benefits of the t_token. Do not
make a std::string conversion operator.
@par @b Static Initialization and Deinitializtion @b
Static initialization problems can occur when a static file local variable from one compilation unit/file is
accessed in a function called from another compilation unit/file during static initialization/de-initialization.
The initialization order is not specified in the C++ spec and hence potentially random. There are 3 ways to fix
it for 3 different use cases, outlined below.
@li 1. Create a non-inlined function that allocates (via new) a static pointer to a t_token and return a reference to it.
This is used in z_empty() and is rock solid and never fails. As long as it is not inlined the static allocation is executed once
when control passes over the line. It is never de-allocated so it never faces the static de-initialization problem. The downside
is that is always results in the overhead of a function call. It is necessary for creating default parameters in function
declarations.
@li 2. Use new to allocate a static pointer in a local scope block. This is also rock solid and works for everything except
creaing default parameters in function declarations. This has the slight drawback of the pointer overhead.
static t_token *z_empty = new t_token("", false);
@li 3. Lastly create a static object in a local scope. This is avoids the pointer dereference, but risks a static de-initalization
core dump if z_some_string is deallocated before its last use. If you are sure that this function is never called by destructors
outside of the compilation unit/file that it is created in, then this method is correct, but a little risky
static const t_token z_some_string("some_string", false);
*/
class t_token : public n_interned::t_interned_token<std::string> {
@ -80,9 +102,14 @@ public:
/// Do not inline this enforces static initialization order
template <char T_defval>
const t_token& t_token::default_value() {
static t_token z_defval(std::string(1, T_defval), false);
return z_defval;
t_token const & t_token::default_value() {
//This is NOTa memory leak
//It is static so it is only allocated once and not de-allocated until the program terminates.
//If changed to a static reference it may cause a static de-initialization
//core dump when the destructor for z_empty is called here before its last use in another file.
//Do not remove the = new t_token(std::string(1, T_defval), false); part
static t_token *z_defval = new t_token(std::string(1, T_defval), false);
return *z_defval;
}
inline std::string operator+(const n_token::t_token &a, const std::string &b) { return static_cast<std::string const &>(a) + b; }