Reputation: 33
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:
dense_hash_map
object is being declared as a local variable inside that function, everything works correctly.So, at the end, what I would like to ask is:
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
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