RE: Question about static class objects

From: Philippe Canal <pcanal_at_fnal.gov>
Date: Thu, 29 May 2008 09:45:35 -0500


Hi Christian,

In your singleton example, there is no partical different between:

        class Singleton 
        {
        public:
          Singleton& Instance() { 
            ...
            return *fgInstance;
          }
        private:
          static Singleton* fgInstance; // Only a _declaration_!
          ...
        };

and

        class Singleton 
        {
        public:
          Singleton& Instance() { 
            static Singleton* fgInstance; // Only a _declaration_!
            ...
            return *fgInstance;
          }
        private:
          ...
        };

is there? (In both case fgInstance is only used via the Instance method!

> Could you explain why you believe a static function variable
> is better style than a class variable?

There is 2 reason

  1. order of initialization
  2. guarantee that _no_ code whatsoever can access the variable without going to a method.

The order of initialization matters during the load shared time of the shared library.

The compiler/linker are free to 'run' the initialization the global variables at any time
during the loading of the library.

On the other hand, a static function member must be initialization at the time the function
is being entered.

Of course, this does not matter of your fgInstance since it is a pointer and the pointer
default to 0. However it does matter for your fgLock Mutex.

For example if somebody define (at global scope)

// File user.cxx

    int myvalue = Singleton::Instance()->GetValue();

While loading user.o, the linker compiler must initialize 'myvalue'. However, if you singleton.cxx is part of the same library, it can 'run' this initialization __before__ or __after__ it run the constructor for Mutex; i.e. running the initialization for

        Mutex Singleton::fgLock;

So when executing Instance in part the line:

       LockGuard g(fgLock);

You have _NO_ guarantee that fgLock is a valid initialized object ...

Cheers,
Philippe.

-----Original Message-----
From: Christian Holm Christensen [mailto:cholm_at_nbi.dk] Sent: Thursday, May 29, 2008 9:33 AM
To: Philippe Canal
Cc: 'Tom Roberts'; 'ROOT Talk'
Subject: RE: [ROOT] Question about static class objects

Hi all.

On Tue, 2008-05-27 at 13:59 -0500, Philippe Canal wrote:
> Hi Tom,
>
> > Apparently I simplified it too much, and you took issue with C++ style
> > rather than the actual problem (though your workaround does work).
>
> Actually I did not take issue with C++ style [really :) I did not],
> I was merely mentioning thatthe only working work-around within CINT
> just happens to be better style :)

Uhm, in my mind, a static member of a class (or, as it's called in Java - a class variable) is far better style than a static variable in a function. Could you explain why you believe a static function variable is better style than a class variable?

Also, the use class variables is a very common idiom - especially for things like singletons:

    struct Mutex
    {

      Mutex() { pthread_mutex_init(&fMutex); }
      ~Mutex() { pthread_mutex_destroy(&fMutex); }
      void Lock() { pthread_mutex_lock(&fMutex); }
      void Unlock() { pthread_mutex_unlock(&fMutex); }
    private:
      Mutex(const Mutex& mutex) {}
      Mutex& operator=(const Mutex&) { return &this; }
      pthread_mutex_t fMutex;

    };

    struct LockGuard
    {

       LockGuard(Mutex& m) : fMutex(m) { fMutex.Lock(); }
       ~LockGuard() { fMutex.Unlock(); }
    private:
       Mutex fMutex;

    };
        class Singleton 
        {
        public:
          Singleton& Instance() { 
            if (!fgInstance) { 
              LockGuard g(fgLock);
              if (!fgInstance) // Prevent race-conditions
                fgInstance = new Singleton;
            }
            return *fgInstance;
          }
          void DoSomething() {}
        private:
          Singleton() {}
          Singleton(const Singleton&) {}
          Singleton& operator=(const Singleton&) { return *this; }
          Singleton* fgInstance; // Only a _declaration_!
          Mutex      fgLock;     ?// Only a _declaration_!
        };
        
        // _Definition_ of class variables
        Singleton* Singleton::fgInstance = 0;
        Mutex      Singleton::fgLock();
        

> (i.e. it is not important compared to the fact that one work and one does
> not)

...
> > Making list not be a class variable, but rather a local static object in

> > an accessor function (as you suggest), complicates the code more than I
> > want to do (list is used several dozen times, and there are three such
> > TList-s).
>

...
> > Note that making try() not inline (separating its definition from the
> > class declaration) did not help.

"try" is a very bad name for an identifier since it's a keyword in C++. Why not call it "give_it_a_shot" instead?

> I
> PS.
> > The above code is straightforward, reasonable, and standard C++,
> > and it ought to work; I'm astounded this has not come up before --
> > this is a standard way to keep track of all instances of class Zap.

A better way, would probably be to have an external singleton object that you "register" objects of class Zap with - it separates the functionality of the Zap objects from the book-keeping of the objects.

> Well :), even in C++, this has many caveat, you have no control/guarantee
> of when the class static data member will be initialized and using this
> member during the shared library loading is __in C++__ undefined
> behavior.

Actually, you have perfect control of when a class variable is initialised - especially if you use the Singleton pattern. Since you must _define_ the class variable somewhere, you know exactly which value it has on start of execution/load/what not.

> And, yes, this does not apply to your specific case and CINT ought to
> function properly in this case; it does not and it is hard to fix :(

Replacing your stack object list with a heap object surprisingly works:

  struct Zap
  {
    static TList* list;
  };
  TList* Zap::list = new TList();

  void foo()
  {

     Zap::list->Clear();
  }

which seems to indicate that CINTs problem is really that it doesn't understand the _definition_ of the heap variable.

Yours,

-- 
 ___  |  Christian Holm Christensen 
  |_| |  -------------------------------------------------------------
    | |  Address: Sankt Hansgade 23, 1. th.  Phone:  (+45) 35 35 96 91
     _|           DK-2200 Copenhagen N       Cell:   (+45) 24 61 85 91
    _|            Denmark                    Office: (+45) 353  25 447
 ____|   Email:   cholm_at_nbi.dk               Web:    www.nbi.dk/~cholm
 | |
Received on Thu May 29 2008 - 16:45:45 CEST

This archive was generated by hypermail 2.2.0 : Thu May 29 2008 - 23:50:01 CEST