Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Virtual method inlining

Reply
Thread Tools

Virtual method inlining

 
 
1 2
Guest
Posts: n/a
 
      11-09-2012
On 8 nov, 18:28, Leigh Johnston <(E-Mail Removed)> wrote:
> On 08/11/2012 20:15, 1 2 wrote:
>
>
>
>
>
> > On 8 nov, 11:34, Jorgen Grahn <(E-Mail Removed)> wrote:
> >> On Wed, 2012-11-07, 1 2 wrote:

>
> >> ...

>
> >>> I hadn't thought of using compile-time polymorphism, but that's
> >>> probably because I think the lack of method signatures is a bit of a
> >>> problem. For example, with runtime polymorphism, you might have
> >>> something like this:

>
> >>> class Shape
> >>> {
> >>> public:
> >>> * * *virtual void Draw(int x, int y) = 0;
> >>> };

>
> >>> void DrawShape(Shape* s)
> >>> {
> >>> * * *s->Draw(200, 100);
> >>> }

>
> >>> With compile-time polymorphism, you would have:

>
> >>> // Note: no Shape base class

>
> >>> template<class Shape>
> >>> void DrawShape(Shape* s)
> >>> {
> >>> * * *s->Draw(200, 100);
> >>> }

>
> >> I'd prefer to pass a const reference, not a pointer to non-const.

>
> >>> It's not clear what operations the class supports, and what are the
> >>> parameter/return types of the methods. On the other hand, compile-time
> >>> polymorphism works nicely for things like STL iterators, where the
> >>> supported operations are clear from the nature of the object
> >>> (basically, iterators should behave as pointers).

>
> >> Isn't the situation the same here, though? *Shapes can be drawn. Just
> >> document that, and trust the compiler to catch any errors.

>
> >> On the other hand ...

>
> >> I don't use run-time polymorphism much, and I don't like working with
> >> it. *I never used Smalltalk or Java. *But when the alternative is a
> >> huge switch statement even I find it preferable. *Isn't this a
> >> textbook example of such a case?

