Casting derived classes from void*

Started by
21 comments, last by owl 18 years ago
Quote:Original post by Xai
In C++ you pass around the base class pointer and use dynamic_cast to see if it is a derived class object of a certain type (if necessary - which often isn't once the design is cleaned up).


Even then, one could create an object base class from which all objects inherit. Or, better yet, if only a certain amount of classes may be passed as mentioned above, make all of them inherit, directly or indirectly, from a common base class which can then be used.

Also, don't forget:
class A { public: virtual ~A( ) { } };class B { public: virtual ~B( ) { } };// There are situations where this conversion is possible:// An object can exist that is both A and B.B* Frobnicate(A* a) {  return dynamic_cast<B*>(a);}
Advertisement
I said "In any heirarchy in C++ with a common base class you would NEVER use a void *."

read that sentence again ... and then read the line BELOW it in my original post ...

IF you already have a common base class, you should NOT be using void *. That's all I said, and it is indisputable. You may not loose much by using void *, but you gain absolutely nothing.

If you don't have a common base, then you use void* - that's what it is for - a universal pointer).

As for dynamic_cast, it is a type-safe cast, it sets the result to null if the object is not actually derived from the class given. But it only works on heirarchies of related (common base) objects.

so assume you have 5 classes, B, D1, D2, D3 and X - where B is the base class of D1, D2 and D3 and X is not related.

B* obj = ??
D2* objectD2 = dynamic_cast<D2*>(obj);

objectD2 will be null (0) if obj was NOT derived from class D2.

You cannot get this behavior using void * without your own custom type mapping code (why rewrite the standard).
Quote:Original post by Xai
IF you already have a common base class, you should NOT be using void *. That's all I said, and it is indisputable.


Alright. I'm not trying to dispute with you. I apreciate your help. I was just asking you the reason why things should be like you said, because I don't know it.

I've read that to use dynamic_cast I RTTI must be enabled. Is it enabled by default? I'm using gcc.

Thanks. And rest asure I'm not trying to argue with you. I'm just looking for advice.
[size="2"]I like the Walrus best.
Quote:Original post by owl
I've read that to use dynamic_cast I RTTI must be enabled. Is it enabled by default? I'm using gcc.
Yep. Most compilers are smart enough to turn on RTTI if they encounter a typeid() or dynamic_cast<>, or issue an error if they are used with RTTI off.

Thanks again then :)
[size="2"]I like the Walrus best.
In this case I think the service model is too generic.
You may want to consider a core that has dedciated accessor functions for the sub-systems.
Will you ever use CBase without the renderer? Becuase your design is made for that type of scenario.
When you design classes you want to aim for the sweet-spot, only as complex as they need to be.

IRenderer* Core::render();
IVocalizer* Core::vocalizer();
etc...

[Edited by - Shannon Barber on March 29, 2006 8:55:01 PM]
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Using void* in combination with multiple inheritance can lead to very nasty bugs.
Example:
#include <iostream>using namespace std;struct Interface1 {};struct Interface2 {};struct Derived: public Interface1, public Interface2 { };int main() {    Derived obj;    void* p1 = (void*)&obj;    Interface2* p2 = (Interface2*)p1;    cout << p2 << " " << (Interface2*)&obj <<  endl;}      


This will print two different values (at least on my machine). This is because the compartments for different superclasses start at different points in memory. The C++ compiler will take care of normal upcasts, but it can't know what a void pointer points to in general, so casting to void* means losing information. So imagine what happens if you call a member function on *p2 ...
If you were to do a lot of void* casting to other types, basically something gone wrong. (Over engineered?)

Btw, I agreed with Xai: "In any heirarchy in C++ with a common base class you would NEVER use a void *."
"after many years of singularity, i'm still searching on the event horizon"
Quote:Original post by Shannon Barber
In this case I think the service model is too generic.
You may want to consider a core that has dedciated accessor functions for the sub-systems.
Will you ever use CBase without the renderer? Becuase your design is made for that type of scenario.
When you design classes you want to aim for the sweet-spot, only as complex as they need to be.

IRenderer* Core::render();
IVocalizer* Core::vocalizer();
etc...


The application I've in mind is kind of a server into which you can plug services and programs contained in shared libraries. That's the reason why I made it that generic. I would like to be able to use it for games as well as for any other kind of application with minimal recompiling.

I've been informing myself about dynamic_cast and it introduces quite a bit of troubles when used with shared libraries... but all of them can be solved compiling with the proper options.

Based on what I've read and what it has been said here, I understand that the safer aproach is to use dinamic_cast. Any other work around would introduce a lot of nasty and pretty slow coding that I'm not willing to face at this time, or to re-design in the future.

Anyway, if someone had any other idea, I'll be pleased to hear it.

tnx.
[size="2"]I like the Walrus best.

Still, somewhere in your code you will have to cast from void* to a specific IFoo*, so I can see no reason why your factory shouldn't return base pointers.

interface IRenderer {};interface IFoo {};interface IFilter {};// ... get all your plugin names ..std::vector<std::string> myRendererPlugins = myRegistry.readRendererPlugins();std::vector<std::string> myFooPlugins = myRegistry.readFooPlugins();// ... instantiate the objects and get interfaces (not void*)IRenderer* renderer = myFactory.getRenderer( *(myRendererPlugins.begin()) );IFoo* foo = myFactory.getFoo( "FooBarUltimate" );// ...


Again, I can't see why your factory should be over-generic, as you'll have to be concrete sometime anyway.. or did I miss something? Are the interfaces not known beforehand?

[Edited by - Konfusius on March 30, 2006 10:56:09 AM]

This topic is closed to new replies.

Advertisement