Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

etl::singleton doesn't prevent multiple instances #998

Open
rolandreichweinbmw opened this issue Dec 18, 2024 · 0 comments
Open

etl::singleton doesn't prevent multiple instances #998

rolandreichweinbmw opened this issue Dec 18, 2024 · 0 comments

Comments

@rolandreichweinbmw
Copy link

rolandreichweinbmw commented Dec 18, 2024

The current implementation of etl::singleton doesn't prevent multiple instances of the class originally to be singleton'ed. See also the example at

https://www.etlcpp.com/singleton.html

One solution to this problem would be this draft (just in principle, will need to adjust to etl details):

namespace etl
{
//
// Base class for singletons.
// T:   Any type that wants to expose the instance() interface.
//
// This class is designed to work as a generic base class for any class that wants to
// provide a singleton interface. It'll also work for classes that do not have a
// default constructor.
//
// Usage example:
// 
// class Origin
// :   singleton<Origin>
// {
// public:
//     Origin(int x, int y)
//     :   singleton<Origin>(*this)
//     {}
//
//     int getX() const;
// } theOrigin(0, 0);
//
// int x = Origin::instance().getX();
//
//
// Note:
//
// It is important that a call to instance() will not create the instance of the class. It needs
// to be created by the user before calling instance(). This way, the user has better control
// over the instance lifetime instead of e.g. lazy initialization.
//
template<class T>
class singleton
{
protected:
    //
    // Constructs the instance of singleton.
    // theInstance Reference to T, which will be returned when instance() is called.
    //
    explicit singleton(T& theInstance)
    {
        assert(_self == nullptr); // make sure we only have one instance
        // theInstance does not have a longer lifetime as T inherits from singleton
        _self = &theInstance;
    }

    //
    // Removes the internal reference to the instance passed in the constructor.
    //
    ~singleton() { _self = nullptr; }

public:
    //
    // Returns a reference to the instance.
    //
    static T& instance()
    {
        assert(_self != nullptr);
        return *_self;
    }

    //
    // Returns whether an instance has been attached to singleton<T> or not.
    //
    static bool is_valid() { return (_self != nullptr); }

private:
    static T* _self;
};

// No violation of one definition rule as this is a class template
template<class T>
T* singleton<T>::_self = nullptr;

}

Admittedly, this breaks the current interface and usage pattern of the current ETL implementation. However, it might be worth it, considering the above described issue.

Instead of replacing current etl::singleton, the above solution could be provided as e.g. etl::singleton_base instead, or alternatively, it could be made switchable, similar to choosing between the different etl::bitset implementations.

What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant