workaround for lack of virtual templates (manually instantiated) in C++?

Started by
6 comments, last by MaulingMonkey 19 years, 4 months ago
Yowza, okay, I'm playing around with implementation of a little widget system. Now, normally virtuals and templates do NOT mix, but since in my case all instantiations would be explicit. G++ 3.3.3 is choking on this code:
struct context
{
	void render( widget & w )
	{
		w.render( *this ); //immediately calls do_render
	}
	template < typename widget_type > virtual void do_render( widget_type * ) = 0; //<-- error
};
main.cc:23: error: invalid use of `virtual' in template declaration of `virtual 
   void context::do_render(widget_type*)'
Now, this is a bit of a bummer. Any workarounds that spring to mind? I wouldn't have though this would be a problem since IIRC vtable building occurs at link time... after all the instantiations would be available... The basic idea is that context cannot determine the type of the widget, and thus asks the widget to render itself, as it knows what kind of widget it is. However, I wish to implement the rendering functions as templatized - but it dosn't seem to want to allow that. I'm seeing no obvious workaround. Any hints? The basic idea is to allow a GUI widget set that is extensible without modifying headers. As is, I would have to manually add another virtual function to the context class, and then copy those changes to all versions of that class (opengl_context, sdl_context, etc etc etc). In this system, link errors would occur if used stuff was missing, but that's it:

widget name    | used w/ base | implemented w/ derived | either
widget_1       | yes          | no                     | yes
widget_2       | no           | yes                    | yes
widget_3       | yes          | yes                    | yes
widget_4       | no           | no                     | no

base context vtable:

1 : do_render(  widget_1  ) : throw by default ( to be caught if misrendered )
2 : do_render(  widget_3  ) : throw by default ( to be caught if misrendered )

derived context vtable:

1 : do_render(  widget_1  ) : inherited, not overridden (throws if we try and use since never implemented
2 : do_render(  widget_3  ) : inherited, overridden (now does custom version since we implemented)
3 : do_render(  widget_2  ) : new (custom version we implemented)
do_render( widget_4 ) is never included because we never have potential to use it.
Advertisement
Quote:Original post by MaulingMonkey
Now, this is a bit of a bummer. Any workarounds that spring to mind? I wouldn't have though this would be a problem since IIRC vtable building occurs at link time... after all the instantiations would be available...

In most compilers, the vtable is laid out at compile time, and the name bindings are resolved at link time.

In any case, the C++ standard specifically forbids virtual member function templates. I'm not entirely clear what you are trying to do, so I can't suggest a better solution for your problem.
It sounds almost like you want single dynamic dispatch(or whatever the proper name is), which you can easily implement as:
struct context{   void do_render(widget *w)   {      w->render_self();   }}

where render_self is a virtual function and thus each widget pointer will know the right one to call.

If not, perhaps further explaining exactly what you want would be helpful
"Walk not the trodden path, for it has borne it's burden." -John, Flying Monk
Quote:Original post by SiCrane
Quote:Original post by MaulingMonkey
Now, this is a bit of a bummer. Any workarounds that spring to mind? I wouldn't have though this would be a problem since IIRC vtable building occurs at link time... after all the instantiations would be available...

In most compilers, the vtable is laid out at compile time, and the name bindings are resolved at link time.

In any case, the C++ standard specifically forbids virtual member function templates. I'm not entirely clear what you are trying to do, so I can't suggest a better solution for your problem.


Bah humbug. Guess that's another thing to throw into my toy language protoypes... thanks though!

Pretty much, here's a working example of the basic idea:

struct sdl_context : public context{    void render( widget * w ) //inherits from context    {        if ( dynamic_cast< textbox * >( w ) ) {            //... render a textbox ...        } else if ( dynamic_cast< label * >( w ) ) {            //... render a label ...        } else if ( dynamic_cast< button * >( w ) ) {            //... render a button ...        } else {            throw error();        }    }}class render_into{    context & c;public:    render_into( context & c ) : c(c) {}    void operator()( widget * w ) { c.render(w); }};sdl_context my_sdl_context;std::list< widget * > widget_list;for_each( widget_list.begin() , widget_list.end() , render_into( my_sdl_context ) );


The disadvantage is that to add a new widget type, one must add render functions to all of the different context class's render functions (aka 1 each for sdl_context, opengl_context, x11_context, etc).

To work around this I tried to use templates, that would in effect let me handle this totally seperate from the actual function entry, like so:

void sdl_context::do_render< textbox >( textbox * renderee ){    //render the textbox...}


That way, users can extend the widget system without mucking with what might be header files.
Quote:Original post by Extrarius
It sounds almost like you want single dynamic dispatch(or whatever the proper name is), which you can easily implement as:
*** Source Snippet Removed ***
where render_self is a virtual function and thus each widget pointer will know the right one to call.

If not, perhaps further explaining exactly what you want would be helpful


ALMOST correct. The only problem is that the widget needs to be rendered differently depending on the "context" it's in - aka, it must use OpenGL calls inside an opengl context - SDL calls inside an sdl window context - etc.

I _could_ manage this by specifying each widget specifically like so:

struct context{    virtual void render ( widget & w ) { w.render( *this ); }    virtual void do_render ( textbox & t ) = 0;    virtual void do_render ( label & l ) = 0;};struct opengl_context : public context{    virtual void do_render( textbox & t )    {        //render textbox in opengl    }    virtual void do_render( label & l )    {       //render label in opengl    }};struct sdl_context : public context{    virtual void do_render( textbox & t )    {        //render textbox in SDL    }    virtual void do_render( label & l )    {        //render label in SDL    }};struct widget{    virtual void render( context & c ) = 0;); }};struct label : public widget{    virtual void render( context & c ) { c.do_render( *this ); }};struct textbox : public widget{    virtual void render( context & c ) { c.do_render( *this ); }};


but this has the disadvantage that if I want to add a new widget, I must add the widget to the header of every single context class.
Actually, it sounds like you want double dispatch. It's a sufficiently complex subject in C++ that entire chapters of books can be devoted to it like chapter 10 in Alexendrescu's "Modern C++ Design." IIRC, Meyers also covers it in "More Effective C++." I don't know of any good online sources covering it. Sorry.
I think I have a workable idea, but I want to test it before typing it out here because I'm not sure it works and if it does I'm not sure exactly how yet =-P
Edit: Well, I have something that works, but after I got it working I realized it isn't at all what you want =-/
"Walk not the trodden path, for it has borne it's burden." -John, Flying Monk
Quote:Original post by SiCrane
Actually, it sounds like you want double dispatch.

That sounds right - I knew there was a term for it but I couldn't for the life of me remember it.
Quote:It's a sufficiently complex subject in C++ that entire chapters of books can be devoted to it like chapter 10 in Alexendrescu's "Modern C++ Design." IIRC, Meyers also covers it in "More Effective C++." I don't know of any good online sources covering it. Sorry.

Allright, I'll try some googlefu and if that turns up nothing, I'll check out some of those books. I really need to expand my library in the area of techniques anyways... thanks!

edit: googlefu turns up 3 different methods.
1) RTTI fu (ala dynamic_cast, only done better)
2) Directly managed fu (ala do_render( textbox & ))
3) Preprocessing fu (ala CMM).

This topic is closed to new replies.

Advertisement