3

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());
OrenIshShalom
  • 5,974
  • 9
  • 37
  • 87
  • Exactly the same as in your linked answer. You give the initializer after `static S instance` as in `static S instance(...);` or `static S instance{...};` Could you clarify where the problem is? – walnut Mar 07 '20 at 06:54
  • I want to have an init call: `S::Init(8,7.25)` then use with `S::getInstance()` ... and guard each method with its assert: `Init` will be guarded with `instance == nullptr` and `getInstance` will be guarded with `instance != nullptr` ... – OrenIshShalom Mar 07 '20 at 06:58
  • You should just follow the single responsibility principle and move out lazy initialization code from instance fetching function. – user7860670 Mar 07 '20 at 07:15
  • @user7860670 could you please explain? – OrenIshShalom Mar 07 '20 at 09:06
  • Put your other requirements in the question itself, not in the comments. – Nikos C. Mar 07 '20 at 09:19
  • My point is that `Singleton` should have only `Get_Instance` and `Set_Instance` functions and should not be performing any kind of initialization at all. That is `Singleton` should have only a single responsibility - providing access to a single instance of a class. – user7860670 Mar 07 '20 at 10:02

2 Answers2

2

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.

OrenIshShalom
  • 5,974
  • 9
  • 37
  • 87
Nikos C.
  • 50,738
  • 9
  • 71
  • 96
-1

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;
  }
};
Alan Birtles
  • 32,622
  • 4
  • 31
  • 60