>
> > Exactly. In real code, a class might support quite a few methods
> > (maybe a couple dozen if it's part of a class hierarchy), and the
> > parameters for some of them can be more numerous and complicated than
> > in the example above.

>
> > Actually I just thought of a solution: combine the contracts provided
> > by runtime polymorphism with the fast calls of compile-time
> > polymorphism. We can define a COM interface or an abstract class for
> > the polymorphic operations:

>
> > interface IShape : IUnknown
> > {
> > * * *HRESULT Draw(int x, int y);
> > }

>
> Missing /virtual/ keyword.
>
>
>
> > And in the code that uses the polymorphic operations, we use a
> > template parameter, but make sure that it implements the interface by
> > using a static_assert that tests a std::is_base_of:

>
> > template<class Shape>
> > void DrawShape(Shape* s)
> > {
> > * * *static_assert(std::is_base_of<IShape, Shape>::value, "Shape must
> > implement IShape");

>
> > * * *s->Draw(200, 100);
> > }

>
> > I can definitely see this being used at large scale.

>
> What is the point of doing that? *The compiler doesn't know that Draw is
> "final" so still requires dynamic (virtual dispatch) unless it knows
> Shape is the most derived type; the "final" overrider in C++11 improves
> the situation somewhat.


OK, then maybe it can be changed to the following to disable calling
through the vtable:

template<class Shape>
void DrawShape(Shape* s)
{
static_assert(std::is_base_of<IShape, Shape>::value, "Shape must
implement IShape");

s->Shape:raw(200, 100);
}
 
Reply With Quote
 
 
 
 
1 2
Guest
Posts: n/a
 
      11-09-2012
On 9 nov, 10:39, Leigh Johnston <(E-Mail Removed)> wrote:
> On 09/11/2012 07:12, 1 2 wrote:
>
>
>
>
>
> > On 8 nov, 18:28, Leigh Johnston <(E-Mail Removed)> wrote:
> >> On 08/11/2012 20:15, 1 2 wrote:
> >>> And in the code that uses the polymorphic operations, we use a
> >>> template parameter, but make sure that it implements the interface by
> >>> using a static_assert that tests a std::is_base_of:

>
> >>> template<class Shape>
> >>> void DrawShape(Shape* s)
> >>> {
> >>> * * * static_assert(std::is_base_of<IShape, Shape>::value, "Shape must
> >>> implement IShape");

>
> >>> * * * s->Draw(200, 100);
> >>> }

>
> >>> I can definitely see this being used at large scale.

>
> >> What is the point of doing that? *The compiler doesn't know that Draw is
> >> "final" so still requires dynamic (virtual dispatch) unless it knows
> >> Shape is the most derived type; the "final" overrider in C++11 improves
> >> the situation somewhat.

>
> > OK, then maybe it can be changed to the following to disable calling
> > through the vtable:

>
> > template<class Shape>
> > void DrawShape(Shape* s)
> > {
> > * * *static_assert(std::is_base_of<IShape, Shape>::value, "Shape must
> > implement IShape");

>
> > * * *s->Shape:raw(200, 100);
> > }

>
> Totally pointless.


Why? It accomplishes what I said earlier: combine the contracts
provided by runtime polymorphism with the fast (w/o virtual dispatch)
calls of compile-time polymorphism.
 
Reply With Quote
 
 
 
 
1 2
Guest
Posts: n/a
 
      11-10-2012
On Nov 9, 5:46*pm, Leigh Johnston <(E-Mail Removed)> wrote:
> On 09/11/2012 22:35, 1 2 wrote:
>
>
>
>
>
> > On 9 nov, 10:39, Leigh Johnston <(E-Mail Removed)> wrote:
> >> On 09/11/2012 07:12, 1 2 wrote:

>
> >>> On 8 nov, 18:28, Leigh Johnston <(E-Mail Removed)> wrote:
> >>>> On 08/11/2012 20:15, 1 2 wrote:
> >>>>> And in the code that uses the polymorphic operations, we use a
> >>>>> template parameter, but make sure that it implements the interface by
> >>>>> using a static_assert that tests a std::is_base_of:

>
> >>>>> template<class Shape>
> >>>>> void DrawShape(Shape* s)
> >>>>> {
> >>>>> * * * *static_assert(std::is_base_of<IShape, Shape>::value,"Shape must
> >>>>> implement IShape");

>
> >>>>> * * * *s->Draw(200, 100);
> >>>>> }

>
> >>>>> I can definitely see this being used at large scale.

>
> >>>> What is the point of doing that? *The compiler doesn't know that Draw is
> >>>> "final" so still requires dynamic (virtual dispatch) unless it knows
> >>>> Shape is the most derived type; the "final" overrider in C++11 improves
> >>>> the situation somewhat.

>
> >>> OK, then maybe it can be changed to the following to disable calling
> >>> through the vtable:

>
> >>> template<class Shape>
> >>> void DrawShape(Shape* s)
> >>> {
> >>> * * * static_assert(std::is_base_of<IShape, Shape>::value, "Shape must
> >>> implement IShape");

>
> >>> * * * s->Shape:raw(200, 100);
> >>> }

>
> >> Totally pointless.

>
> > Why? It accomplishes what I said earlier: combine the contracts
> > provided by runtime polymorphism with the fast (w/o virtual dispatch)
> > calls of compile-time polymorphism.

>
> Pointless because you need the object that draws all the shapes to be
> aware of all the different types of shapes which is a nonsense from a
> design point of view.


No it doesn't need that. Where in this code:

template<class Shape>
void DrawShape(Shape* s)
{
static_assert(std::is_base_of<IShape, Shape>::value, "Shape must
implement IShape");

s->Shape:raw(200, 100);
}

do you see awareness of all the different types of shapes? It's just
like the original runtime polymorphism and compile-time polymorphism
examples in my reply to Tobias Müller, except that I basically
combined the two into one.

>*C++ templates are great especially for things
> such as generic containers/algorithms, traits and policies but you are
> misusing them: you need to learn how to do proper OOP.


I know proper OOP. Normally I would just use runtime polymorphism and
no templates at all (in this case), but this is more of an
optimization issue (maybe premature optimization if the virtual call
overhead is negligible compared to the actual work of the method, but
not otherwise).
 
Reply With Quote
 
Tobias Müller
Guest
Posts: n/a
 
      11-10-2012
Leigh Johnston <(E-Mail Removed)> wrote:
> On 10/11/2012 00:10, 1 2 wrote:
>> On Nov 9, 5:46 pm, Leigh Johnston <(E-Mail Removed)> wrote:
>>> On 09/11/2012 22:35, 1 2 wrote:
>>>
>>>
>>>
>>>
>>>
>>>> On 9 nov, 10:39, Leigh Johnston <(E-Mail Removed)> wrote:
>>>>> On 09/11/2012 07:12, 1 2 wrote:
>>>
>>>>>> On 8 nov, 18:28, Leigh Johnston <(E-Mail Removed)> wrote:
>>>>>>> On 08/11/2012 20:15, 1 2 wrote:
>>>>>>>> And in the code that uses the polymorphic operations, we use a
>>>>>>>> template parameter, but make sure that it implements the interface by
>>>>>>>> using a static_assert that tests a std::is_base_of:
>>>
>>>>>>>> template<class Shape>
>>>>>>>> void DrawShape(Shape* s)
>>>>>>>> {
>>>>>>>> static_assert(std::is_base_of<IShape, Shape>::value, "Shape must
>>>>>>>> implement IShape");
>>>
>>>>>>>> s->Draw(200, 100);
>>>>>>>> }
>>>
>>>>>>>> I can definitely see this being used at large scale.
>>>
>>>>>>> What is the point of doing that? The compiler doesn't know that Draw is
>>>>>>> "final" so still requires dynamic (virtual dispatch) unless it knows
>>>>>>> Shape is the most derived type; the "final" overrider in C++11 improves
>>>>>>> the situation somewhat.
>>>
>>>>>> OK, then maybe it can be changed to the following to disable calling
>>>>>> through the vtable:
>>>
>>>>>> template<class Shape>
>>>>>> void DrawShape(Shape* s)
>>>>>> {
>>>>>> static_assert(std::is_base_of<IShape, Shape>::value, "Shape must
>>>>>> implement IShape");
>>>
>>>>>> s->Shape:raw(200, 100);
>>>>>> }
>>>
>>>>> Totally pointless.
>>>
>>>> Why? It accomplishes what I said earlier: combine the contracts
>>>> provided by runtime polymorphism with the fast (w/o virtual dispatch)
>>>> calls of compile-time polymorphism.
>>>
>>> Pointless because you need the object that draws all the shapes to be
>>> aware of all the different types of shapes which is a nonsense from a
>>> design point of view.

>>
>> No it doesn't need that. Where in this code:
>>
>> template<class Shape>
>> void DrawShape(Shape* s)
>> {
>> static_assert(std::is_base_of<IShape, Shape>::value, "Shape must
>> implement IShape");
>>
>> s->Shape:raw(200, 100);
>> }
>>
>> do you see awareness of all the different types of shapes? It's just
>> like the original runtime polymorphism and compile-time polymorphism
>> examples in my reply to Tobias Müller, except that I basically
>> combined the two into one.

>
> The code that instantiates the template has to pass an object of the
> correct type to benefit from your "optimization" which means that code
> needs to know all the different types which is a nonsense from a design point of view.


That's exactly the point of compile time polymorphism. The caller can of
course be a template itself, but at one point you have an object of a
concrete type anyway, that's where the template is instanciated.
This whole concept is of course pointless if you want to draw an
inhomogenous collection of shapes. There you have to find out the concrete
type by hand, which is worse that normal virtual function dispatch IMO.

>>> C++ templates are great especially for things
>>> such as generic containers/algorithms, traits and policies but you are
>>> misusing them: you need to learn how to do proper OOP.

>>
>> I know proper OOP. Normally I would just use runtime polymorphism and
>> no templates at all (in this case), but this is more of an
>> optimization issue (maybe premature optimization if the virtual call
>> overhead is negligible compared to the actual work of the method, but
>> not otherwise).

>
> It is an "optimization" I have never had to employ modulo *normal* (read:
> "sane") template use.


Then again it seems to be a real demand amongst 'sane' developers to
restrict the types for template arguments, how else do you explain C++
'concepts'? IShape is just a poor men's 'concept'.
Typeclasses in Haskell or Traits in Rust (www.rust-lang.org) are also
conceptionally similar.

Tobi
 
Reply With Quote
 
Bo Persson
Guest
Posts: n/a
 
      11-10-2012
Tobias Müller skrev 2012-11-07 13:50:
>>> On 11/07/12 22:49, (E-Mail Removed) wrote:
>>>
>>>> I've heard of virtual method inlining in Java, and I'd like to know if
>>>> the same is possible in C++. If so, in which cases is it applicable?
>>>

>
> In C++11, you can qualify a virtual method 'final'. That means it can be
> inlined if it is called via a pointer of the same class:
>
> struct A
> {
> virtual void f() { /* do stuff */ }
> };
>
> struct B : A
> {
> virtual void f() final { /* do other stuff */ }
> };
>
> void process(A* a)
> {
> a->f(); // cannot be inlined
> }


Assuming inlining, if 'process' gets inlined we again might know the
static type of a at the call site, and inline that call too.

>
> void process(B* b)
> {
> b->f(); // can be inlined
> }
>


Here is is easier, but just a little bit.


Bo Persson

 
Reply With Quote
 
Tobias Müller
Guest
Posts: n/a
 
      11-10-2012
Leigh Johnston <(E-Mail Removed)> wrote:
> On 10/11/2012 09:19, Tobias Müller wrote:
>> Leigh Johnston <(E-Mail Removed)> wrote:
>>> On 10/11/2012 00:10, 1 2 wrote:
>>>> On Nov 9, 5:46 pm, Leigh Johnston <(E-Mail Removed)> wrote:
>>>>> On 09/11/2012 22:35, 1 2 wrote:
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>> On 9 nov, 10:39, Leigh Johnston <(E-Mail Removed)> wrote:
>>>>>>> On 09/11/2012 07:12, 1 2 wrote:
>>>>>
>>>>>>>> On 8 nov, 18:28, Leigh Johnston <(E-Mail Removed)> wrote:
>>>>>>>>> On 08/11/2012 20:15, 1 2 wrote:
>>>>>>>>>> And in the code that uses the polymorphic operations, we use a
>>>>>>>>>> template parameter, but make sure that it implements the interface by
>>>>>>>>>> using a static_assert that tests a std::is_base_of:
>>>>>
>>>>>>>>>> template<class Shape>
>>>>>>>>>> void DrawShape(Shape* s)
>>>>>>>>>> {
>>>>>>>>>> static_assert(std::is_base_of<IShape, Shape>::value, "Shape must
>>>>>>>>>> implement IShape");
>>>>>
>>>>>>>>>> s->Draw(200, 100);
>>>>>>>>>> }
>>>>>
>>>>>>>>>> I can definitely see this being used at large scale.
>>>>>
>>>>>>>>> What is the point of doing that? The compiler doesn't know that Draw is
>>>>>>>>> "final" so still requires dynamic (virtual dispatch) unless it knows
>>>>>>>>> Shape is the most derived type; the "final" overrider in C++11 improves
>>>>>>>>> the situation somewhat.
>>>>>
>>>>>>>> OK, then maybe it can be changed to the following to disable calling
>>>>>>>> through the vtable:
>>>>>
>>>>>>>> template<class Shape>
>>>>>>>> void DrawShape(Shape* s)
>>>>>>>> {
>>>>>>>> static_assert(std::is_base_of<IShape, Shape>::value, "Shape must
>>>>>>>> implement IShape");
>>>>>
>>>>>>>> s->Shape:raw(200, 100);
>>>>>>>> }
>>>>>
>>>>>>> Totally pointless.
>>>>>
>>>>>> Why? It accomplishes what I said earlier: combine the contracts
>>>>>> provided by runtime polymorphism with the fast (w/o virtual dispatch)
>>>>>> calls of compile-time polymorphism.
>>>>>
>>>>> Pointless because you need the object that draws all the shapes to be
>>>>> aware of all the different types of shapes which is a nonsense from a
>>>>> design point of view.
>>>>
>>>> No it doesn't need that. Where in this code:
>>>>
>>>> template<class Shape>
>>>> void DrawShape(Shape* s)
>>>> {
>>>> static_assert(std::is_base_of<IShape, Shape>::value, "Shape must
>>>> implement IShape");
>>>>
>>>> s->Shape:raw(200, 100);
>>>> }
>>>>
>>>> do you see awareness of all the different types of shapes? It's just
>>>> like the original runtime polymorphism and compile-time polymorphism
>>>> examples in my reply to Tobias Müller, except that I basically
>>>> combined the two into one.
>>>
>>> The code that instantiates the template has to pass an object of the
>>> correct type to benefit from your "optimization" which means that code
>>> needs to know all the different types which is a nonsense from a design point of view.

>>
>> That's exactly the point of compile time polymorphism. The caller can of
>> course be a template itself, but at one point you have an object of a
>> concrete type anyway, that's where the template is instanciated.
>> This whole concept is of course pointless if you want to draw an
>> inhomogenous collection of shapes. There you have to find out the concrete
>> type by hand, which is worse that normal virtual function dispatch IMO.

>
> That was the point I was making: if a single object is responsible for
> drawing all the shapes then for it to benefit from this dubious
> "optimization" it needs to be aware of all the different sub-class types
> in order to correctly instantiate this useless function template.


I really cannot that single object you are talking about.
For example if you want to draw all pixels of an Image, you could use the
same rendering algorithm for your collection of pixels and it will be
nicely optimized due to inlining.
I wouldn't even consider rendering pixel like this with runtime
polymorphism.

Consider the following example:

class Drawable
{
public:
virtual void draw(/*...*/) = 0;
};

template <typename Iterator>
void drawSequence(Iterator begin, Iterator end)
{
static_assert(std::is_base_of<Drawable,
std::iterator_traits<Iterator>::value_type>::value , "Must implement
Drawable");
for (Itetator it = begin; it != end; ++it)
it->draw(/*...*/);
}

class Pixel : public Drawable
{
public:
virtual void draw(/*...*/) final { /* simple */ }
};

class Image : public Drawable
{
public:
virtual void draw(/*...*/) final
{
// can be optimized, value type is statically known
drawSequence(pixels.begin(), pixels.end());
}
private:
std::vector<Pixel> pixels;
};

class Path : public Drawable
{
public:
virtual void draw(/*...*/) final { /* complex */ }
};

typedef boost::indirect_iterator<std::vector<Drawable*>, Drawable> ItD;

int main(/*.../)
{
std::vector<Drawable*> objects;
objects.push_back(new Image);
objects.push_back(new Path);
// Normal runtime polymorphism
draw_sequence(ItD(objects.begin()), ItD(objects.end()));
}

>>>>> C++ templates are great especially for things
>>>>> such as generic containers/algorithms, traits and policies but you are
>>>>> misusing them: you need to learn how to do proper OOP.
>>>>
>>>> I know proper OOP. Normally I would just use runtime polymorphism and
>>>> no templates at all (in this case), but this is more of an
>>>> optimization issue (maybe premature optimization if the virtual call
>>>> overhead is negligible compared to the actual work of the method, but
>>>> not otherwise).
>>>
>>> It is an "optimization" I have never had to employ modulo *normal* (read:
>>> "sane") template use.

>>
>> Then again it seems to be a real demand amongst 'sane' developers to
>> restrict the types for template arguments, how else do you explain C++
>> 'concepts'? IShape is just a poor men's 'concept'.
>> Typeclasses in Haskell or Traits in Rust (www.rust-lang.org) are also
>> conceptionally similar.

>
> Like I said earlier C++ templates are great I just feel they are being
> misused in this case. This is a classic example of premature optimization.


I think in a program or library that deals with graphics, this an important
design decision, not premature optimization.

Tobi
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Method Inlining in HotSpot hemartin Java 0 03-17-2008 02:32 PM
Need clarification for virtual method and pure virtual function a C++ 7 06-28-2007 12:38 PM
inlining a text/plain mime attachment into email Matthew Lenz Perl 0 02-22-2005 05:39 PM
I see no difference in an inheirited non-virtual method and an inheirited virtual method jlopes C++ 7 11-19-2004 07:47 PM
virtual destructor Vs virtual method Calvin Lai C++ 7 12-18-2003 03:11 AM



Advertisments