Question

I've been wondering how to pass argument to a singleton contructor. I already know how to do a singleton, but I've been unlucky to find a way to do it.

Here is my code (part of it).

Questionnary* Questionnary::getInstance(){

    static Questionnary *questionnary = NULL;

    if(questionnary == NULL){
        cout << "Object created";
        questionnary = new Questionnary();

    }
    else if(questionnary != NULL){
        cout << "Object exist";
    }

    return questionnary;
}

Questionnary::Questionnary(){
    cout << "I am an object";
}

//This is want i want to acheive
Questionnary::Questionnary(string name){
    cout << "My name is << name;
}

Many thanks in advance

(BTW i know how and why a singleton is bad)

Was it helpful?

Solution

You don't need to allocate the instance of singleton dynamically. It could look the following way (this is sometimes called "lazy loading singleton" ~ the instance is created late & kinda "automatically"):

#include <iostream>
#include <string>

class Questionnary
{
private:
    // constructor taking string:
    Questionnary(const std::string& name) : name_(name) { }
public:
    static Questionnary& getInstance(const std::string& name)
    {
        static Questionnary q(name);
        std::cout << "My name is: " << q.name_ << std::endl;
        return q;
    }
private:
    std::string name_;
};

int main() {
    Questionnary::getInstance("Josh");
    Questionnary::getInstance("Harry");
}

output:

My name is: Josh
My name is: Josh

Note that constructor will be called only once right when the getInstance is called for the first time.

OTHER TIPS

Let me extend Martin York's answer for your use case. I recommend using pointer for argument(s) in this particular situation, as we make use of its inherent property, that it can be "empty".

class Questionnary
{
  std::string _str;

  static Questionnary& getInstanceImpl(std::string* const s = nullptr)
  {
    static Questionnary instance{ s };
    return instance;
  }

  Questionnary(std::string* const s)
    : _str{ s ? move(*s) : std::string{} } // employ move ctor
  {
    if (nullptr == s)
      throw std::runtime_error{ "Questionnary not initialized" };
  }

public:
  static Questionnary& getInstance()
  {
    return getInstanceImpl();
  }
  static void init(std::string s) // enable moving in
  {
    getInstanceImpl(&s);
  }

  Questionnary(Questionnary const&) = delete;
  void operator=(Questionnary const&) = delete;
};

I find this approach less confusing, as it let's you get the instance after first initialization without (anyway discarded) arguments:

// first init
Questionnary::init("my single Questionnary");

// later on ...
Questionnary& q = Questionnary::getInstance();

Edit: Removed argument from getInstance function and a few optimizations.

Have a method to create the instance to pass arguments to the constructor and you could assert in the getInstance() method if CreateInstance has not been called prior to calling it. Like:

class Questionnary
{
private:
    // constructor taking string:
    Questionnary(const std::string& name) : name_(name) 
    {
        std::cout << "My name is: " << q.name_ << std::endl; 
    }

    static Questionnary* m_instance;
public:
    static void createInstance(const std::string& name)
    {
        assert(!m_instance);
        m_instance = new Questionary(name);
    }

    static void destroyInstance()
    {
        assert(m_instance);
        delete m_instance;
    }

    static Questionnary* Questionnary::getInstance()
    {
        assert(m_instance);
        return m_instance;
    }
private:
    std::string name_;
};

My version using Modern C++ that wraps an existing type:

#ifndef SINGLETON_H
#define SINGLETON_H

template <typename C, typename ...Args>
class singleton
{
private:
  singleton() = default;
  static C* m_instance;

public:
  ~singleton()
  {
    delete m_instance;
    m_instance = nullptr;
  }
  static C& instance(Args...args)
  {
    if (m_instance == nullptr)
      m_instance = new C(args...);
    return *m_instance;
  }
};

template <typename C, typename ...Args>
C* singleton<C, Args...>::m_instance = nullptr;

#endif // SINGLETON_H

Here is what in looks like in a unit test:

  int &i = singleton<int, int>::instance(1);
  UTEST_CHECK(i == 1);

  tester1& t1 = singleton<tester1, int>::instance(1);
  UTEST_CHECK(t1.result() == 1);

  tester2& t2 = singleton<tester2, int, int>::instance(1, 2);
  UTEST_CHECK(t2.result() == 3);

The problem with this is that instance() requires arguments on each call but only uses them on the first (as noted above). Default arguments cannot be used generally. It may be better to use a create(Args...) method which must precede the call of instance() or throw an exception.

//! @file singleton.h
//!
//! @brief Variadic template to make a singleton out of an ordinary type.
//!
//! This template makes a singleton out of a type without a default
//! constructor.

#ifndef SINGLETON_H
#define SINGLETON_H

#include <stdexcept>

template <typename C, typename ...Args>
class singleton
{
private:
  singleton() = default;
  static C* m_instance;

public:
  singleton(const singleton&) = delete;
  singleton& operator=(const singleton&) = delete;
  singleton(singleton&&) = delete;
  singleton& operator=(singleton&&) = delete;

  ~singleton()
  {
    delete m_instance;
    m_instance = nullptr;
  }

  static C& create(Args...args)
  {
    if (m_instance != nullptr)
      {
    delete m_instance;
    m_instance = nullptr;
      }
    m_instance = new C(args...);
    return *m_instance;
  }

  static C& instance()
  {
    if (m_instance == nullptr)
      throw std::logic_error(
        "singleton<>::create(...) must precede singleton<>::instance()");
    return *m_instance;
  }
};

template <typename C, typename ...Args>
C* singleton<C, Args...>::m_instance = nullptr;

#endif // SINGLETON_H

and:

void
singleton_utest::test()
{
  try
    {
      singleton<int, int>::instance();
      UTEST_CHECK(false);
    }
  catch (std::logic_error& e)
    {
      UTEST_CHECK(true);
    }

  try
    {
      UTEST_CHECK((singleton<int, int>::create(1) == 1));
      UTEST_CHECK((singleton<int, int>::instance() == 1));
    }
  catch (...)
    {
      UTEST_CHECK(false);      
    }

  using stester0 = singleton<tester0>;

  try
    {
      stester0::instance();
      UTEST_CHECK(false);
    }
  catch (std::logic_error& e)
    {
      UTEST_CHECK(true);
    }

  try
    {
      UTEST_CHECK((stester0::create().result() == 0));
      UTEST_CHECK((stester0::instance().result() == 0));
    }
  catch (...)
    {
      UTEST_CHECK(false);      
    }

  using stester1 = singleton<tester1, int>;

  try
    {
      stester1::instance();
      UTEST_CHECK(false);
    }
  catch (std::logic_error& e)
    {
      UTEST_CHECK(true);
    }

  try
    {
      UTEST_CHECK((stester1::create(1).result() == 1));
      UTEST_CHECK((stester1::instance().result() == 1));
    }
  catch (...)
    {
      UTEST_CHECK(false);      
    }

  using stester2 = singleton<tester2, int, int>;

  try
    {
      stester2::instance();
      UTEST_CHECK(false);
    }
  catch (std::logic_error& e)
    {
      UTEST_CHECK(true);
    }

  try
    {
      UTEST_CHECK((stester2::create(1, 2).result() == 3));
      UTEST_CHECK((stester2::instance().result() == 3));
    }
  catch (...)
    {
      UTEST_CHECK(false);      
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top