Sandro Massa
Sandro Massa

Reputation: 108

C++ map of string to custom class with generics

I have tried for the past days to solve this issue I have with C++. It may be trivial, but I could not find a solution and searching on the Internet got me nowhere, so I'll ask here.

I have a C++ wrapper Singleton class, a superclass and a number of subclasses. I need the the instances of the subclasses to be singletons, and so I used the solution proposed here as it is the one fitting my needs the most. I need a map to act as a register from a string to the correct subclass. To make it concrete, here is the code:

// The Singleton class
template <typename T>
class Singleton
{
public:
    static T* Instance()
    {
        if (s_instance == NULL) { s_instance = new T(); }
        return s_instance;
    }
protected:
    static T* s_instance;
}

// Superclass
class MySuperclass
{
public:
    inline LPCSTR GetName() { return m_name; }
    
    virtual ~MySuperclass() { }
protected:
    LPCSTR m_name;
    
    MySuperclass(LPCSTR name) { m_name = name; }
}

// Example subclass
class MySubclass : public MySuperclass
{
    // subclass stuff
}

Now, I have tried a number of things, let me show all that I have tried:

// Where the problems begin
class MyRegister
{
public:
    static void Register()
    {
        Singleton<MySubclass> mySubclassSingleton;
        LPCSTR name = Singleton<MySubclass>::Instance()->GetName();
        s_classes.insert(std::make_pair(name, mySubclassSingleton));
    }
private:
    static std::unordered_map<LPCSTR, Singleton<MySuperclass>> s_classes;
}

This is the version I'm stuck with and it gives an error on the insert saying:

E0304 no instance of overloaded function "std::map<_Kty, _Ty, _Pr, _Alloc>::insert [with _Kty=LPCSTR, _Ty=Singleton<MySuperclass>, _Pr=std::less<LPCSTR>, _Alloc=std::allocator<std::pair<const LPCSTR, Singleton<MySuperclass>>>]" matches the argument list

I have tried using std::pair instead of std::make_pair, changed the definition of the map to:

template <class T : public MySuperclass>
std::unordered_map s_classes<LPCSTR, Singleton<T>> s_classes;

But to no avail, as the first resulted in the same error (also using the [] operator gave me issues with [name] as no operator matches these operands) and the second results in type errors.

As it stands, I need the classes to be singletons, and to ensure that they are singletons, and I need a register that links a unique string identifying the class to its singleton. Can anyone either explain why this is impossible or if there is something like Java's <? extends MySuperclass> in C++ that can work here?

Upvotes: 1

Views: 270

Answers (1)

Jan Schultke
Jan Schultke

Reputation: 39648

template <class T : public MySuperclass>
std::unordered_map s_classes<std::string, Singleton<T>> s_classes;

is not valid C++. But you could simply make a std::unordered_map<std::string, MySuperclass*> s_classes. It's not necessary for our lookup map to know about the Singleton pattern, it just needs to map onto a pointer to the corresponding subclass. We must use std::string or std::wstring as a key, because otherwise pointer comparison would be used between keys, not string comparison. Alternatively, we could provide a custom comparison operator.

The Singleton itself can also be simplified. static local variables are initialized upon the first use of the function, which is exactly the behavior that we want.

template <typename T>
class Singleton
{
    static T *Instance()
    {
        static T instance{};
        return &instance;
    }
}

Our previous use of new would have been a memory leak anyways, since delete was never called.

Now, we can also update or MyRegister accordingly.

class MyRegister
{
public:
    static void Register()
    {
        MySubclass *instance = Singleton<MySubclass>::instance();
        s_classes.emplace(instance->GetName(), instance);
    }
private:
    static std::unordered_map<std::string, MySuperclass*> s_classes;
}

Upvotes: 1

Related Questions