2

First I'll write example to properly address the question.

First of all, I'll declare template to be used to create singleton object (not auto-created): singleton_base.h

template <class Derived>
class SingletonBase
{
  public:
    static Derived* instance() { assert(s_instance); return dynamic_cast<Derived*>(s_instance); }
  protected:
    SingletonBase() { assert(s_instance==0); s_instance=this; }
    virtual ~SingletonBase() { assert(s_instance); s_instance=0; }
  private:
    static SingletonBase* s_instance;
};

template <class Derived>
SingletonBase<Derived>* SingletonBase<Derived>::s_instance = 0;

Now we can declare any class that derived from template, and each derived class should have its own s_instance. For example:

child1.h

class Child1 : public SingletonBase<Child1>
{
  ...
  void doSomething();
  static void staticInvokeOne();
};

child2.h

class Child2 : public SingletonBase<Child2>
{
  ...
  void doSomethingElse();
  static void staticInvokeBoth();
};

I also have Child's implementation in child1.cpp and child2.cpp respectively.

child1.cpp

void Child1::staticInvokeOne()
{
  instance()->doSomething();
}

child2.cpp

void Child2::staticInvokeBoth()
{
  Child1::instance()->doSomething();
  instance()->doSomethingElse();
}

Now I have Child1 and Child2 having their own s_instance and they will point to the only instance of that class at given moment.

The question is about storage for this static data member s_instance. Unlike with regular static data members I have not specified where it should be allocated. I would, of course, like to have SingletonBase<Child1>::s_instance and SingletonBase<Child2>::s_instance in child1.o and child2.o respectively, but is that something I can expect or enforce?

The issue gets more complicated if I put Child1 and Child2 into two distinct libraries - lib1 and lib2. Inside Child2::staticInvokeBoth() there is a call to Child1::instance(). As far as I understand, default gcc's behaviour is to generate a copy of SingletonBase<Child1>::s_instance in each compilation unit, thus one copy will be emitted in lib2.

Will it also generate a copy of SingletonBase<Child1>::s_instance in lib2? Definitely, one copy of SingletonBase<Child1>::s_instance should be in lib1. If those two libraries are later used together in one application, can I be sure that there is only one instance of SingletonBase<Child1>::s_instance and that both Child1::staticInvokeOne() and Child2::staticInvokeBoth() are using it?

Is it generally safe to use this approach with statics wrapped in template, or are there any drawbacks?

Thank you in advance!

nyrl
  • 769
  • 5
  • 15

2 Answers2

5

The answer to this is the same as with any other template-based or inline function - the only difference is in this case the variable ends up being marked for placement in a read-write section.

In most compilers, the compiler will instantiate any needed template functions and static member variables in every compilation unit they are referenced in. The compiler will also mark these as 'weak symbols'; this means that, at the final link phase, the linker will choose one of the emitted copies arbitrarily to go into the final executable.

Note, however, that the weak symbol consolidation process is usually done at the static linking stage. The dynamic linker will not do this for you. As such, you should avoid having mutable (read-write) template static data members in shared libraries. Also avoid address comparisons on read-only template static data members across shared libraries (including RTTI data).

And keep in mind in general with shared libraries that almost any change in template definitions that cross the ABI boundrary will break your ABI - so it's probably best to avoid templates entirely in shared library APIs!

bdonlan
  • 224,562
  • 31
  • 268
  • 324
  • Hm... Does it also apply to shared libraries? Will those names be resolved at runtime by dynamic linker (assuming linux)? Can it be mislead by DT_SYMBOLIC? – nyrl Feb 07 '11 at 17:27
  • Shared libraries can be broken, badly, by this stuff. I've been bitten by issues with this stuff before across shared libs; usually the weak symbols are resolved at final link time, and can no longer be merged at dynamic link time. Stick with static libs, or don't use static members in template types at all. – bdonlan Feb 07 '11 at 17:35
0

Though I may misunderstand your question, if storage in your question means definition, my answer might apply.
As for one definition rule, 3.2 p5 of the standard says:

There can be more than one definition of ... static data member of a class template ... in a program provided that each definition appears in a different translation unit,

and

If the definitions of D satisfy all these requirements, then the program shall behave as if there were a single definition of D.

There are some requirements for this rule. In this question's case, since the variable is initialized by an integral constant 0, the requirements are satisfied.

Ise Wisteria
  • 11,259
  • 2
  • 43
  • 26