Virtual method inlining

Discussion in 'C++' started by s0suk3@gmail.com, Nov 7, 2012.

  1. Guest

    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?

    I ask because I'm learning Direct3D, and I want to know if it's OK to use virtual methods for rendering operations that are performed at every frame, or if I should give up virtual methods and just do all rendering in a single file, of course at the expense of loss of abstraction.
    , Nov 7, 2012
    #1
    1. Advertising

  2. Ian Collins Guest

    On 11/07/12 22:49, 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?
    >
    > I ask because I'm learning Direct3D, and I want to know if it's OK to use virtual methods for rendering operations that are performed at every frame, or if I should give up virtual methods and just do all rendering in a single file, of course at the expense of loss of abstraction.


    ** Please wrap your lines to something readable! **

    The compiler may inline a virtual method if it knows the type of an
    object. In the following trivial example, f.f() will probably be
    inlined because the compiler knows that b is a B.

    struct A
    {
    int n;
    virtual void f() { n = 0; }
    };

    struct B : A
    {
    void f() { n = 10; }
    };

    int main()
    {
    B b;
    b.f();
    }

    --
    Ian Collins
    Ian Collins, Nov 7, 2012
    #2
    1. Advertising

  3. 1 2 Guest

    On Nov 7, 5:06 am, Ian Collins <> wrote:
    > On 11/07/12 22:49, 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?

    >
    > > I ask because I'm learning Direct3D, and I want to know if it's OK to use virtual methods for rendering operations that are performed at every frame, or if I should give up virtual methods and just do all rendering in a single file, of course at the expense of loss of abstraction.

    >
    > ** Please wrap your lines to something readable! **
    >
    > The compiler may inline a virtual method if it knows the type of an
    > object.  In the following trivial example, f.f() will probably be
    > inlined because the compiler knows that b is a B.
    >
    > struct A
    > {
    >    int n;
    >    virtual void f() { n = 0; }
    >
    > };
    >
    > struct B : A
    > {
    >    void f() { n = 10; }
    >
    > };
    >
    > int main()
    > {
    >    B b;
    >    b.f();
    >
    > }


    Well that's pretty much non-virtual method inlining, because B::f is
    not virtual (even though A::f is), not to mention that you're calling
    it through a statically-known B instance.

    In the code I'm writing, there are a lot of cases where the runtime
    type is not known statically (after all, that's the whole point of
    polymorphism).
    1 2, Nov 7, 2012
    #3
  4. 1 2 Guest

    On 7 nov, 05:48, Asuranceturix <> wrote:
    > On Wed, 07 Nov 2012 02:33:53 -0800, 1 2 wrote:
    > > Well that's pretty much non-virtual method inlining, because B::f is not
    > > virtual (even though A::f is), not to mention that you're calling it
    > > through a statically-known B instance.

    >
    > > In the code I'm writing, there are a lot of cases where the runtime type
    > > is not known statically (after all, that's the whole point of
    > > polymorphism).

    >
    > I quote from Wikipedia:
    > "In computing, inline expansion, or inlining, is a manual or compiler
    > optimization that replaces a function call site with the body of the
    > callee."
    >
    > How do you expect the (non-JIT-)compiler to know which body to inline if
    > the type of the object is not known at compile-time?


    Thank you all, I guess that's why all the samples in the DX SDK are so
    non-object-oriented and hard to follow :(
    1 2, Nov 7, 2012
    #4
  5. Am 07.11.2012 11:33, schrieb 1 2:

    >> The compiler may inline a virtual method if it knows the type of an
    >> object. In the following trivial example, f.f() will probably be
    >> inlined because the compiler knows that b is a B.
    >>
    >> struct A
    >> {
    >> int n;
    >> virtual void f() { n = 0; }
    >>
    >> };
    >>
    >> struct B : A
    >> {
    >> void f() { n = 10; }
    >>
    >> };
    >>
    >> int main()
    >> {
    >> B b;
    >> b.f();
    >>
    >> }

    >
    > Well that's pretty much non-virtual method inlining, because B::f is
    > not virtual (even though A::f is), not to mention that you're calling
    > it through a statically-known B instance.


    Ehem. B::f *is* virtual. It doesn't require a "virtual" keyword to make
    it virtual - it is sufficient to have it virtual in the base class.

    Yes, of course here the compiler can see the full type, and that is also
    the reason why a compiler could inline it.

    > In the code I'm writing, there are a lot of cases where the runtime
    > type is not known statically (after all, that's the whole point of
    > polymorphism).


    In that case, the compiler likely cannot inline it. (Of course, nothing
    stops anyone from writing a JIT compiler for C++, but this is rather
    untypical, and in classical compiler-linker setups, the compiler does
    not know the dynamic type of a polymorphic class.)

    Greetings,
    Thomas
    Thomas Richter, Nov 7, 2012
    #5
  6. 1 2 <> wrote:
    > On Nov 7, 5:06 am, Ian Collins <> wrote:
    >> On 11/07/12 22:49, 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?

    >>
    >>> I ask because I'm learning Direct3D, and I want to know if it's OK to
    >>> use virtual methods for rendering operations that are performed at
    >>> every frame, or if I should give up virtual methods and just do all
    >>> rendering in a single file, of course at the expense of loss of abstraction.

    >>
    >> ** Please wrap your lines to something readable! **
    >>
    >> The compiler may inline a virtual method if it knows the type of an
    >> object. In the following trivial example, f.f() will probably be
    >> inlined because the compiler knows that b is a B.
    >>
    >> struct A
    >> {
    >> int n;
    >> virtual void f() { n = 0; }
    >>
    >> };
    >>
    >> struct B : A
    >> {
    >> void f() { n = 10; }
    >>
    >> };
    >>
    >> int main()
    >> {
    >> B b;
    >> b.f();
    >>
    >> }

    >
    > Well that's pretty much non-virtual method inlining, because B::f is
    > not virtual (even though A::f is),


    B::f is implicitly virtual, if A::f is virtual.

    > not to mention that you're calling
    > it through a statically-known B instance.


    That's the whole point.
    Inlining means to copy the content of the called function into the calling
    function. For this to be possible, the content of the called funtion has to
    be known statically.

    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
    }

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

    > In the code I'm writing, there are a lot of cases where the runtime
    > type is not known statically (after all, that's the whole point of
    > polymorphism).


    OO-style runtime polymorphism is not the only type of polymorphism.
    Polymorphism via templates is another possibility.
    We are using a template based rendering library at work, that generates
    performant code even if the rendering of every pixel is done in its own
    function.
    The library is highly polymorphic, but at compile time, not at runtime.

    There are programming languages like Haskell, that rely almost exclusively
    on compile time polymorphism. And Haskell still allows to write highly
    generic/polymorphic code. Typical Haskell code is probably even more
    generic than typical C++ code.

    Tobi
    Tobias Müller, Nov 7, 2012
    #6
  7. 1 2 Guest

    On 7 nov, 07:50, Tobias Müller <> wrote:
    > 1 2 <> wrote:
    > > On Nov 7, 5:06 am, Ian Collins <> wrote:
    > >> On 11/07/12 22:49, 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?

    >
    > >>> I ask because I'm learning Direct3D, and I want to know if it's OK to
    > >>> use virtual methods for rendering operations that are performed at
    > >>> every frame, or if I should give up virtual methods and just do all
    > >>> rendering in a single file, of course at the expense of loss of abstraction.

    >
    > >> ** Please wrap your lines to something readable! **

    >
    > >> The compiler may inline a virtual method if it knows the type of an
    > >> object.  In the following trivial example, f.f() will probably be
    > >> inlined because the compiler knows that b is a B.

    >
    > >> struct A
    > >> {
    > >>    int n;
    > >>    virtual void f() { n = 0; }

    >
    > >> };

    >
    > >> struct B : A
    > >> {
    > >>    void f() { n = 10; }

    >
    > >> };

    >
    > >> int main()
    > >> {
    > >>    B b;
    > >>    b.f();

    >
    > >> }

    >
    > > Well that's pretty much non-virtual method inlining, because B::f is
    > > not virtual (even though A::f is),

    >
    > B::f is implicitly virtual, if A::f is virtual.
    >
    > > not to mention that you're calling
    > > it through a statically-known B instance.

    >
    > That's the whole point.
    > Inlining means to copy the content of the called function into the calling
    > function. For this to be possible, the content of the called funtion has to
    > be known statically.
    >
    > 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
    >
    > }
    >
    > void process(B* b)
    > {
    >    b->f(); // can be inlined
    >
    > }
    > > In the code I'm writing, there are a lot of cases where the runtime
    > > type is not known statically (after all, that's the whole point of
    > > polymorphism).

    >
    > OO-style runtime polymorphism is not the only type of polymorphism.
    > Polymorphism via templates is another possibility.
    > We are using a template based rendering library at work, that generates
    > performant code even if the rendering of every pixel is done in its own
    > function.
    > The library is highly polymorphic, but at compile time, not at runtime.
    >
    > There are programming languages like Haskell, that rely almost exclusively
    > on compile time polymorphism. And Haskell still allows to write highly
    > generic/polymorphic code. Typical Haskell code is probably even more
    > generic than typical C++ code.


    Thanks, very insightful!

    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);
    }

    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).

    P.S. didn't know about the "once virtual, always virtual" rule, even
    after using C++ for so long!
    1 2, Nov 7, 2012
    #7
  8. Stuart Guest

    On 11/7/12 "1 2" wrote:
    > P.S. didn't know about the "once virtual, always virtual" rule, even
    > after using C++ for so long!


    That's why I always put the keyword "virtual" on virtual methods,
    regardless whether the method is implicitly or explicitly virtual. If I
    don't put a comment in front of it, then it should be clear that the
    method overrides a base class method:

    class Foo {
    /** Does something.
    */
    virtual void doSomething ();
    };

    class Bar : public Foo {
    // No comment needed, the "superfluous" virtual
    // should indicate that this method is explained
    // in some base class.
    virtual void doSomething ();

    /** Does another thing.
    */
    virtual void doAnotherThing (); // comment needed
    };

    Regards,
    Stuart
    Stuart, Nov 7, 2012
    #8
  9. Jorgen Grahn Guest

    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?

    If you have to draw a changing bunch of mixed objects that means you
    have to call various drawing functions, and you cannot statically
    decide which ones in which order. You worry about the performance
    penalty for virtual calls, but what alternative would be faster?
    The same job still has to be done.

    (The only optimization I can think of at the moment is to draw all Foo
    objecte before all Bar objects, to keep the instruction cache warm.)

    /Jorgen

    --
    // Jorgen Grahn <grahn@ Oo o. . .
    \X/ snipabacken.se> O o .
    Jorgen Grahn, Nov 8, 2012
    #9
  10. 1 2 Guest

    On 8 nov, 11:34, Jorgen Grahn <> 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);
    }

    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.
    1 2, Nov 8, 2012
    #10
  11. 1 2 Guest

    On 8 nov, 18:28, Leigh Johnston <> wrote:
    > On 08/11/2012 20:15, 1 2 wrote:
    >
    >
    >
    >
    >
    > > On 8 nov, 11:34, Jorgen Grahn <> 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::Draw(200, 100);
    }
    1 2, Nov 9, 2012
    #11
  12. 1 2 Guest

    On 9 nov, 10:39, Leigh Johnston <> wrote:
    > On 09/11/2012 07:12, 1 2 wrote:
    >
    >
    >
    >
    >
    > > On 8 nov, 18:28, Leigh Johnston <> 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::Draw(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.
    1 2, Nov 9, 2012
    #12
  13. 1 2 Guest

    On Nov 9, 5:46 pm, Leigh Johnston <> wrote:
    > On 09/11/2012 22:35, 1 2 wrote:
    >
    >
    >
    >
    >
    > > On 9 nov, 10:39, Leigh Johnston <> wrote:
    > >> On 09/11/2012 07:12, 1 2 wrote:

    >
    > >>> On 8 nov, 18:28, Leigh Johnston <> 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::Draw(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::Draw(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).
    1 2, Nov 10, 2012
    #13
  14. Leigh Johnston <> wrote:
    > On 10/11/2012 00:10, 1 2 wrote:
    >> On Nov 9, 5:46 pm, Leigh Johnston <> wrote:
    >>> On 09/11/2012 22:35, 1 2 wrote:
    >>>
    >>>
    >>>
    >>>
    >>>
    >>>> On 9 nov, 10:39, Leigh Johnston <> wrote:
    >>>>> On 09/11/2012 07:12, 1 2 wrote:
    >>>
    >>>>>> On 8 nov, 18:28, Leigh Johnston <> 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::Draw(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::Draw(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
    Tobias Müller, Nov 10, 2012
    #14
  15. Bo Persson Guest

    Tobias Müller skrev 2012-11-07 13:50:
    >>> On 11/07/12 22:49, 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
    Bo Persson, Nov 10, 2012
    #15
  16. Leigh Johnston <> wrote:
    > On 10/11/2012 09:19, Tobias Müller wrote:
    >> Leigh Johnston <> wrote:
    >>> On 10/11/2012 00:10, 1 2 wrote:
    >>>> On Nov 9, 5:46 pm, Leigh Johnston <> wrote:
    >>>>> On 09/11/2012 22:35, 1 2 wrote:
    >>>>>
    >>>>>
    >>>>>
    >>>>>
    >>>>>
    >>>>>> On 9 nov, 10:39, Leigh Johnston <> wrote:
    >>>>>>> On 09/11/2012 07:12, 1 2 wrote:
    >>>>>
    >>>>>>>> On 8 nov, 18:28, Leigh Johnston <> 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::Draw(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::Draw(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
    Tobias Müller, Nov 10, 2012
    #16
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Matthew Lenz
    Replies:
    0
    Views:
    509
    Matthew Lenz
    Feb 22, 2005
  2. Calvin Lai
    Replies:
    7
    Views:
    540
    Calvin Lai
    Dec 18, 2003
  3. jlopes
    Replies:
    7
    Views:
    406
    jlopes
    Nov 19, 2004
  4. a
    Replies:
    7
    Views:
    353
    dasjotre
    Jun 28, 2007
  5. hemartin

    Method Inlining in HotSpot

    hemartin, Mar 17, 2008, in forum: Java
    Replies:
    0
    Views:
    306
    hemartin
    Mar 17, 2008
Loading...

Share This Page