Inheritance + RTTI

Started by
8 comments, last by kavika 20 years, 1 month ago
Have a question, tho it requires some BG: I've got two classes, here: #ifndef RENDERER_H #define RENDERER_H // It's really a lot longer than this class surface { public: surface (); virtual ~surface (); virtual void somefunc () = 0; }; class image; // defined elsewhere class renderer { public: renderer (); virtual ~renderer (); virtual surface& create_surface (image& image) = 0; virtual void render_surface (surface& blah) = 0; }; And I have a .DLL that derives from this interface #ifndef INCLUDED_SDL_RENDERER_H #define INCLUDED_SDL_RENDERER_H class sdl_surface : public surface { public: sdl_surface (); ~sdl_surface (); void somefunc (); }; class sdl_renderer : public renderer { public: sdl_renderer (); ~sdl_renderer (); sdl_surface& create_surface (image& image); void render_surface (surface& blah); }; Here's the problem I have: in render_surface, how to convert it back to a sdl_surface. I've come up with two options: 1) use dynamic_cast, and if I don't get something valid, either no-op, or throw an exception 2) implement a magic cookie system (using unsigned ints) to refer to the surfaces, and storing them in a map -> std::map (Well, I've got a special pointer class and not a sdl_surface, and a typedef instead of unsigned int, but you get the point =) Wondering if there are any other options I haven't considered, cause I don't think RTTI is a good option (dynamic_cast). Also, instead of the magic cookie system, I'd rather have users just keep track of pointers, if possible. They're going to be using them to manipulate the surfaces directly, so that would be simpler, and they wouldn't have to come up with enums, or me come up with "unique random numbers" The only reason I'm using an inheritance scheme is since the "renderer" will be loaded from a .dll, and there's no way for the main library to know which renderer will be loaded. I can't think of any non-inheritance scheme to make this work, that's simple, and doesn't involve crazy hacks... Of course there's always the "all implmentations known at compile time" deal, along with simple switch statements for which to use, but that takes some of the fun, and purpose, out of coming up with a plugin system =) Anything I've missed? Any bright ideas? And no comments, please, on the speed this particular approach disallows, or how idiotic it is. I still wanna do it, and see for myself exactly how slow it'll probably be =) [edited by - kavika on March 5, 2004 10:59:38 PM] [edited by - kavika on March 5, 2004 11:00:29 PM]
Advertisement
Why do you need to do this cast in the first place?
Inside the sdl_renderer class ->
void render_surface (surface& blah);

CANNOT be overridden like this->
void render_surface (sdl_surface& blah);

I''m pretty sure it''s the language that doesn''t allow this, and not just GCC being stupid

Basically, I need the implementation specific sdl_renderer to see the implementation specific sdl_surface, in order to extract implementation-specific data member SDL_Surface* m_surface, in order to blit it to the implementation-specific SDL_Surface* m_screenbuf; =)

Unfortunately, I need this inheritance scheme to make the user unaware of which renderer will be run, but still be able to access both the renderer, and the surfaces.

If they didn''t have to have access to both, I wouldn''t need this inheritance scheme, and I wouldn''t need to deal with all this hastle. I guess I could make surface-level operations exist on the renderer interface, and the user would have no knowledge of surfaces whatsoever, but that seems less intuitive from the user perspective.

Hope I don''t sound to incoherant or redundant with my statements I''m on the phone with my g/f while writing this heh
Will you ever have more than one renderer in existance at any point or more than one type of derived surface class instantiated at once?
No, but the user needs to not know what type of renderer or surface they''re dealing with, just how renderers and surfaces interact with each other.

I have a renderer_loader class that "knows" which dll to load, and calls dlload, gets back a pointer to a "surface", and gives that to the user. Unless there''s some better way to interface that part with the user, that I don''t know about? I''d like to write the user code to be renderer-independant, and if I implement a new renderer, not require the user classes to change.
While the client only needs to be provided with the renderer''s abstract type, you can maintain a pointer to the renderer''s concrete type in the dynamic library, since you only have the one renderer. If you migrate the render_surface() function to the surface class as a virtual function, it can use the concrete type of the renderer to grab the screen buffer and blit from it''s own surface data.

Or you could just use static_casts in the render_surface() function, and use an assert() to check against the pointer returned by a dynamic_cast in debug mode. Since you only have one set of related types, this should be reasonably safe.
How about using the visitor design pattern like this?
class surface{public:    surface ();    virtual ~surface ();    virtual void render() = 0;};class renderer{public:    renderer ();    virtual ~renderer ();    virtual surface& create_surface (image& image) = 0;};class sdl_surface : public surface{public:    sdl_surface ( sdl_renderer & renderer ) : m_renderer( renderer );    ~sdl_surface ();    void render() { m_renderer.render_surface( *this ); }private:    sdl_renderer & m_renderer;};class sdl_renderer : public renderer{public:    sdl_renderer ();    ~sdl_renderer ();    sdl_surface& create_surface (image& image) { ... new sdl_surface( *this ); ... }    void render_surface ( sdl_surface& blah);};  


To render a surface:
surface & s = renderer.create_surface( image );s.render();  


John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
You know, I spent $50 on the GoF book, you''d think I''d have at least read it Checking out the chapter on that pattern now.. the diagram looks so far pretty much like my hierarchy, too.

Thanks guys =)
Seems actually to be a variation on the visitor pattern.

Instead of the client creating concrete visitor classes to feed to the concrete nodes, all the visiting happens in the background, via the concrete object setup. The visitor also is simultaneously the program storing the nodes. Also, the single visitor is locked into the node on creation.

I really don''t need all the benefits of the visitor pattern touted by the GoF book, and this particular setup seems not to have the benifits they offer. However, the modifications you described seem to solve _this_ problem neatly.
ACTUALLY, since I only need one renderer at a time, I could make it a singleton class, and save the per-surface storage cost, and probably some other tricks like this =) Bleh. When I actually think out problems, it helps a lot.

Okay, I think I definately have my answers now.

Thanks a lot!
Cheers

This topic is closed to new replies.

Advertisement