vendredi 28 juin 2019

Casting template class based on template argument type

Currently I store pointers of different types in a vector. To archive this, I implemented a class template "Store" which derives from a non class template "IStore". My vector finally stores pointers to "IStore". In code:

class IStore
{
public:
    IStore() = default;
    virtual ~IStore() = default;

    virtual void call() = 0;
    // ... other virtual methods
};

template<typename T>
class Store : public IStore
{
public:
    Store() = default;
    virtual ~Store() = default;

    virtual void call() override;
    // ... other virtual methods

private:
    T* m_object = nullptr;
}

And in my main class which holds the vector:

class Main
{
public:
    template<typename T>
    void registerObject(T* ptr);

    template<typename T>
    void callObjects();
    // ... other methods

private:
    std::vector<IStore*> m_storedObjects;
};

So far the current class structure. To describe the problem I need to introduce the following three example structs:

struct A {}
struct B : public A {}
struct C : {}

Other classes should call the Main::registerObject method with pointers to objects of A, B or C types. This method will then create a new Store<A>, Store<B> resp. Store<C> template class object and inserts this objects pointer to m_storedObjects.

Now the tricky part starts: The method Main::callObjects should be called by other classes with a template argument, such as Main::callObjects<B>(). This should iterate though m_storedObjects and call the IStore::call method for each object, which is of type B or which type B is derived from.

For example:

Main::registerObject<A>(obj1);
Main::registerObject<B>(obj2);
Main::registerObject<C>(obj3);
Main::callObjects<B>();

Should call obj1 and obj2 but not obj3, because C isn't B and B isn't derived from C.

My approaches in Main::callObjects were: 1. Perform dynamic_cast and check against nullptr like:

for(auto store : m_storedObjects)
{
    Store<T>* base = dynamic_cast<Store<T>*>(store);
    if(base)
    {
        // ...
    }
}

which will only work for the same classes, not derived classes, because Store<B> isn't derived from Store<A>. 2. To overwrite the cast operator in IStore and Store, such that I can specify Store should be castable when the template argument is castable. For example in Store:

template<typename C>
operator Store<C>*()
{
    if(std::is_convertible<T, C>::value)
    {
        return this;
    }
    else
    {
        return nullptr;
    }
}

But this method is never called.

Does anyone has a solution for this problem? Sorry for the long post, but I thought more code would be better to understand the problem. Thanks for your help anyway :)

Aucun commentaire:

Enregistrer un commentaire