Jump to content
  • Advertisement
Sign in to follow this  
MaulingMonkey

safe initialization-dependant global vars during initialization - how to templatize?

This topic is 4960 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I recently came up with a rather hackerish, but effective means of ensuring variable A of type B, which has a constructor, gets initialized before use - even when using _init/_fini or the safer: __attribute__((constructor))/__attribute__((destructor)). I came up with it to ensure my log globals (of type vector<string>) are initialized when I use them in functions with the aforementioned __attribute__ s. I replace:
__declspec(dllexport) array<string> error_log;
with:
__declspec(dllexport) array<string> & error_log( void )
{
	static array<string> error_log_;
	return error_log_;
}
and then replace:
error_log.push_back("OMG ERROR!!!111oneoneone");
with:
error_log().push_back("OMG ERROR!!!111oneoneone");
Which works dandy and great. I now get all the messages sent by all the __attribute__((constructor)) marked functions across my many .cc files. However, it has a few drawbacks: 1) I have to type overly much for a simple global variable (create a function for it) 2) error_log() is not exactly elegant. So I thought a good idea would be to create a templatized class by which one can access the actual class via -> or * operators. However, there is a problem:
template< typename T > class global
{
    T & get( void )
    {
        static T data;
        return data;
    }
public:
    T * operator->( void ) { return &get(); }
    T & operator*( void ) { return get(); }
};
does not work because static T data will declare a single data variable per type - not per instance. However, the corresponding code:
template< typename T > class global
{
    T data;
public:
    T * operator->( void ) { return &data; }
    T & operator->( void ) { return data; }
};
is no better than the original global, because the implicit ctor of global (which calls data's ctor) is not necessairly called before any other initialization function! One could solve point 2 at least (making useage elegant) by adding a template parameter that takes a function pointer. The best solutions to my knowledge would be a simple variant on the first example I gave, only adding a template parameter to make each class-instance unique ala: (edit: this dosn't work)
global<vector<string>,0> error_log;
global<vector<string>,1> debug_log;
Or better yet, using strings: (edit: This alas won't compile on GCC 3.3.3 on cygwin)
global<vector<string>,"industry::dll::error_log"> error_log;
global<vector<string>,"industry::dll::debug_log"> debug_log;
error: string literal "industry::dll::error_log" is not a valid template argument because it is the address of an object with static linkage (where the long strings help prevent other similarly named variables from melding together) However, this is still a bit far from elegant. So, I'd like to fish out here for feedback and ideas. A couple other options with their cons:
1. The Pointer singleton method

template < typename T > class global
{
    T * data;
    T * get( void ) { if (!data) data = new T; return data; }
public:
    T * operator->( void ) { return get(); }
    T & operator*( void ) { return *get(); }

    ~global( void ) { delete data; }
};

Cons:
1) Uses dynamically allocated memory needlessly.
2) Depends on heap being 0-initialized (otherwise data could equal anything before ctor call).
3) Needs to check (and derefernce) data each access instead of simply resolving at original page-load
2. The placement new method

template < typename T > class global
{
    char data[sizeof(T)];
    bool initialized;
    T * get( void )
    {
        if (!initialized)
        {
            initialized = true;
            new (reinterpret_cast<T *>(data)) T;
        }
        return reinterpret_cast<T *>(data);
    }
public:
    T * operator->( void ) { return get(); }
    T & operator*( void ) { return *get(); }

    ~global( void ) { reinterpret_cast<T *>(data)->~T(); }
};

Cons:
1) No alignment guarantee. data can be aligned starting at any byte, could cause problems on certain processor architecutres.
2) Depends on heap being 0-initialized (for "initialized" bool).
3) Needs to check bool each access instead of simply resolving at original page-load
3. Aligned placement new method

template < typename T > class global
{
    WORD data[(sizeof(T)/sizeof(WORD))+1];
    bool initialized;
    T * get( void )
    {
        if (!initialized)
        {
            initialized = true;
            new (reinterpret_cast<T *>(data)) T;
        }
        return reinterpret_cast<T *>(data);
    }
public:
    T * operator->( void ) { return get(); }
    T & operator*( void ) { return *get(); }

    ~global( void ) { reinterpret_cast<T *>(data)->~T(); }
};

Cons:
1) May enforce a higher than needed size in this implementation.
2) Depends on heap being 0-initialized (for "initialized" bool).
3) Needs to check bool each access instead of simply resolving at original page-load
[Edited by - MaulingMonkey on October 23, 2004 1:50:38 AM]

Share this post


Link to post
Share on other sites
Advertisement
The initialization check disadvantage also applies to the first method since that's how static class instances are implemented by the compiler.
Don't worry about static data not beeing initialized to zero, it's guaranteed. It's supposed to work for floats and pointers even if their actual bit patterns are non-zero.

In our project we use a combination of templates and macro magic to simulate global objects. The static instance itself only registers the objects and it's priority for later construction while the actual creation occurs in main(). Otherwise you're bound to get into serious problems with the error handling system sooner or later.

PM me if your interested in discussing some of the problems/solutions involved.

[Edited by - doynax on October 23, 2004 5:00:40 AM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!