Reputation: 1177
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
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
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