C++ Logo

STD-PROPOSALS

Advanced search

Subject: Re: [std-proposals] Add a specialized "Extension" concept to the inheritance syntax
From: Tony V E (tvaneerd_at_[hidden])
Date: 2019-05-19 11:20:08


Yes, Herb's solution does not compose well in a multilevel hierarchy.

And yes, what you describe is a fairly common pattern, but I typically find it to be an anti pattern. Inheritance with partial implementation at each level is also a anti-pattern, IMO.

Sure a car is a vehicle, and a truck is just a car with the trunk replaced with an open bed and stronger frame, and a cube van is just a truck with ‎a cube instead of the open bed, etc. 

But I'd rather break those components into mixin classes or member variables, and then just derive cub van straight from vehicle, and give it a cube member variable.

https://en.wikipedia.org/wiki/Composition_over_inheritance

Or better, just don't do inheritance at all: "Inheritance is the base class of evil" - Sean Parent
https://channel9.msdn.com/Events/GoingNative/2013/Inheritance-Is-The-Base-Class-of-Evil

But, yes, sometimes inheriting and calling the base which calls its base, etc, is a pattern that works. I just don't think it is important enough to be a keyword.


Sent from my BlackBerry portable Babbage Device
From: Ofri Sadowsky
Sent: Sunday, May 19, 2019 11:58 AM
To: std-proposals@lists.isocpp.org; Tony V E
Subject: Re: [std-proposals] Add a specialized "Extension" concept to the inheritance syntax

It looks like the experience that I believed was common is no as common as I thought.  So let me try to re-explain.

Suppose that class Base0 has a setup() method which acquires some resources, and a cleanup() method which releases them.  Before getting to the difficult cases, let's assume a simple case in which setup() subscribes to some event in the larger application framework, and cleanup() unsubscribes.

To be closer to Sutter's guidelines, let's suppose that setup() and cleanup() both break down to a public, nonvirtual method setup()/cleanup() and another private or protected virtual method, say onSetup()/onCleanup() which is to be overridden by derived classes.  And, further, let's suppose that class Base0 defines this interface, and class Base1 (public Base0) uses it to define the subscription operations.

Now, let's examine a class Derived (public Base1), which also has to perform some onSetup() and onCleanup() tasks, like subscription to more framework events.  However, it also must ensure that its base class, Base1, is properly set up itself before Derived even begins to set itself up.  Since Derived does not know the implementation of Base1::onSetup(), it must be able to call _some_ Base1 method.  Continuing this down inheritance line, rather than making up new method names in each base class just so that the derived classes would be able to invoke the base's onSetup() through the interface, would it not make sense that each derived class would *extend* (I am deliberately not suggesting to _call_) the base class's onSetup() method?

This is not a pattern (or anti-pattern, if you happen to dislike it) that I made up myself.  I encountered it in various framework libraries, and, at least, the concept makes sense to me.

The solution that these libraries used was, indeed, a direct call from the beginning (in the case of tail extension) or end (in the case of head extension) of the _overriding_ derived method to the same method in the base class.  Maybe there is a better solution for this, but this is how it was done.

To stress the point: the issue is not about being or not being able to call base virtual methods from derived classes. It is about the need to scale up by extension operations such as setup()/cleanup().  This is why I am presenting it as new declarative syntax.  I do believe that such a syntax would enable better solutions given automatically by the compiler, as in the case of virtual diamond multiple inheritance.  And I am convinced that the need is a real one.

Sincerely

Ofri Sadowsky


On Sat, May 18, 2019 at 1:49 AM Tony V E <tvaneerd@gmail.com> wrote:


This most like is worth a read, from Herb Sutter:  http://www.gotw.ca/publications/mill18.htm




On Fri, May 17, 2019 at 5:24 AM Ofri Sadowsky via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
This post is based on an experience which, I believe, is shared by many, having to do with the concept of "extending" a base class rather than "overriding" its functionality.  Here are two directions for this concept.

First: It is common in class methods that perform some setup or cleanup operations to require a derived class to invoke the corresponding base class method, as in the following manner.

class Base
{
public:
    virtual void setup()
    {
        // do some setup
    }

    virtual void cleanup()
    {
        // do some cleanup
    }
};

class Derived : public Base
{
public
    void setup() override
    {
        Base::setup();
        // do my own setup
    }

    void cleanup() override
    {
        // do my own cleanup
        Base::cleanup();
    }
};

In current language, this is the only way to achieve it ,and it requires the developer to explicitly make the call to the base class method, with the risk of missing it.  In principle, the existing compiler mechanism could also support something such as the following.

class Base
{
public:
    tail_extensible void setup()
    {
        // do some setup
    }

   head_extensible void cleanup()
   {
      // do some cleanup
   }
};

class Derived
{
public:
    void setup() extension
    {
        // Base::setup() is called automatically
        // do my own setup
    }

    void cleanup() extension
    {
        // do my own cleanup
        // Base::cleanup is called automatically
    }
};

Semantically, the tail_extensible is similar to a virtual function, including an identical signature for the base and derived method, and all that is left is to forward the arguments passed to the derived method onto the base by using the regular signature rules (i.e by-value will be forwarded by value, by const reference will be forwarded as const reference, etc.).



Second: It sometimes happens that I want to extend an existing class with functionality only, but not with new members.  For example, I want to have a class that has all the functionality of a std::string and then some of my own.  This can be accomplished using composition or, probably more simply, through inheritance.  However, my class only needs the members of std::string.

In the current language, I'd have to manually write all the constructors to allow my class to be constructed from a string, or from anything that constructs a string, assigned from a string, etc.  Moreover, I'd have to add the move constructor etc., whose defaults expire as soon as I define the regular constructors.  But all this is not really necessary, and furthermore creates a risk of forgetting to do it.

Suppose we had an "extends" keyword whose semantics is: the extending class cannot define new members (maybe more relaxed for static members), and in return it does not have to specify all the mechanism of construction and assignment, which is inherited from the base class.

class MySpecialString : public extends std::string
{
public:
   // only new methods or overrides from the base, no members, no constructors,
   // no assignments, no operator overloading
};

This does pose some potential complications, such as would and how overloaded operators be included in the extension semantics, what about destructors? what to do with multiple extension (corresponding to multiple inheritance) an so on.  But I think that consistent rules can be developed for this.



All this is more about simplifying a developer's life, reducing boilerplate and making safer code.  It's replacing what would be done manually with a declarative form that implies clear semantics.  I'd like to see this standardized.

Regards

Ofri Sadowsky

--
Ofri Sadowsky, PhD
Computer Science Consulting and Training
7 Carmel St., #37
Rehovot  7630507
Israel
 
Tel: +972-77-3436003
Mob: +972-54-3113572
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
http://lists.isocpp.org/mailman/listinfo.cgi/std-proposals


--
Be seeing you,
Tony


--
Ofri Sadowsky, PhD
Computer Science Consulting and Training
7 Carmel St., #37
Rehovot  76305
Israel
 
Tel: +972-77-3436003
Mob: +972-54-3113572



STD-PROPOSALS list run by herb.sutter at gmail.com

Standard Proposals Archives on Google Groups