On 8/12/2015 1:09 PM, Dan Kegel wrote:
> On Wed, Aug 12, 2015 at 12:58 PM, Jeffrey Walton <[email protected]> wrote:
>> The C++ object that uses it is in another translation unit, and it has
>> a init_pritority attribute.
> File-scope or static C++ objects are the spawn of the devil.
> There is no reliable or portable way to control initialization order,
> even with init_priority, See e.g.
> http://stackoverflow.com/questions/3371968/attribute-init-priorityx-in-gcc
>
I got around this problem in C++ by defining an initialization
order-independent method of creating global variables:
/* nvalues.hpp */
class nv_base {
public:
nv_base(const my_string &name);
const my_string &name(void) const { return _name; }
...
private:
my_string _name;
static nv_base *first_nv; /* for hash table initialization */
nv_base *next_nv; /* traverse from first_nv */
nv_base(const nv_base &other); /* unimplemented */
nv_base &operator =(const nv_base &other);
};
/* nvalues.cpp */
nv_base *nv_base::first_nv = 0;
nv_base::nv_base(const my_string &name)
{
/* define a new named variable - must be global */
_name = name;
next_nv = first_nv; /* link ourselves in */
first_nv = this;
}
class nv_integer : public nv_base {
...
};
/* client.cpp */
nv_integer config_param("a_named_integer_value",5);
As shown, client code derives from the base class, so I could have an
integer value, a string value, etc.
The traversal links allow high-level code to dump all of the values for
debugging or saving state. There is a routine, invoked by main(), to
prevent creation of these objects on the stack or with operator new (in
my case, it also built a hash table for all the names).
Access to the value in a named variable is only through a member
function, so regardless of link order, when you access a variable it is
initialized. Zero initialization is done before the program starts (ARM
section 3.4), and global variables in a translation module are
initialized before any code in that module (e.g. the constructors) is
called.
Originally I wrote this in 1991, for some of the very first C++
compilers. As features were added, I began to hit linkage order
problems. I went to great lengths to make it link-order safe across
platforms, compilers, and compiler versions, even defining a shadow set
of utility routines for use only by this subsystem so there were no
dependency loops. After all that, I used only one of these variables in
500,000 lines of code. Even though they were intended only for
configuration parameters (the global list allowed me to dump the system
configuration in the event of an error), I found that it was too
difficult to isolate problems in code, or even to reuse it, when there
was an arbitrary number of global variables influencing it.
Now I refactor the code to avoid global variables, even if it means
passing a value through multiple levels of existing code. I learned to
pass a parameter block to a function as soon as the number of parameters
got too high, or if I had to add a parameter to that function later. If
I have to modify a function once, it's a safe bet that I will have to
modify it again. It's much easier to add a field to a parameter block
and define a good default value (in the constructor for C++ or the
allocator function in C) than to modify every function call to have
another argument. Also, I avoid default function parameter values in
C++ because they make calling code harder to understand, and in a real
product a function is called many more times than it is defined (i.e. once).
Global variables are going to cause problems if you rely on the
compiler, linker, or loader to initialize or destroy them in the order
you want. Your code will always be vulnerable to the whims of another
programmer. That programmer could be working on GCC, a run-time
library, the OS, or even for you - when global variables change state at
surprising times, every line of code in your program suddenly becomes a
suspect. I've seen development of a suite of commercial products
(millions of dollars per year in sales) be crippled by the use of global
variables, even without considering initialization order.
So even though I just told you how to guarantee that global variables in
C++ are initialized before they are used, don't do it. :-) Refactoring
sounds expensive but in the long run it is cheaper than debugging
interactions between global variables.
--
David Chapman [email protected]
Chapman Consulting -- San Jose, CA
Software Development Done Right.
www.chapman-consulting-sj.com
------------------------------------------------------------------------------
_______________________________________________
Valgrind-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/valgrind-users