OrenIshShalom
OrenIshShalom

Reputation: 7162

C++ Meyer's Singleton with arguments

Is it possible to define a Meyer's singleton (like this one) with arguments?

I know it is possible with the GOF style singleton (like here),

but I can't seem to make it work with Meyer's singletons:

// ...
public:

    static S& getInstance()
    {
        static S instance; // no way to pass arguments here ...
        return instance;
    }

EDIT:

I want a single Init function, and multiple getInstance. So a typical usage is something like:

S::Init(5, 6.4);
foo(S::getInstance());
bar(S::getInstance());

Upvotes: 5

Views: 1989

Answers (2)

Nikos C.
Nikos C.

Reputation: 51910

You can just store the initialization parameters in statics. Example:

class S {
public:
    static void Init(int i)
    {
        i_ = i;
        initialized_ = true;
    }

    static S& getInstance()
    {
        if (!initialized_) {
            throw SomeException;
        }
        static S instance(i_);
        return instance;
    }

private:
    S(int) { }

    static int i_;
    static bool initialized_;
};

Remember to actually define the statics in the implementation (.cpp) file:

int S::i_ = 0;
bool S::initialized_ = false;

Obviously you could use Meyer singletons as well for these, but since they're built-in types and do not depend on other data, you wouldn't really gain much.

Upvotes: 3

Alan Birtles
Alan Birtles

Reputation: 36488

You could do something like this:

class Singleton
{
private:
  static std::unique_ptr<Singleton>& getObject()
  {
    static std::unique_ptr<Singleton> instance;
    return instance;
  }

  Singleton(int foo);

public:
  static void Init(int foo)
  {
    auto& instance = getObject();
    if (instance) throw std::runtime_error("aleady inited");
    instance.reset(new Singleton(foo));
  }

  static Singleton& getInstance()
  {
    auto& instance = getObject();
    if (!instance) throw std::runtime_error("not inited");    
    return *instance;
  }
};

Note that this isn't thread safe and will have undefined behaviour if multiple threads call Init or a thread calls getInstance whilst another is calling Init.

If your parameters could be replaced by template arguments then you could do it this way instead:

template <int foo>
class SingletonImpl
{
private:
  SingletonImpl(int f);

public:
  static SingletonImpl<foo>& getInstance()
  {
    static SingletonImpl<foo> instance(foo);
    return instance;
  }
};

using Singleton = SingletonImpl<10>;

The best solution is probably to separate initialisation and construction:

class Singleton
{
private:
  std::atomic<bool> initialised;
  Singleton()
  : initialised(false)
  {
  }

  Singleton& instanceImpl()
  {
    static Singleton singleton;
    return singleton;
  }

public:
  void Init(int foo)
  {
    auto& instance = instanceImpl();
    if (instance.initialised) throw std::runtime_error("already inited");
    instance.initialised = true;
  }

  Singleton& getInstance()
  {
    auto& instance = instanceImpl();
    if (!instance.initialised) throw std::runtime_error("not inited");
    return instance;
  }
};

Upvotes: -1

Related Questions