Globals initialization using templates

Discussion in 'C++' started by Dilip, Nov 16, 2006.

  1. Dilip

    Dilip Guest

    The subject is a bit misleading because I really can't figure out what
    the following code snippet is doing and would appreciate any help in
    deciphering it.

    I mean I can understand code-wise what is going on but I am missing the
    larger picture.

    In what circumstances would I use this?

    template<typename T>
    class Globals
    {
    public:
    Globals()
    {
    if (impl == NULL)
    {
    init();
    }
    }
    T* operator->() const
    {
    return impl;
    }
    private:
    static T* impl;
    static void init()
    {
    static union
    {
    char buf[ sizeof( T ) ] ;
    double dummyForAlignment ;
    } data ;
    impl = new (data.buf) T;
    }
    };

    template<typename T>
    T* Globals<T>::impl = NULL;
    Dilip, Nov 16, 2006
    #1
    1. Advertising

  2. Dilip

    Guest

    Dilip wrote:
    > The subject is a bit misleading because I really can't figure out what
    > the following code snippet is doing and would appreciate any help in
    > deciphering it.
    >
    > I mean I can understand code-wise what is going on but I am missing the
    > larger picture.
    >
    > In what circumstances would I use this?
    >
    > template<typename T>
    > class Globals
    > {
    > public:
    > Globals()
    > {
    > if (impl == NULL)
    > {
    > init();
    > }
    > }
    > T* operator->() const
    > {
    > return impl;
    > }
    > private:
    > static T* impl;
    > static void init()
    > {
    > static union
    > {
    > char buf[ sizeof( T ) ] ;
    > double dummyForAlignment ;
    > } data ;
    > impl = new (data.buf) T;
    > }
    > };
    >
    > template<typename T>
    > T* Globals<T>::impl = NULL;


    Defining Globals in a multithreaded environment (even though toe code
    here doesnt have any multi threading constructs)where you could control
    the order of Initialization especially when you oculd be possibly
    having a bunch of globals depending upon each other.
    , Nov 16, 2006
    #2
    1. Advertising

  3. Dilip

    Dilip Guest

    wrote:

    > Defining Globals in a multithreaded environment (even though toe code
    > here doesnt have any multi threading constructs)where you could control
    > the order of Initialization especially when you oculd be possibly
    > having a bunch of globals depending upon each other.


    can you give me a short example?

    I thought the order of initialization for global variables are defined
    within a translation unit?
    Do you mean cases where global vars defined across translation units
    somehow end up depending on one another for initialization? If so, how
    does that class solve the problem?
    Dilip, Nov 16, 2006
    #3
  4. Dilip

    red floyd Guest

    Dilip wrote:
    > The subject is a bit misleading because I really can't figure out what
    > the following code snippet is doing and would appreciate any help in
    > deciphering it.
    >
    > I mean I can understand code-wise what is going on but I am missing the
    > larger picture.
    >
    > In what circumstances would I use this?
    >
    > template<typename T>
    > class Globals
    > {
    > public:
    > Globals()
    > {
    > if (impl == NULL)
    > {
    > init();
    > }
    > }
    > T* operator->() const
    > {
    > return impl;
    > }

    // missing destructor
    // this is necessary in case T manages some kind of resource.
    ~Globals()
    {
    impl->T::~T();
    }

    > private:
    > static T* impl;
    > static void init()
    > {
    > static union
    > {
    > char buf[ sizeof( T ) ] ;
    > double dummyForAlignment ;
    > } data ;
    > impl = new (data.buf) T;
    > }
    > };
    >
    > template<typename T>
    > T* Globals<T>::impl = NULL;
    >
    red floyd, Nov 16, 2006
    #4
  5. Dilip

    Guest

    Dilip wrote:
    > wrote:
    >
    > > Defining Globals in a multithreaded environment (even though toe code
    > > here doesnt have any multi threading constructs)where you could control
    > > the order of Initialization especially when you oculd be possibly
    > > having a bunch of globals depending upon each other.

    >
    > can you give me a short example?
    >
    > I thought the order of initialization for global variables are defined
    > within a translation unit?
    > Do you mean cases where global vars defined across translation units
    > somehow end up depending on one another for initialization? If so, how
    > does that class solve the problem?


    what is the following code doing ?

    static T* impl;
    static void init()
    {
    static union
    {
    char buf[ sizeof( T ) ] ;
    double dummyForAlignment ;
    } data ;
    impl = new (data.buf) T;
    }

    How many times do you think the Init function is called per type ?
    , Nov 16, 2006
    #5
  6. red floyd wrote:

    > // missing destructor
    > // this is necessary in case T manages some kind of resource.
    > ~Globals()
    > {
    > impl->T::~T();
    > }


    That's not clearly the right thing. impl is a static and so it does not
    necessarily need destruction from a memory leaking perspective.
    Gianni Mariani, Nov 16, 2006
    #6
  7. Dilip

    Guest

    Gianni Mariani wrote:
    > red floyd wrote:
    >
    > > // missing destructor
    > > // this is necessary in case T manages some kind of resource.
    > > ~Globals()
    > > {
    > > impl->T::~T();
    > > }

    >
    > That's not clearly the right thing. impl is a static and so it does not
    > necessarily need destruction from a memory leaking perspective.


    impl might be static....but it might be holding a pointer to an object
    T which might be holding up stuff. Therefore you need to call the T's
    destructor.
    , Nov 16, 2006
    #7
  8. Dilip wrote:
    > The subject is a bit misleading because I really can't figure out what
    > the following code snippet is doing and would appreciate any help in
    > deciphering it.
    >
    > I mean I can understand code-wise what is going on but I am missing the
    > larger picture.
    >
    > In what circumstances would I use this?
    >
    > template<typename T>
    > class Globals
    > {
    > public:
    > Globals()
    > {
    > if (impl == NULL)
    > {
    > init();
    > }
    > }
    > T* operator->() const
    > {
    > return impl;
    > }
    > private:
    > static T* impl;
    > static void init()
    > {
    > static union
    > {
    > char buf[ sizeof( T ) ] ;
    > double dummyForAlignment ;
    > } data ;
    > impl = new (data.buf) T;
    > }
    > };
    >
    > template<typename T>
    > T* Globals<T>::impl = NULL;
    >


    I don't see what some of the constructs are used - why not use this:

    template<typename T>
    class Globals
    {
    public:
    T* operator->() const
    {
    static T * x = new T;
    return x;
    }
    };

    OK - so the memory for x is dynamically allocated in this example while
    the OP example is not.

    The use synax would be simply:

    Globals<Type>()->Member ...


    This below is probably equivalent to what was posted. (i.e. No dynamic
    memory allocation.)

    template<typename T>
    class Globals
    {
    public:

    T* operator->() const
    {
    static union
    {
    char buf[ sizeof( T ) ] ;
    double dummyForAlignment ;
    } data ;
    static T * x = new (data.buf) T;
    return x;
    }
    };
    Gianni Mariani, Nov 16, 2006
    #8
  9. wrote:
    > Gianni Mariani wrote:
    >> red floyd wrote:
    >>
    >>> // missing destructor
    >>> // this is necessary in case T manages some kind of resource.
    >>> ~Globals()
    >>> {
    >>> impl->T::~T();
    >>> }

    >> That's not clearly the right thing. impl is a static and so it does not
    >> necessarily need destruction from a memory leaking perspective.

    >
    > impl might be static....but it might be holding a pointer to an object
    > T which might be holding up stuff. Therefore you need to call the T's
    > destructor.
    >


    That would mean that impl will point to unallocated memory if used like
    this:


    Globals<Type>()->X(); // ok works first time
    Globals<Type>()->Y(); // oops - broken this time
    Gianni Mariani, Nov 16, 2006
    #9
  10. Dilip

    red floyd Guest

    Gianni Mariani wrote:
    > red floyd wrote:
    >
    >> // missing destructor
    >> // this is necessary in case T manages some kind of resource.
    >> ~Globals()
    >> {
    >> impl->T::~T();
    >> }

    >
    > That's not clearly the right thing. impl is a static and so it does not
    > necessarily need destruction from a memory leaking perspective.


    Who said anything about memory? T could be a type that manages a
    resource that could conceivably live beyond the lifetime of the program,
    e.g. a SysV Semaphore.

    When the program ends, Global<my_semaphore_manager_class> should bloody
    well call the destructor for the my_semaphore_manager_class object it
    created in impl, so that it can release the resource it holds.
    red floyd, Nov 16, 2006
    #10
  11. Dilip

    Guest

    Gianni Mariani wrote:
    > wrote:
    > > Gianni Mariani wrote:
    > >> red floyd wrote:
    > >>
    > >>> // missing destructor
    > >>> // this is necessary in case T manages some kind of resource.
    > >>> ~Globals()
    > >>> {
    > >>> impl->T::~T();
    > >>> }
    > >> That's not clearly the right thing. impl is a static and so it does not
    > >> necessarily need destruction from a memory leaking perspective.

    > >
    > > impl might be static....but it might be holding a pointer to an object
    > > T which might be holding up stuff. Therefore you need to call the T's
    > > destructor.
    > >

    >
    > That would mean that impl will point to unallocated memory if used like
    > this:
    >
    >
    > Globals<Type>()->X(); // ok works first time
    > Globals<Type>()->Y(); // oops - broken this time


    I dont understand this code above....
    But you need some kind of descurtion on a refcount basis....
    , Nov 16, 2006
    #11
  12. red floyd wrote:
    > Gianni Mariani wrote:
    >> red floyd wrote:
    >>
    >>> // missing destructor
    >>> // this is necessary in case T manages some kind of resource.
    >>> ~Globals()
    >>> {
    >>> impl->T::~T();
    >>> }

    >>
    >> That's not clearly the right thing. impl is a static and so it does
    >> not necessarily need destruction from a memory leaking perspective.

    >
    > Who said anything about memory? T could be a type that manages a
    > resource that could conceivably live beyond the lifetime of the program,
    > e.g. a SysV Semaphore.
    >
    > When the program ends, Global<my_semaphore_manager_class> should bloody
    > well call the destructor for the my_semaphore_manager_class object it
    > created in impl, so that it can release the resource it holds.
    >


    We have no idea what this is used for. Clearly there is no destructor
    and possibly for good reason. Saying it "should bloody well" have one
    is making many assumptions that may not be valid.
    Gianni Mariani, Nov 17, 2006
    #12
  13. Dilip

    red floyd Guest

    Gianni Mariani wrote:
    > red floyd wrote:
    >> Gianni Mariani wrote:
    >>> red floyd wrote:
    >>>
    >>>> // missing destructor
    >>>> // this is necessary in case T manages some kind of resource.
    >>>> ~Globals()
    >>>> {
    >>>> impl->T::~T();
    >>>> }
    >>>
    >>> That's not clearly the right thing. impl is a static and so it does
    >>> not necessarily need destruction from a memory leaking perspective.

    >>
    >> Who said anything about memory? T could be a type that manages a
    >> resource that could conceivably live beyond the lifetime of the
    >> program, e.g. a SysV Semaphore.
    >>
    >> When the program ends, Global<my_semaphore_manager_class> should
    >> bloody well call the destructor for the my_semaphore_manager_class
    >> object it created in impl, so that it can release the resource it holds.
    >>

    >
    > We have no idea what this is used for. Clearly there is no destructor
    > and possibly for good reason. Saying it "should bloody well" have one
    > is making many assumptions that may not be valid.
    >


    Exactly. We have no idea what this is used for. Therefore is should
    behave properly in the case where the template parameter has a
    non-trivial destructor.
    red floyd, Nov 17, 2006
    #13
  14. red floyd wrote:
    ....
    > Exactly. We have no idea what this is used for. Therefore is should
    > behave properly in the case where the template parameter has a
    > non-trivial destructor.


    Can you make a logical argument why is "requires" a destructor making no
    assumptions ?
    Gianni Mariani, Nov 17, 2006
    #14
  15. Dilip

    red floyd Guest

    Gianni Mariani wrote:
    > red floyd wrote:
    > ...
    >> Exactly. We have no idea what this is used for. Therefore is should
    >> behave properly in the case where the template parameter has a
    >> non-trivial destructor.

    >
    > Can you make a logical argument why is "requires" a destructor making no
    > assumptions ?


    Because if T has a trivial destructor, then no harm is done by invoking
    its destructor. If T has a non-trivial destructor, then failure to
    invoke it can cause problems. Therefore, the destructor should be invoked.
    red floyd, Nov 17, 2006
    #15
  16. red floyd wrote:
    ....
    >
    > Because if T has a trivial destructor, then no harm is done by invoking
    > its destructor.


    I don't think "no harm" is entirely clear. I'm not suggesting this is
    good code, but there have been badly designed systems where I have
    needed to not call the destructor on system exit because bad things
    would in fact happen. Hence, as a generalization, I don't think "no
    harm" is supportable.

    > ... If T has a non-trivial destructor, then failure to
    > invoke it can cause problems. Therefore, the destructor should be invoked.


    Not all non-trivial destructors (for globals) need to be called. The
    operating system does a much more efficient cleanup on exit than a

    e.g. this code (brain dump - not checked)

    #include <vector>


    std::vector<int> * foo = new std::vector<int>(10000000);

    int main()
    {
    }

    will not delete the object pointed to by foo, however on all popular
    operating systems (I don't talk about embedded ones), this code is well
    behaved.

    In general, you can't really bargain on global destructors being called
    anyway because there are ways of termination that do not call the final
    cleanup routines (memory fault, power failure, failure to catch
    exception, memory depletion, unresovable page faults etc just to mention
    some). So if your code truly depends on global destructors to be
    called, you're in for trouble.

    For example, if your code uses temporary files, you may want to "lock"
    the file and on startup, cleanup all unlocked temporary files. You
    could do the same for all persistent OS objects.

    So the point is, your generalization is clearly incorrect. While it is
    "preferable" to have destructors of global objects called, it is
    certainly not required all the time and it certainly cannot be depended
    on all the time.

    All generalizations are wrong.
    Gianni Mariani, Nov 17, 2006
    #16
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. John Harrison

    using templates in templates

    John Harrison, Jul 31, 2003, in forum: C++
    Replies:
    8
    Views:
    380
    Torsten Curdt
    Jul 31, 2003
  2. JKop
    Replies:
    3
    Views:
    468
  3. JKop
    Replies:
    10
    Views:
    941
  4. recover
    Replies:
    2
    Views:
    800
    recover
    Jul 25, 2006
  5. aaragon
    Replies:
    2
    Views:
    615
    James Kanze
    Nov 2, 2008
Loading...

Share This Page