Question on c++ faq 21.2 Derived** -> Base**

  • Thread starter Filimon Roukoutakis
  • Start date
F

Filimon Roukoutakis

Dear all,
assuming that through a mechanism, for example reflexion, the Derived**
is known explicitly. Would it be legal (and "moral") to do this
conversion by a cast (probably reinterpret would work here)? The
conversion is done for this purpose: I have an
std::map<std::string, Base*>. I want to "associate" Derived* handles to
the stored Base* so when Base* in the map changes (ie points another
address), the Derived* handle outside of the map is updated
automatically to point to the new address also. Actually what I do for
the moment is that I make the association using eg
Map.Associate(std::string name, Derived*&), where Map is a class
containing the mentioned std::map but I guess this is related to the
title of the post (Is Derived*& -> Base*& moral?). This is the only
reason for the conversion, the Derived objects are to be handled only by
the Derived* pointers. Could there be a better solution that I fail to
see? I need to use a polymorphic container because I have several
Derived* types. Thanks,

filimon
 
J

John Harrison

Filimon said:
Dear all,
assuming that through a mechanism, for example reflexion, the Derived**
is known explicitly. Would it be legal (and "moral") to do this
conversion by a cast (probably reinterpret would work here)? The
conversion is done for this purpose: I have an
std::map<std::string, Base*>. I want to "associate" Derived* handles to
the stored Base* so when Base* in the map changes (ie points another
address), the Derived* handle outside of the map is updated
automatically to point to the new address also. Actually what I do for
the moment is that I make the association using eg
Map.Associate(std::string name, Derived*&), where Map is a class
containing the mentioned std::map but I guess this is related to the
title of the post (Is Derived*& -> Base*& moral?). This is the only
reason for the conversion, the Derived objects are to be handled only by
the Derived* pointers. Could there be a better solution that I fail to
see? I need to use a polymorphic container because I have several
Derived* types. Thanks,

filimon

Well using reinterpret_cast would work, but the danger is simple. There
is no guarantee in this design that when you change the Base pointer
that you don't change it to point to an object of a different type,
therefore you run the risk of ending up with Derived1* pointing at an
object of type Derived2.

Whether this risk is worth running or whether you can't smarten up the
design somehow so it's intrinsically impossible is not a question I can
answer for you. But personally I would try and improve the design. Why
do you need to handle Derived pointers at all? Can't you use Base
pointers thoughtout. If you really need a Derived pointer then use
dynamic_cast to obtain it when you need it, rather than storing Derived
pointers in your data structures.

john
 
F

Filimon Roukoutakis

John said:
Well using reinterpret_cast would work, but the danger is simple. There
is no guarantee in this design that when you change the Base pointer
that you don't change it to point to an object of a different type,
therefore you run the risk of ending up with Derived1* pointing at an
object of type Derived2.

This probably won't be possible. The stored objects are updated by an
internal procedure that assigns always the same Derived* (by name) on
the std::map. The type of the Derived* is associated with the name at
compile time, essentially what is done is that my users have this interface:
Derived1* t1=Produce<Derived>(name)
Derived2* t2=Produce<Derived2>(name)
....
Produce stores the objects in std::map<std::string, Base*>, with
key_val=name
In this sense, the name (which is used for lookup in std::map) is
statically bound to the type.

In another process, after the objects get serialized/deserialized, my
users use either map lookup or the "handles" we discuss to access the
objects.
Whether this risk is worth running or whether you can't smarten up the
design somehow so it's intrinsically impossible is not a question I can
answer for you. But personally I would try and improve the design. Why
do you need to handle Derived pointers at all? Can't you use Base
pointers thoughtout. If you really need a Derived pointer then use
dynamic_cast to obtain it when you need it, rather than storing Derived
pointers in your data structures.

The reason for not using Base pointers is that my Derived types have
different member functions. The only reason that they inherit from Base
is that I want to keep them in the same std::map (any other
options/suggestion would be helpful). In addition, I want the user to be
able to perform operations directly with the Derived* handles without
having necessarily to lookup the std::map. I assume that the solution
you suggest for dynamic cast essentially implies
dynamic_cast<Derived>(std::map::Find(name)->second)->Call
This could potentially hurt performance (I am talking about several
thousands of lookups per second).
Other solution would be to put all possible members as pure virtuals in
Base, but I would like to avoid this design because it would mean
essentially either throwing an exception when I do not want to allow an
operation for eg Derived1 but I want it for Derived2 or putting out a
message "Do not use this method with Derived1". I do not find these
solutions very good.
My question boils down to the following: If I absolutely know the type
of the Derived* throughout my code, so essentially eliminating the
reason for using it the wrong way, are there any other dangerous issues
about this cast (ie could memory alignment problems or something else I
cannot imagine come into play)?

filimon
 
J

John Harrison

Filimon said:
This probably won't be possible. The stored objects are updated by an
internal procedure that assigns always the same Derived* (by name) on
the std::map. The type of the Derived* is associated with the name at
compile time, essentially what is done is that my users have this
interface:
Derived1* t1=Produce<Derived>(name)
Derived2* t2=Produce<Derived2>(name)
...
Produce stores the objects in std::map<std::string, Base*>, with
key_val=name
In this sense, the name (which is used for lookup in std::map) is
statically bound to the type.

In another process, after the objects get serialized/deserialized, my
users use either map lookup or the "handles" we discuss to access the
objects.



The reason for not using Base pointers is that my Derived types have
different member functions. The only reason that they inherit from Base
is that I want to keep them in the same std::map (any other
options/suggestion would be helpful).

Make a std::map of void* pointers. If your types are really unrelated
(as seems to be the case) then that would be more 'honest'.


In addition, I want the user to be
able to perform operations directly with the Derived* handles without
having necessarily to lookup the std::map. I assume that the solution
you suggest for dynamic cast essentially implies
dynamic_cast<Derived>(std::map::Find(name)->second)->Call
This could potentially hurt performance (I am talking about several
thousands of lookups per second).
Other solution would be to put all possible members as pure virtuals in
Base, but I would like to avoid this design because it would mean
essentially either throwing an exception when I do not want to allow an
operation for eg Derived1 but I want it for Derived2 or putting out a
message "Do not use this method with Derived1". I do not find these
solutions very good.
My question boils down to the following: If I absolutely know the type
of the Derived* throughout my code, so essentially eliminating the
reason for using it the wrong way, are there any other dangerous issues
about this cast (ie could memory alignment problems or something else I
cannot imagine come into play)?

No I don't see any problems. Multiple inheritence in one of your derived
classes could be a problem, but you can avoid even that by using void*
instead of Base*.

john
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top