Gerastios
Gerastios

Reputation: 15

dynamic_cast from non-template class to template subclass

I have a non-template class NC and a derived template class TC. I want to cast a pointer to NC, which is potentially a pointer to a TC instance, to a TC pointer. The actual types of the template are limited to e.g. bool, int, and string.

class NC {
...
}

template <typename T>
class TC: public NC {
private:
    T value;
public:
    ...
    void setValue(T value) {
        this->value = value;
    }
}

class UserValueProvider {
public:
    int getValue() const { return 5; }
    bool getValue() const { return true; }
    string getValue() const { return "foobar"; }
}

void setUserValue(UserValueProvider *uvp, NC *obj) {
    auto tobj = dynamic_cast< ? >(obj);        // what goes here?
    if(tobj)
        tobj->setValue(uvp->getValue());
}

The obvious solution would be to perform 3 dynamic casts (for int, bool, and string) and call the setValue of the specialized instance. Yet I wonder whether there might be another solution, for the more specializations are possible the more dynamic casts would be needed and it would be more likely to forget one specialization.

Upvotes: 0

Views: 271

Answers (1)

Quentin
Quentin

Reputation: 63134

One solution here is to flip the call around, and use a virtual function. But first, let's rewrite UserValueProvider::getValue, as overloading solely on return types is forbidden.

class UserValueProvider {
    template <class T> struct tag { };
    int getValue(tag<int>) const { return 5; }
    bool getValue(tag<bool>) const { return true; }
    std::string getValue(tag<std::string>) const { return "foobar"; }

public:
    template <class T>
    T getValue() const {
        return getValue(tag<T>{});
    }
};

Now we can call uvp.getValue<T>() to get the corresponding value. Next, add a virtual function to NC and its derived classes:

class NC {
public:
    virtual void setValueFrom(UserValueProvider &uvp) = 0;
};

template <typename T>
class TC: public NC {
private:
    T value;
public:
    void setValue(T value) {
        this->value = value;
    }

    void setValueFrom(UserValueProvider &uvp) override {
        setValue(uvp.getValue<T>());
    }
};

And voilà, you can just pass your UserValueProvider to your type-erased NC and it will dispatch correctly.

void setUserValue(UserValueProvider *uvp, NC *obj) {
    obj->setValueFrom(*uvp);
}

See it live on Wandbox

Upvotes: 0

Related Questions