C++ Logo

std-proposals

Advanced search

Re: Add a specialized "Extension" concept to the inheritance syntax

From: Ofri Sadowsky <sadowsky.o.phd_at_[hidden]>
Date: Sun, 19 May 2019 18:58:06 +0300
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_at_[hidden]> 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_at_[hidden]> 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_at_[hidden]
>> 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

Received on 2019-05-19 11:00:01