On 8/12/2015 1:09 PM, Dan Kegel wrote:
> On Wed, Aug 12, 2015 at 12:58 PM, Jeffrey Walton <noloa...@gmail.com> 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      dcchap...@acm.org
     Chapman Consulting -- San Jose, CA
     Software Development Done Right.
     www.chapman-consulting-sj.com


------------------------------------------------------------------------------
_______________________________________________
Valgrind-users mailing list
Valgrind-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/valgrind-users

Reply via email to