2

I have several modules that build to static libraries. Depending on what's needed, some of the static libraries may be linked, and others may not. Several of these static libraries have classes that have a static Init() method. The "Core" library, which is also linked in, also has an Init() method, and previously it contained multiple lines like:

Telephony::Init();

But if Telephony is not going to be linked into this specific application, I don't want to have to modify the Core library's code to remove that line. So I tried something like:

class init_hook_base
{
public:
    inline init_hook_base() { Core::AddInitHook(this); }
protected:
    friend class Core;
    virtual ~init_hook_base() {}
    virtual void operator()() const = 0;
};

template<class T>
class init_hook_t
{
protected:
    void operator()() const
    {
        T::Init();
    }
};

And then I would just make a static global variable like:

static init_hook_t<Telephony> telephony_init_hook;

In the Telephony library, and that would allow Core to call Telephony::Init indirectly without knowing about it at compile time. Unfortunately, telephony_init_hook is not getting constructed, and I'm assuming it's getting stripped in the linking phase despite its constructor having side effects. Doing something like:

void Telephony::Init() { (void)telephony_init_hook; }

Also doesn't work, because Init itself is also unreachable as far as the linker is concerned. Is there a way to achieve this style of modularity with C++? Compiler specific options are viable, so long as there's an equivalent for both GCC and VC++. Thanks.

Note: For those concerned about global initialization order affecting Core::AddInitHook:

std::vector<init_hook_base*>* Core::init_hooks; //THESE ARE DELIBERATELY UNINITIALIZED!!!
unsigned char Core::init_hooks_ptr_hash[20];
bool Core::inited = false;

void Core::EnsureInitHooksContainerReady()
{
    unsigned char current_hash[20];
    sha1::calc(&init_hooks, sizeof(init_hooks), current_hash);
    if (memcmp(current_hash, init_hooks_ptr_hash, 20))
    {
        //the hash is incorrect, so init_hooks is not yet initialized;
        init_hooks = new std::vector<init_hook_base*>();
        sha1::calc(&init_hooks, sizeof(init_hooks), init_hooks_ptr_hash);
    }
}

void Core::AddInitHook(init_hook_base* init_hook)
{
    EnsureInitHooksContainerReady();
    init_hooks->push_back(init_hook);
}

void Core::Init()
{
    assert(!inited);
    PlatformInit();
    EnsureInitHooksContainerReady();
    for (auto init_hook_base : *init_hooks) {
        (*init_hook_base)();
    }
}
Brent
  • 4,153
  • 4
  • 30
  • 63
  • 1
    What happens if you remove the static in front of the telephony_init_hook? – thisisdog Mar 14 '13 at 02:40
  • 1
    Also should init_hook_t inherit from init_hook_base? – thisisdog Mar 14 '13 at 02:46
  • @thisisdog - you're right. I thought it was kinda strange that something with side effects would be optimized away, but I don't know compilers that well I suppose. Turns out the missing inheritance was the problem. If you want to make an answer, I'll mark it as the answer. – Brent Mar 14 '13 at 17:22
  • The iostream library has a very ugly solution to this. I can't remember what it is offhand, but I do distinctly remember it being quite ugly. – Omnifarious Mar 14 '13 at 20:48
  • 1
    Possible duplicate of [How do I make an unreferenced object load in C++?](https://stackoverflow.com/questions/1939899/how-do-i-make-an-unreferenced-object-load-in-c) – Lanting Aug 17 '18 at 06:30

2 Answers2

1

You need to make init_hook_t inherit from init_hook_base. As it is right now the base class constructor doesn't get called.

thisisdog
  • 908
  • 1
  • 5
  • 13
  • Sorry, this seemed to solve the problem, but I soon learned that whether it works or not is completely dependent on compiler and whether or not it decides to include a translation unit. – Brent Mar 15 '13 at 23:08
0

Based on the information I found in several places like http://www.gamedev.net/topic/622861-how-to-force-global-variable-which-define-in-a-static-library-to-initialize/ and C++ global variable not initialized when linked through static libraries, but OK when compiled with source this is just not doable in any reliable fashion in C++.

Community
  • 1
  • 1
Brent
  • 4,153
  • 4
  • 30
  • 63