1

We are designing some "functional-like" data-structures, where every object is immutable. To represent the empty element of each container, we decided to have a static instance.

What we were doing is defining all those static objects in a single file StaticInit.h, so we had control over the order of definition. That header file was included by the main implementation file.

// StaticInit.h
FunctionalMap FunctionalMap::gEmpty = FunctionalMap();

The templated class and static object

Complication arises when we implemented some templated structures :

// FunctionalArray.h
template <class T_contained>
class FunctionalArray
{
    static FunctionalArray gEmpty;

    private:
        FunctionalArray();
}

template <class T_contained>
FunctionalArray<T_contained> FunctionalArray<T_contained>::gEmpty = FunctionalArray();

The standard

From the standard (and this answer)

A [...] static data member of a class template shall be defined in every translation unit in which it is implicitly instantiated [...], unless the corresponding specialization is explicitly instantiated [...] .

Since we want to avoid explicit instanciation of each static object, it seems we are forced to keep the (templated) definition of the static FunctionalArray::gEmpty in the header file (the only way we can guarantee that the definition will be present in all the translation unit instantiating a FunctionalArray).

The problem

Now, we have a (non templated) static object which initialization uses an instance of a templated static object.

// StaticInit.h
#include "FunctionalArray.h"
DependantClass DependantClass::gEmpty = methodReferingToTheEmptyArray();

By including FunctionalArray.h (where the definition of the empty array is done), we would have expected not to get bitten by the static initialization order problem... we could not have been more wrong !

Questions

  • Why is the order not specified in our case ? (I would guess that could be because the compiler is still producing only one actual definition of the empty array, and this definition could be in any other compilation unit using it. But guesses are not very satisfying...)
  • Is there a way to specify the order of initialization in our case, still using the "consolidated definition file" approach ?
Community
  • 1
  • 1
Ad N
  • 7,930
  • 6
  • 36
  • 80
  • 3
    [basic.start.init]/2 "Dynamic initialization of a non-local variable with static storage duration is either ordered or unordered. Definitions of explicitly specialized class template static data members have ordered initialization. Other class template static data members (i.e., implicitly or explicitly instantiated specializations) have unordered initialization" – dyp Nov 27 '13 at 11:05
  • @DyP: Thank you, that answers my first question ! Perhaps it could have been posted as an anwser : ) – Ad N Nov 27 '13 at 11:10
  • Couldn't you use static member functions with local static variables instead of static data members? – dyp Nov 27 '13 at 11:17
  • @DyP We had this alternative in mind (which seems well documented). Still, we were curious to see if there was an other way out ; ) From your extract of the standard, it seems that without explicit specialization there is none. – Ad N Nov 27 '13 at 11:19
  • sorry but it's not clear for me: why do you think you have initialization order problem? – Andriy Tylychko Nov 27 '13 at 11:26
  • @AndyT Because we actually hit one at 'runtime'. Perhaps should I have said so in the question, but it seemed anecdotic. – Ad N Nov 27 '13 at 11:28
  • Yeah, I missed a point that order is not defined for not explicitly specialized templates. Otherwise it would be fine because both initialization should be in the same TU – Andriy Tylychko Nov 27 '13 at 11:44

1 Answers1

0

Here is a proposal that you may find useful in resolving your issue. I used this technique to avoid multiple instantiations by encapsulating my static information within a non-static member. According to the standard, static variables defined within a member function will be instantiated once when the member is first called, also see relevant question here. Since statics are initialized after calling the containing member functions, then it is possible to completely control their initialization at runtime.

Please note, all types used in this code snippet are typedef'd in another file, but I believe that their real types are evident.

// type to name mapper template
template<typename T>
struct TP
{
    struct info
    {
        info() : pT(0), sz(0) { }
        Char *pT;
        UInt sz;
        Char *name() { return pT; }
        UInt size() { return sz; }
    } _info;

    TP() { }
    TP(const Char *pName) { typeInfo(pName); }

    info &typeInfo(const Char *pN=0) 
    { 
        static TP<T> x; 
        if (x._info.pT==0) { x._info.pT = (Char *)pN; x._info.sz=sizeof(T);/* prin tf("==New Type %s\n", pN);*/}
//      Assert(x._info.pT !=0 || "TP::typeInfo encountered an unknown type" == 0);
        return x._info; 
    }
};

struct InitBarrayTypes
{
    InitBarrayTypes()
    {
        TP<Char> ctp("char");
        TP<Int> itp("int");
        TP<Long> ltp("long");
        TP<Double> ftp("float");
    }
};

// this will be created many times, but has no data and so takes no space,
// and finally only the static members of TP within typeInfo() will remain
static InitBarrayTypes s__ibt;

The above is used as follows:

cout << "Name of Int is " << TP<Int>().typeInfo().name() << endl;

I hope you find the technique useful.

Community
  • 1
  • 1
DNT
  • 2,356
  • 14
  • 16