Singletones, curiously repeating template patterns and forwarding constructor options

Well, I know that singletones should be avoided, however there are a few examples when they are really needed. Therefore, my solution implements them using CRTP (curiously repeating pattern) as follows:

#include <iostream> #include <utility> using namespace std; template<typename T> // Singleton policy class class Singleton { protected: Singleton() = default; ~Singleton() = default; Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; public: template<typename... Args> static T& getInstance(Args... args) // Singleton { // Guaranteed to be destroyed. // Instantiated on first use. // Thread safe in C++11 static T instance{std::forward<Args>(args)...}; return instance; } }; class Foo: public Singleton<Foo> { friend class Singleton<Foo>; Foo() { cout << "Constructing instance " << this <<" of Foo" << endl; } Foo(int x) { cout << "Constructing instance " << this <<" of Foo with argument x = "\ << x << endl; } ~Foo() { cout << "Destructing instance " << this << " of Foo" << endl; } public: // public }; int main() { Foo& rfoo = Foo::getInstance(); // default constructible // this should just return the instance // instead, it constructs another instance // because invokes an overloaded version of get_instance() Foo& rfoo1 = Foo::getInstance(1); // this is OK // calls the SAME overloaded version again, instance is static // so we get the same instance Foo& rfoo2 = Foo::getInstance(2); } 

As you can see, I admit the possibility of creating singletones from classes with overloaded / non-default constructors. But this happens and bites me, because, using the variable-template function get_instance() and passing the instance parameters through std::forward , the compiler generates an overload for each call with different types and, therefore, returns a new static instance for each overload. I would like to return one instance by reference, somehow, to all possible overloads, but I can’t understand how to do this. Any ideas how to do this? Thanks!

PS: I can implement a solution with pointers instead of links, but I prefer a reference solution because it is thread safe and more elegant in my opinion.

+6
source share
2 answers

Sorry, I finally had time. You can try the following:

 #include <iostream> #include <utility> #include <functional> using namespace std; template<typename T> // Singleton policy class class Singleton { protected: Singleton() = default; Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; virtual ~Singleton() = default; public: template<typename... Args> static T& getInstance(Args... args) // Singleton { cout << "getInstance called" << std::endl; //we pack our arguments in a T&() function... //the bind is there to avoid some gcc bug static auto onceFunction = std::bind( createInstanceInternal<Args...>, args... ); //and we apply it once... return apply( onceFunction ); } private: //This method has one instance per T //so the static reference should be initialized only once //so the function passed in is called only the first time static T& apply( const std::function<T&()>& function ) { static T& instanceRef = function(); return instanceRef; } //Internal creation function. We have to make sure it is called only once... template<typename... Args> static T& createInstanceInternal(Args... args) { static T instance{ std::forward<Args>(args)... }; return instance; } }; 

IdeOne Link:

http://ideone.com/Wh9cX9

Edit (thread safety issues and design issues):

As far as I know, this should be thread safe in C ++ 11. Both instances and instanceRef are static local variables and should only be thread safe initialized once. Depending on your compiler, it may be, however, that the safe initialization of a C ++ 11 stream is not implemented, as specified in the standard. You can explicitly apply synchronously as a temporary workaround if that is the case. In my opinion, it is still worried that client code might call getInstance with no parameters and with parameters. If the client code calls parameters, than, most likely, it has a wait / need for a singleton to be initialized with the given parameters. Providing multiple initialization capabilities will result in unexpected / unnatural behavior for client code. It may not be good. It should be nice if Foo has only one parameter. However, this would mean that you still have to pass some arguments to get the instance ... In the end, parameterizing a singleton getInstance will cause more problems than it solves. It was said that I simply could not solve the intellectual challenge ... :-).

+5
source

The following code works for me in a single threaded program. Not sure how it should be adapted for a multi-threaded program.

 template<typename T> // Singleton policy class class Singleton { protected: Singleton() = default; ~Singleton() = default; Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; public: struct PointerWrapper { T* instancePtr; PointerWrapper() : instancePtr(NULL) {} ~PointerWrapper() { delete instancePtr; } }; template<typename... Args> static T& getInstance(Args... args) // Singleton { if ( NULL == wrapper.instancePtr ) { wrapper.instancePtr = new T{std::forward<Args>(args)...}; } return *(wrapper.instancePtr); } static PointerWrapper wrapper; }; template <typename T> typename Singleton<T>::PointerWrapper Singleton<T>::wrapper; 
+1
source

Source: https://habr.com/ru/post/972865/


All Articles