FilippoBiga
FilippoBiga

Reputation: 33

Template object declaration and initialization: manually call template constructor to bypass standard constructors calls order

First of all, I'm sorry if my terminology is somewhat imprecise or incorrect: this is an aspect of C++ I haven't dealt with very much.

I'm using Google's sparsehash hash table implementation for an iOS project (I'm dealing with shared libraries because it's targeted to jailbroken devices): particularly, I'm using dense_hash_map: I need an object of that template (is that naming correct?) as a member of a struct which is shared across multiple files in a project.

I need to access that 'object' (that is, the dense_hash_map inside the struct) inside an __attribute__((constructor)) function, a constructor of a shared library. The problem is, apparently, the "automatic constructor" of the template is being called after dyld handles the constructors of the shared library: I can only use the dense_hash_map object only if I manually call the constructor as in mymap = dense_hash_map<const char*, int, hash<const char*>, eqstr>(); inside the __attribute__((constructor)) function.

However, the template constructor is being called twice in this way, one manually and one as per the C++ automatic constructor. How can I avoid this without altering the design of the template itself?

Other strange things are:

So, at the end, what I would like to ask is:

  1. How can I declare a template object and force it to be manually initialized, basically not having the template constructor to be automatically called?
  2. How are template constructors handled differently between the local/global variable cases?

If I am not using the correct terms, please let me know. I hope someone could shed a light on this, thanks in advance!

UPDATE: example code with different cases.

// global dense_hash_map
dense_hash_map<const char*, int, hash<const char*>, eqstr> months;

__attribute__((constructor)) static void my_constructor_0()
{
    /*
     actually crashes because you need to call set_empty_key()
     right after the template constructor, which has not been called
     yet, at this point
     */
    months.set_empty_key(NULL);
    months["january"] = 31;
}



__attribute__((constructor)) static void my_constructor_1()
{
    // local dense_hash_map
    dense_hash_map<const char*, int, hash<const char*>, eqstr> months;

    /*
     doesn't crash, works as it should and the map is being initialized
     correctly
     */

    months.set_empty_key(NULL);
    months["january"] = 31;
}


// global dense_hash_map
dense_hash_map<const char*, int, hash<const char*>, eqstr> months;

__attribute__((constructor)) static void my_constructor_2()
{
    /*
     works as it should but if you add a log to the template constructor,
     you see it being initialized twice: the first one is the manual call
     and the second one is the automatic constructor
     */

    months = dense_hash_map<const char*, int, hash<const char*>, eqstr>();
    months.set_empty_key(NULL);
    months["january"] = 31;
}

Upvotes: 2

Views: 459

Answers (1)

uroboro
uroboro

Reputation: 291

Reading C++: When (and how) are C++ Global Static Constructors Called? and http://www.parashift.com/c++-faq-lite/static-init-order.html

There seems to be something called "static initialization order fiasco". The order of initialization varies between compilations, therefore it is not certain when a variable is initialized before use.

A suggestion from http://www.parashift.com/c++-faq-lite/static-init-order-on-first-use.html is to create a function containing a static variable that will ensure initialization happens before first use.

From that, and using dense_hash_map from this question, here is a snippet of code that prevents this problem:

typedef dense_hash_map<const char*, int, hash<const char*>, eqstr> hashtable;

//singleton function
hashtable *month_singleton() {
    static hashtable *months = NULL;
    //initialization, happens on first run
    if (months == NULL) {
        months = new hashtable();
        months.set_empty_key(NULL);
    }
    return months;
}

__attribute__((constructor)) static void monthly_constructor()
{
    months_singleton()["january"] = 31;
}

Upvotes: 2

Related Questions