0

I want to use a static member in a template class to instantiate a singleton object for each class that inherits from that template class.

Here is an example:

#include <iostream>
#include <vector>

struct X{

    static std::vector<X*>& getRegistry(){
         static std::vector<X*> registry;
         return registry;
    }

    X(){
        getRegistry().push_back(this); // Each X adds itself to the registry
    }
};

template<typename T>
struct Y : X{ 
private:   
   static T instance; // The per-type singleton
};

template<typename T>
T Y<T>::instance {};

The idea is that objects of type X enter themselves into a "registry" whenever they are created. Class Y<T> that is derived from X is a template class that should instantiate a static singleton object of each type that inherits from it (using the curiously recurring template pattern).

The idea is that the "registry" will contain one object of each class that inherits from Y<T>. These classes should not have to do anything to get added to the registry; instead, simply inheriting from Y<T> should be enough to create a singleton object that is added to the registry.

I tried my code like this:

struct A : Y<A>{};
struct B : Y<B>{};
struct C : Y<C>{};

int main(){
    std::cout << "Number of objects in the registry: " << X::getRegistry().size() << std::endl;
    // Should print "3"
}

Fiddle link: http://ideone.com/aWDEg4

The desired behaviour should be that one A, one B, and one C, should be in the registry. But none of them is. The problem is that Y<T>::instance is not instantiated for any of the classes as it is not used in the code. However, I never want to use these fields, they are just their to instantiate the singleton. So is there a way to force the instantiation of the static field without having to add additional code to the deriving classes (i.e., A,B, and C)?

I know I can explicitly instantiate template class Y<A>;, but this would add additional code to the deriving class A.

gexicide
  • 38,535
  • 21
  • 92
  • 152
  • I bet your problem is related to using the `this` pointer from the `X` constructor. Within the constructor the `this` pointer is not guaranteed to be pointing to the completed derived object. – b4hand Nov 18 '14 at 16:44
  • 5
    you never create any instances of your structs, how else will the constructor run? – AlexanderBrevig Nov 18 '14 at 16:44
  • x vs y problem, maybe? – IdeaHat Nov 18 '14 at 16:46
  • **Hint:** `struct Xyz {};` doesn't run *any* constructor. `Xyz instance;` does! – Nawaz Nov 18 '14 at 16:48
  • @AlexanderBrevig: Well, there is a static instance in the class `Y`. If it would be instanciated, then this would be my instance. – gexicide Nov 18 '14 at 16:54
  • @Nawaz: I am aware of that. That is why there is a static member `X::instance`. – gexicide Nov 18 '14 at 16:54
  • @b4hand: The constructor is not even called, that is not the problem. – gexicide Nov 18 '14 at 16:55
  • The constructor of your static is not called until it's needed, try making instance public then put `(void*)&A::instance;` above your print. This is according to spec. – AlexanderBrevig Nov 18 '14 at 17:14
  • @AlexanderBrevig: I know, but this would require extra code for each derived class which is exactly the point I am trying to avoid here. – gexicide Nov 19 '14 at 10:20
  • You're fighting against the c++ spec and compiler optimizations. If you are hellbent on getting this to work, I'd use a macro for this. – AlexanderBrevig Nov 19 '14 at 12:47

2 Answers2

1

You could just create static instances:

struct A : Y<A>{};
struct B : Y<B>{};
struct C : Y<C>{};

static A a;
static B b;
static C c;

int main(){
    std::cout << "Number of objects in the registry: " << X::getRegistry().size() << std::endl; 
}

output:

Number of objects in the registry: 3
sjdowling
  • 2,994
  • 2
  • 21
  • 31
  • 1
    Yes, but this makes me add additional code for each derived class which I was trying to avoid with `Y`. With your approach, I could throw away class `Y` entirely. – gexicide Nov 18 '14 at 16:53
0

I don't think you can avoid involving the clients of Y, but you can make Y's constructor private forcing client involvement. For example, this works as you want:

template<typename T>
struct Y : X {
   Y(const Y<T> &other) {}
   static Y<T> create() { &instance; return Y<T>(); }
private:
   static T instance; // The per-type singleton
   Y() {}
};

template<typename T>
T Y<T>::instance {};

struct A : Y<A> { A() : Y(Y::create()) {} };
struct B : Y<B> { B() : Y(Y::create()) {} };
struct C : Y<C> { C() : Y(Y::create()) {} };

The client involvement is the invocation of Y::create which also happens to force the instantiation of the instance member. Granted, this is probably not what you wanted, but it does work. Note also, my previous comment about the this pointer will still apply even if you can add the static instances to your registry.

b4hand
  • 9,550
  • 4
  • 44
  • 49