tags:

views:

1628

answers:

7

Recently I got question on implement Singleton but abstract base class involved. Suppose we have class hierarchy like this:

class IFoo {...}; // it's ABC
class Foo : public IFoo {...};

we have singleton class defined as follows:

template <typename T>
class Singleton
{
public:
static T* Instance() {
   if (m_instance == NULL) {
      m_instance = new T();
   }
   return m_instance;
}
private:
static T* m_instance;
};

So if I want to use like following: IFoo::Instance()->foo(); what should I do?

If I do this: class IFoo : public Singleton<IFoo> {...}; it won't work since Singleton will call IFoo's ctor but IFoo is a ABC so can not be created.

And this: class Foo : public IFoo, public Singleton<Foo> {...}; can't work too, because this way class IFoo doesn't have the interface for method Instance(), so the call IFoo::Instance() will fail.

Any ideas?

+4  A: 

You can't do this. IFoo is an interface, by design and definition. The number of instances is therefore 0. On the other hand, the definition of a singleton class is that you have 1 instance. 0 != 1.

MSalters
+1  A: 

You'd want to use something like

IFoo my_foo = Singleton<Foo>::Instance();
my_foo->foo();

Basically you'll have to instantiate the template Singleton using a concrete class (in this case, your class Foo) and given that your Foo derives from IFoo you can refer to it through a base pointer. You cannot directly instantiate a template using an incomplete or abstract class.

Timo Geusch
A: 

You can always do something like this:

class IFoo {};
class Foo : public IFoo {};

template <typename T>
class Singleton
{
    // ..
};

typedef Singleton<Foo> FooSingleton;

int main()
{
    FooSingleton::Instance()->foo();

    return 0;
}
Magnus Skog
+2  A: 

The annoying meta-answer is, "why are you using a singleton?" I have yet to find a situation where you really need to use it. IMHO it's drawbacks outweigh it's advantages, in real life situations that is.

Using something like 'boost::noncopyable' might be what you are after.

See this post for more info

Chris Huang-Leaver
I agree with your point. But you still need to learn how to do it (and do it correctly) just so you know the problems it adds (tight coupling).
Martin York
A: 

Look at it like this: There is nothing in your program that would tell the compiler which implementation of the IFoo interface it should be instantiating. Remember, there could be other implementations besides Foo.

If you want to use a class via an interface and define which actual implementation shall be used somewhere else, take a look at the Abstract Factory pattern.

Jan
A: 

I had to do something similar to add unit tests to some legacy code. I had to replace an existing singleton which used a template. I gave two parameters to the singleton template, the first is the interface the second is the implementation.

However I also had to add a setTestInstance method to enable the unit tests override the instance at runtime.

template <typename IfaceT, typename ImplT>
class Singleton
{
public:
   static IfaceT* Instance() {
      if (m_instance == NULL) {
         m_instance = new ImplT();
      }
      return m_instance;
   }

   // Only used for unit tests 
   // Takes ownership of instance
   static void setTestInstance(IfaceT* instace) {
      m_instance = instance;
   }
private:
   static IfaceT * m_instance;
};

In this case setTestInstance should use a std::auto_ptr and m_instance should be a boost::scoped_ptr. To avoid memory leaks.

iain
A: 

I think the best solution would be to introduce a factory class or method here. Just imagine the following:

struct FooCreator
{
  typedef IFoo*     result_type;

  result_type operator()()const
  {
     return new Foo;
  }
};

template<class Factory>
struct Singleton
{

  static typename Factory::result_type instance()
  {
    if(instance_==typename Factory::result_type())
      instance_ = Factory()();
    return instance_;
  } 

private:
  Singleton(){};

  static typename Factory::result_type instance_;
};

template<class F>
typename F::result_type Singleton<F>::instance_ = typename F::result_type();

Best Regards,
Ovanes

ovanes