Carles Araguz
Carles Araguz

Reputation: 1177

How to check the type of a template parameter for a polymorphic subclass

I have a class hierarchy in which I<T> and O<T> are a templated classes that extend B (regular class). I aggregates O by means of a pointer, like so:

class B { ... };
template <class T>
class O : public B
{
    T getValue() { return value; }
private:
    T value;
}
template <class T>
class I : public B
{
    T getValue() { return ptr->getValue(); }
    void setPtr(O<T>* po) { ptr = po; }
private:
    O<T>* ptr;
}

The code above is indeed simplified to show only its bare bones. "Connecting" I and O is done automatically although through user configuration parameters. Because of that, a Singleton table stores polymorphic B pointers to all I and O objects and performs the linking by calling I::setPtr as necessary. The problem though is that the user can actually mess up and confuse template types of I and O objects, e.g. user can ask to connect an I<int> with an O<float>. E.g.:

O<int>* oi = new O<int>();
O<float>* of = new O<float>(); 
I<int> ii;
ii.setPtr(oi);  /* This is OK. */
O<int>* oi_wrong = static_cast<O<int>*>((B*)of);  /* This happens. */
ii.setPtr(oi_wrong); /* This is bad. */

In order to prevent this sort of behaviour, I included a member variable in both I and O that stores typeid(T).name(), thus allowing to check whether the template parameter is actually the same.

Is there a better way?

EDIT: Would there be a better way to do the casting here? Is it not a good idea to store and work with polymorphic pointers in this case?

Upvotes: 2

Views: 213

Answers (2)

Vittorio Romeo
Vittorio Romeo

Reputation: 93264

There's really nothing much you can do if your user is going out of the way to misuse your interface - you should (reasonably) design your code without assuming that your user is going to use static_cast and reinterpret_cast liberally.

In your case, I wouldn't add any additional code/checks. Just assume that your user acts in a reasonable manner.


If you really want, you could use assert and dynamic_cast to check if the passed pointer is actually what you expected in DEBUG builds:

struct A { virtual ~A() { } };
struct B : A { };
struct C : A { };

void setPtr(A* po) 
{ 
    assert(dynamic_cast<C*>(po) != nullptr);
}

int main()
{
    B b;
    setPtr((C*)&b); // might trigger assertion
}

But I strongly suggest against it, as this is overkill.

Upvotes: 1

Jan Fischer
Jan Fischer

Reputation: 71

How about using a friend function for setPtr(), so the user is forced to input fitting types for I and O:

    class B { };
    template <typename T>
    class O : public B
    {
    public:
      T getValue() { return value; }
      void setPtr(I<T>& ref_i) { ref_i.ptr = this; };
    private:
      T value;
    };

    template <typename T>
    class I : public B
    {
    public:
      T getValue() { return ptr->getValue(); }
      friend void O::setPtr(I<T>& ref_i)
    private:
      O<T>* ptr;
    };


    int main()
    {
      O<int>* oi = new O<int>();
      O<float>* of = new O<float>();
      I<int> ii;
      of->setPtr(ii);  /* This fails. */
      O<int>* oi_wrong = static_cast<O<int>*>((B*)of);  /* This CAN                 happen. */
      oi->setPtr(ii); /* This is OK. */
    }

Upvotes: 0

Related Questions