Discussion:
[std-proposals] Overriding virtual functions of member class
a***@gmail.com
2017-11-02 22:35:27 UTC
Permalink
Hello,

Currently the only way to override a virtual function of a class A is by
inheriting A in class B (A is a base-class subobject of B). I propose that
the ability to override virtual functions is extended to when A is a member
object of B.

The syntax does not really matter at this point, but it could be something
like this:

struct A {
virtual void virtualFunction() = 0;
};

struct B {
A m_a;

void virtualFunctionImpl() override(m_a.virtualFunction) {
// Implementation
}
};

Semantically this would be much like if A was a base class of B and B
overrode the virtual function, except that A is a member and with that all
the usual semantics that come with this. Note how in this example the class
A is used as a field despite being abstract, since all its pure virtual
functions are implemented when embedded within B.

I propose only overriding virtual functions of a direct member class, not
more complicated cases like overriding the virtual function of a member
class inside a base class.

I think this would be useful in the context of single-threaded
callback-based event-driven design, where the virtual functions can
represent callbacks to the "owner" of a class. For example, consider a
simple timer facility:

class Timer : private boost::noncopyable {
public:
Timer(EventLoop &);
~Timer();
void start(uint64_t timeout);
void stop();
bool isRunning() const;
protected:
virtual void timerExpired() = 0;
private:
// implementation details...
};

Note: the Timer class provides useful guarantees such as 1) timerExpired()
is called only when the timer is actually running (stop() reliably prevents
a future callback), 2) Timer can be safely destructed from its own
timerExpired() callback.

I think this is a very nice interface, but use of virtual functions for the
callback implies that Timer must be inherited which has significant
drawbacks, such as:
- Problems when one class needs to use more than one Timer (wrapper classes
needed).
- Possible base class ambiguity, such as when class A inherits a Timer but
also inherits class B which itself inherits a Timer, even when private
inheritance is used. B using a Timer is an implementation detail and
shouldn't cause a compile error because A also uses a Timer.

The other way to implement the callback is with facilities like
std::function, but I think that is non-ideal:
- Boilerplate needed to bind the callback (especially the
std::placeholder's).
- std::bind/std::function is not noexcept and may require dynamic memory
allocation, this is unacceptable for certain embedded systems.
- Less memory efficient, each callback necessarily contributes at least 2
words to the total size of the containing object (a function pointer and an
instance pointer). But with virtual functions, all virtual functions in one
subobject together contribute only 1 word (the vtable pointer). Yes I know
the program memory (.text section) overhead is different.

I believe this feature can be implemented by generating a "special" vtable
for the member class for the case when the containing class overrides any
of its virtual function (it could be called "vtable for A in B"). Such a
vtable would have the same layout as the vtable for A except that the
function pointers for those overridden functions would point to member
functions of B. Note, just like if B was a base class of A at nonzero
offset, this may need generation of thunks, but this is really
ABI-dependant.

I do not propose any changes to the type system. The dynamic type of m_a is
A (not some kind of automatically generated derived class!), and
typeid(m_a) would still indicate that. The only new thing is that there is
something else that can override virtual functions.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/26216c34-9ff6-435b-8da5-9b21460fee1c%40isocpp.org.
Nicol Bolas
2017-11-02 22:47:24 UTC
Permalink
Post by a***@gmail.com
Hello,
Currently the only way to override a virtual function of a class A is by
inheriting A in class B (A is a base-class subobject of B). I propose that
the ability to override virtual functions is extended to when A is a member
object of B.
No.

What you're proposing doesn't make sense. Inheritance represents an "is-a"
relationship; overriding virtual functions is part of that. `A` defines an
interface which all things that are `A`s implement.

B is not an A. B& is not convertible to A& (unless you create a conversion
operator for that). So if you have an `A&`, it should not arbitrarily be
able to behave like a `B&`.

The dynamic type of an object should be sufficient information to know what
will happen if virtual functions are called. If `A` is not a `B`, then
calling `A`'s functions should not magically cause `B` functions to be
called.

At least, not without the `A` type having some syntax that ties it to `B`.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/48759c93-8f08-458e-ac0b-ea283b75faa1%40isocpp.org.
a***@gmail.com
2017-11-02 22:52:00 UTC
Permalink
I have heard that argument before but I think it is only the result of
people wanting correspondence with "OOP principles". There is no semantic
or implementation issue. Semantically, not much more than the definition of
"final overrider" changes, and implementation-wise the right vtable needs
to be generated. I reject the idea that C++ must strictly follow "OOP
principles".
Post by Nicol Bolas
Post by a***@gmail.com
Hello,
Currently the only way to override a virtual function of a class A is by
inheriting A in class B (A is a base-class subobject of B). I propose that
the ability to override virtual functions is extended to when A is a member
object of B.
No.
What you're proposing doesn't make sense. Inheritance represents an "is-a"
relationship; overriding virtual functions is part of that. `A` defines an
interface which all things that are `A`s implement.
B is not an A. B& is not convertible to A& (unless you create a conversion
operator for that). So if you have an `A&`, it should not arbitrarily be
able to behave like a `B&`.
The dynamic type of an object should be sufficient information to know
what will happen if virtual functions are called. If `A` is not a `B`, then
calling `A`'s functions should not magically cause `B` functions to be
called.
At least, not without the `A` type having some syntax that ties it to `B`.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/4517d88c-2eb1-455a-b9c3-22af9d698ccd%40isocpp.org.
Ville Voutilainen
2017-11-02 22:54:17 UTC
Permalink
Post by a***@gmail.com
I have heard that argument before but I think it is only the result of
people wanting correspondence with "OOP principles". There is no semantic or
implementation issue. Semantically, not much more than the definition of
"final overrider" changes, and implementation-wise the right vtable needs to
be generated. I reject the idea that C++ must strictly follow "OOP
principles".
Here's an interesting bit of your original email: "I believe this
feature can be implemented by generating a "special" vtable for the
member class..".
Indeed, so let's solve the problem by adding a reflection mechanism
that can make the delegation you currently need to write tediously
manually
more automatic. That's not even far from your suggestion, but happens
to be more generic than being able to designate a 'source' for
an override. Which has its pros and cons, of course.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAFk2RUaPmg-FMF7aE1evyRCMnpUz4i%2BDxpVubPvJJohfGcm5FA%40mail.gmail.com.
a***@gmail.com
2017-11-02 22:59:07 UTC
Permalink
Post by Ville Voutilainen
Here's an interesting bit of your original email: "I believe this
feature can be implemented by generating a "special" vtable for the
member class..".
Indeed, so let's solve the problem by adding a reflection mechanism
that can make the delegation you currently need to write tediously
manually
more automatic. That's not even far from your suggestion, but happens
to be more generic than being able to designate a 'source' for
an override. Which has its pros and cons, of course.
I'm not sure what you are actually proposing: a more general way to
override virtual functions, or a nicer way to implement delegation without
using virtual functions? If the latter, then how would you address my
concern about memory use overhead of std::function-like solutions compared
to virtual functions?
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/eb236e3b-c8a0-4fca-84e3-db0935e169c2%40isocpp.org.
Ville Voutilainen
2017-11-02 23:06:00 UTC
Permalink
Post by Ville Voutilainen
Here's an interesting bit of your original email: "I believe this
feature can be implemented by generating a "special" vtable for the
member class..".
Indeed, so let's solve the problem by adding a reflection mechanism
that can make the delegation you currently need to write tediously
manually
more automatic. That's not even far from your suggestion, but happens
to be more generic than being able to designate a 'source' for
an override. Which has its pros and cons, of course.
I'm not sure what you are actually proposing: a more general way to override
virtual functions, or a nicer way to implement delegation without using
virtual functions? If the latter, then how would you address my concern
about memory use overhead of std::function-like solutions compared to
virtual functions?
I wrote "the delegation you currently need to write".

Furthermore, you claim that there's no semantic or implementation
problem. That seems doubtful,
considering that you're suggesting that we add a mechanism for
plugging into the virtual dispatch
of a concrete object. That's very reflection-ish.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAFk2RUY7u0tUiLdHWm70smrUh77_ZqwXYfnoHxrTy_Gg24kkaw%40mail.gmail.com.
a***@gmail.com
2017-11-02 23:17:41 UTC
Permalink
Post by Ville Voutilainen
Furthermore, you claim that there's no semantic or implementation
problem. That seems doubtful,
considering that you're suggesting that we add a mechanism for
plugging into the virtual dispatch
of a concrete object. That's very reflection-ish.
My understaning of compiler implementation is that these changes are needed:
- Generation of an appropriate vtable for each case of containing class
overriding virtual functions of a member class.
- Initialization of the vtable pointer within the base class when the base
class is being constructed. Just like how the vtable pointer of a base
class is set to the vtable of the derived class.
- Identification of any code in the compiler which assumes that final
overrider of a virtual function is somewhere is in the same or a derived
class, and correction to deal with the case that it is in a containing
class. However, the "default" code generation for virtual function calls
(which lowers it to reading a function pointer from the vtable and calling
it) should not need changes.

Of course I will let myself be corrected by someone with real knowledge in
compiler implementation :)
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/e9b87378-3591-48e8-a94b-a1b5201a1e79%40isocpp.org.
a***@gmail.com
2017-11-02 23:18:51 UTC
Permalink
Post by a***@gmail.com
Initialization of the vtable pointer within the base class when the base
class is being constructed.
I mean of course "Initialization of the vtable pointer within the MEMBER
class when the MEMBER class is being constructed".
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/f1d90a4a-9b1b-44e8-8279-c06db37ac0e7%40isocpp.org.
Thiago Macieira
2017-11-02 23:52:57 UTC
Permalink
Post by a***@gmail.com
- Generation of an appropriate vtable for each case of containing class
overriding virtual functions of a member class.
- Initialization of the vtable pointer within the base class when the base
class is being constructed. Just like how the vtable pointer of a base
class is set to the vtable of the derived class.
- Identification of any code in the compiler which assumes that final
overrider of a virtual function is somewhere is in the same or a derived
class, and correction to deal with the case that it is in a containing
class. However, the "default" code generation for virtual function calls
(which lowers it to reading a function pointer from the vtable and calling
it) should not need changes.
In other words, deriving from the class.

For all intents and purposes, it's a new class that derived from the original,
with a new constructor that properly initialises the vtable, a new vtable,
typeinfo information, etc.

Given that that is so, what's the benefit? Only the syntax?
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/6065757.R1G8NcXUOa%40tjmaciei-mobl1.
a***@gmail.com
2017-11-03 07:35:57 UTC
Permalink
Post by Thiago Macieira
In other words, deriving from the class.
For all intents and purposes, it's a new class that derived from the original,
with a new constructor that properly initialises the vtable, a new vtable,
typeinfo information, etc.
Given that that is so, what's the benefit? Only the syntax?
- It's not a new class, the dynamic type of m_a is A.
- Yes there is a new vtable. Semantically what changes is something like
"the final overrider of a virtual function is F(dynamic type)" becomes "if
the contained class overrides the virtual function the final overrider is
the overriding function in the contained class, otherwise it is F(dynamic
type)". I know that's simplified because the final overrider is different
during construction but it would be essentially the same as for base class
construction.
- "with a new constructor that properly initialises the vtable" - I don't
see how there is a "new" constructor, just the containing class's
constructor initializes the vtable pointer at the right point(s), same as a
derived class's constructor does for a base class.
- No there is no different typeinfo structure, the same typeinfo as for A
can be used (at least I don's see a need for a new typeinfo).
- I already explained the benefits: containing class can easily have more
than one instance of the same class, no base class ambiguities, smaller
object size compared to std::function-like solutions.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/2391aab0-76d0-40ac-bafd-769b40439360%40isocpp.org.
Thiago Macieira
2017-11-03 15:17:10 UTC
Permalink
Post by a***@gmail.com
Post by Thiago Macieira
In other words, deriving from the class.
For all intents and purposes, it's a new class that derived from the original,
with a new constructor that properly initialises the vtable, a new vtable,
typeinfo information, etc.
Given that that is so, what's the benefit? Only the syntax?
- It's not a new class, the dynamic type of m_a is A.
Yes, it is. If it has a different vtable, then typeid() on it MUST return a
different value. That's a different class.
Post by a***@gmail.com
- Yes there is a new vtable. Semantically what changes is something like
"the final overrider of a virtual function is F(dynamic type)" becomes "if
the contained class overrides the virtual function the final overrider is
the overriding function in the contained class, otherwise it is F(dynamic
type)". I know that's simplified because the final overrider is different
during construction but it would be essentially the same as for base class
construction.
That's still the same. The base class as well as any callers using a pointer
to the base class pointer don't care how the overriding takes place, only that
it does.
Post by a***@gmail.com
- "with a new constructor that properly initialises the vtable" - I don't
see how there is a "new" constructor, just the containing class's
constructor initializes the vtable pointer at the right point(s), same as a
derived class's constructor does for a base class.
That's again a change without a difference.

If you modify the member sub-object just after its regular constructor has
finished, the modifcation is happening at the exact same point in the code
execution that a more-derived class's constructor would have run.
Post by a***@gmail.com
- No there is no different typeinfo structure, the same typeinfo as for A
can be used (at least I don's see a need for a new typeinfo).
I don't see why we should make a different rule. It should have a different
typeinfo because it has a different vtable.
Post by a***@gmail.com
- I already explained the benefits: containing class can easily have more
than one instance of the same class, no base class ambiguities, smaller
object size compared to std::function-like solutions.
None of which is impossible today. You can already do that, by simply deriving
from that base class. From your example, modified to have two objects:

struct A {
virtual void virtualFunction() = 0;
};

struct B {
struct A1 : A {
void virtualFunction) override;
} m_a1;
struct A2 : A {
void virtualFunction) override;
} m_a2;
};

They don't have to be declared inside B, I did that only to show it can be
done.

This is identical to what you're proposing: both sub-objects m_a1 and m_a2 are
instances of A, with a different vtable, with different overrides to
virtualFunction(). There's no extra overhead of using std::function and
there's no ambiguity.

So I ask again: aside from the syntax, what are the benefits?
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/2354431.YAgt9N0pFV%40tjmaciei-mobl1.
a***@gmail.com
2017-11-02 23:08:35 UTC
Permalink
There is a reason that the memory overhead of virtual functions is less
than std::function, it is that the implementation knows the fixed offset
needed to calculate a pointer to B from a pointer to A (A being a base
class of B or with my proposal a member class). Any solution which does not
exploit this will not achieve this kind of efficiency.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/368dff55-d1d5-4df8-ba7f-e5e818a5e5a2%40isocpp.org.
Nicol Bolas
2017-11-03 03:03:30 UTC
Permalink
Post by a***@gmail.com
I have heard that argument before but I think it is only the result of
people wanting correspondence with "OOP principles".
Well... yes. We want features that make sense. It does not make sense for a
virtual call to an object of dynamic type `A` to directly call a function
in an *unrelated* object `B`.

If you want an arbitrary vtable for a class, then implement that.

There is no semantic or implementation issue. Semantically, not much more
Post by a***@gmail.com
than the definition of "final overrider" changes, and implementation-wise
the right vtable needs to be generated.
In your example, `m_a` is not a reference or pointer to an `A`; it is an
object of type `A`. Therefore, every call to a virtual function through
`m_a`, right now, can be de-virtualized by the compiler. No vtable lookup,
no performance loss.

So your change would require changes to the de-virtualization components of
a compiler. It can't simply see that you're accessing a
non-reference-to-a-polymorphic type to de-virtualize those calls. It now
has to inspect the location the object came from. If its a member
subobject, it has to inspect the containing object to see if it injects any
virtual functions into that object.

Also, let's not forget that the virtual call will be given a `this` pointer
that points to the type `A`. While the virtual function that will
eventually use it is of type `B`, which is *not convertible to `A`*. Which
means that the actual virtual dispatch will need to generate a conversion
from that subobject of `B` to `B` itself. That's not so difficult of
course, but it's also not something automatic.

And what of inherited members? That is, `C` derives from `B` which has a
member of type `A`? Can `C` inject virtual functions into that member of
`B`? If not, why not? And if so, how does this work with virtual
inheritance?

So no, this is not a simple thing.

I reject the idea that C++ must strictly follow "OOP principles".
C++ in no way "strictly follows OOP principles," with or without this
feature. But C++ does have an internal consistency to what it implements.
Virtual functions are not an arbitrary vtables mechanism; they're a
specific methods to extend the behavior of a type through inheritance.

After all, I could just as easily argue that your suggestion is quite
limiting. Why should this be restricted to non-static data members? Why
should I not be able to simply take *any object* and *inject* compatible
functions into its vtable at construction time? Why limit this to just
non-static data members of some other type?

If we're going to treat virtual functions as nothing more than slots in a
vtable, let's be serious about it. It wouldn't even make devirtualization
harder than your proposal, since the devirtualizer would have to look at
the object's declaration (where the injection would take place).

Why stop half-way?
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/2d810f36-74c5-4644-8eeb-f81db4e8687e%40isocpp.org.
a***@gmail.com
2017-11-03 08:11:12 UTC
Permalink
Post by Nicol Bolas
Well... yes. We want features that make sense. It does not make sense for
a virtual call to an object of dynamic type `A` to directly call a function
in an *unrelated* object `B`.
B is A's containing class, that is a relation :)
Post by Nicol Bolas
If you want an arbitrary vtable for a class, then implement that.
That seems overkill for my proposals. Remember I want the solution to make
use of the fact that B is at a known offset compared to A, to minimize
object size. Also note that C++ does not define offsetof() for
non-aggregate classes so it can't be hacked manually in a portable way.
Under that assumption, you anyway could not get much farther from what I
propose, you are limited to overriders being somewhere within the "most
containing object".
Post by Nicol Bolas
There is no semantic or implementation issue. Semantically, not much more
Post by a***@gmail.com
than the definition of "final overrider" changes, and implementation-wise
the right vtable needs to be generated.
In your example, `m_a` is not a reference or pointer to an `A`; it is an
object of type `A`. Therefore, every call to a virtual function through
`m_a`, right now, can be de-virtualized by the compiler. No vtable lookup,
no performance loss.
Yes I agree that this would affect devirtualization, but I think the
problem of checking that no containing type overrides that virtual function
of that object is equivalently hard as checking that no derived class
overrides it (which already must be implemented).
Post by Nicol Bolas
So your change would require changes to the de-virtualization components
of a compiler. It can't simply see that you're accessing a
non-reference-to-a-polymorphic type to de-virtualize those calls. It now
has to inspect the location the object came from. If its a member
subobject, it has to inspect the containing object to see if it injects any
virtual functions into that object.
Just like it has to inspect a derived object to see if it injects any
virtual functions!
Post by Nicol Bolas
Also, let's not forget that the virtual call will be given a `this`
pointer that points to the type `A`. While the virtual function that will
eventually use it is of type `B`, which is *not convertible to `A`*.
Which means that the actual virtual dispatch will need to generate a
conversion from that subobject of `B` to `B` itself. That's not so
difficult of course, but it's also not something automatic.
Of course it's not difficult, subtract the known offset of A within B, the
same way it's done to get the derived class pointer from a base class
pointer.
Post by Nicol Bolas
And what of inherited members? That is, `C` derives from `B` which has a
member of type `A`? Can `C` inject virtual functions into that member of
`B`?
I did mention that I do not suggest that ability initially, only virtual
functions of direct members could be overridden.

If not, why not? And if so, how does this work with virtual inheritance.
If B contains A as a member and V is a virtual base of A, B could override
virtual functions of V. I think the same approach to get A* from V* is
useful, because A is a complete object (this instance it is a member) and
as such the layout of its virtual bases is known.
Post by Nicol Bolas
C++ in no way "strictly follows OOP principles," with or without this
feature. But C++ does have an internal consistency to what it implements.
Virtual functions are not an arbitrary vtables mechanism; they're a
specific methods to extend the behavior of a type through inheritance.
All I want is to add another, sometimes more practical, approach to extend
the behavior of a type.
Post by Nicol Bolas
After all, I could just as easily argue that your suggestion is quite
limiting. Why should this be restricted to non-static data members? Why
should I not be able to simply take *any object* and *inject* compatible
functions into its vtable at construction time? Why limit this to just
non-static data members of some other type?
The user would presumably provide function pointers to functions which
receive the "this" pointer either to to the class where the virtual
function is (first) declared or to the class whose function you directly
request to override; in either case you would have the problem of
converting that to a pointer to the containing class. But offsetof() is not
guaranteed to work for non-aggregate classes.

You would also need to emulate the vtable pointer adjustments currently
performed by derived classes. Consider this (this is my probably incomplete
understanding of how it works in GCC):
- During construction of a base A class its vtable pointer points to
"vtable for A" (except if it has any virtual bases then it points to
"construction vtable for A" which is just like "vtable for A" but has
correct virtual base offsets to reach the virtual bases which are at the
end of B).
- Just after successful construction of A, the derived class B adjusts the
vtable pointer of A to point into the right section of "vtable for B".
- Any more derived classes would also need to adjust the vtable pointer of
A when their constructor body starts.

What I'm trying to say is that such a mechanism would be quite complicated,
both as a mechanism and for the user. If you just allow the containing
class to override virtual functions, these problems are solved by the
compiler and the solution is almost always "do it as it's done for base
classes".
Post by Nicol Bolas
If we're going to treat virtual functions as nothing more than slots in a
vtable, let's be serious about it. It wouldn't even make devirtualization
harder than your proposal, since the devirtualizer would have to look at
the object's declaration (where the injection would take place).
I think I already answered that.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/8383bc2f-638b-4519-ac20-8cfbfc9708d5%40isocpp.org.
Nicol Bolas
2017-11-03 16:19:11 UTC
Permalink
Post by a***@gmail.com
Post by Nicol Bolas
Well... yes. We want features that make sense. It does not make sense for
a virtual call to an object of dynamic type `A` to directly call a function
in an *unrelated* object `B`.
B is A's containing class, that is a relation :)
Member subobjects and base class subobojects are different. Base classes
represent "is a"; members represent "has a."

Passing someone an A& which is a B is a different thing from passing
someone an A& which happens to be held by a B. Logically, if you copy from
an A which is held by a B, the object you have ought to have the same
behavior as the one held by B. Similarly, if you copy from a lone A into an
A which is held by a B, the held object ought to have the same behavior as
the lone A.

By contrast, if you copy a lone A into an A which *is a* B, it shouldn't
have the same behavior. And it won't. And vice-versa. You cannot copy part
of a thing and expect it to work like the thing itself.

Your idea removes this very reasonable "is a/has a" distinction. If your
idea makes sense from an object relationship perspective, then explain what
it means for B to override a method of a member A. The relationship is not
"is a", since the A's type is not B. The relationship is not "has a", since
you cannot separate the value of A from B and still have a functioning
object. It is not "implemented in terms of".

So what is this relationship? Because if you can't explain what it *means*,
then the feature is semantic nonsense. It's just "inject some functions
into the vtable". It doesn't *mean* anything.

Allow me to present an analogy, to explain what I mean by "mean something".
I was at one point toying around with inner classes in C++
<https://github.com/NicolBolas/Proposal-Ideas/blob/master/older/Stateless%20And%20Inner%20Classes.md>.
In that idea, an inner class is a member subobject, but by being an inner
class, it is effectively part of the containing class. Inner class
instances are implicitly convertible to the other class, exactly as if it
were derived from it. And so forth.

So what is the relationship between an inner class subobject and its
container? It is not "is a" or "has a"; it is "*part of a*". The inner
class member subobject is "part of" the containing object. And being "part
of" it has limitations:

* The inner class must be defined within the containing class.
* Objects of inner class types must be created as member subobjects of the
containing class (or a class non-virtually derived from it).

These rules ensure and enforce the "part of a" relationship. The inner
class is an inseparable part of the type it's a member of, so you can't
copy it around to something else. And the relationship is what allows the
inner class to access the outer class's members as if they were its own.

The relationship your proposal talks about seems kind of like "part of a",
but it has two flaws. One, the class that's part of the containing class
has no way to access it; the "part of" is something being done *to it*, not
something it is a party to. The other flaw is that there's no enforcement
mechanism. You can slice the "part of" effectively.

So what exactly is the semantic relationship between B and A here? Can you
give a phrase that represents it? Or are you just poking at the vtable?

If you want an arbitrary vtable for a class, then implement that.
Post by a***@gmail.com
That seems overkill for my proposals.
I meant to *manually* implement a vtable. As in, create a struct of
function pointers, make it a member of `A`, initialize it in `A`'s
constructor, then allow `B` to manipulate it.

That is, in the rare cases where you need this (I, for one, never have),
you should just do the work manually.

Remember I want the solution to make use of the fact that B is at a known
Post by a***@gmail.com
offset compared to A, to minimize object size. Also note that C++ does not
define offsetof() for non-aggregate classes so it can't be hacked manually
in a portable way.
Correction: it's standard layout, not aggregate.
Post by a***@gmail.com
Under that assumption, you anyway could not get much farther from what I
propose, you are limited to overriders being somewhere within the "most
containing object".
Post by Nicol Bolas
There is no semantic or implementation issue. Semantically, not much more
Post by a***@gmail.com
than the definition of "final overrider" changes, and implementation-wise
the right vtable needs to be generated.
In your example, `m_a` is not a reference or pointer to an `A`; it is an
object of type `A`. Therefore, every call to a virtual function through
`m_a`, right now, can be de-virtualized by the compiler. No vtable lookup,
no performance loss.
Yes I agree that this would affect devirtualization, but I think the
problem of checking that no containing type overrides that virtual function
of that object is equivalently hard as checking that no derived class
overrides it (which already must be implemented).
Part of devirtualization is based on tracking down where the actual object
being pointed-to/referenced came from. That is, is the dynamic type of the
pointer/reference in question. If it is not a base class subobject, you
need not look further to know that you can devirtualize any call. This now
much become a more complex test: *every* member subobject must be assumed
to potentially be overridden. So you have to look through the class
definition to make sure (or at least, you have to build that information
into the containing type). That's making devirtualization more complex.

And it's not a matter of "easy vs. hard". It's a matter of "doing the work
vs. not doing the work." You categorized the necessary changes to implement
your feature as merely creating a new vtable and a couple of compiler
tweaks. What I'm saying is that it just isn't that simple.

So your change would require changes to the de-virtualization components of
Post by a***@gmail.com
Post by Nicol Bolas
a compiler. It can't simply see that you're accessing a
non-reference-to-a-polymorphic type to de-virtualize those calls. It now
has to inspect the location the object came from. If its a member
subobject, it has to inspect the containing object to see if it injects any
virtual functions into that object.
Just like it has to inspect a derived object to see if it injects any
virtual functions!
Post by Nicol Bolas
Also, let's not forget that the virtual call will be given a `this`
pointer that points to the type `A`. While the virtual function that will
eventually use it is of type `B`, which is *not convertible to `A`*.
Which means that the actual virtual dispatch will need to generate a
conversion from that subobject of `B` to `B` itself. That's not so
difficult of course, but it's also not something automatic.
Of course it's not difficult, subtract the known offset of A within B, the
same way it's done to get the derived class pointer from a base class
pointer.
Again, it's not a question of difficult vs. easy. It's a question of having
to do the work. It's one more thing to do.

And what of inherited members? That is, `C` derives from `B` which has a
Post by a***@gmail.com
Post by Nicol Bolas
member of type `A`? Can `C` inject virtual functions into that member of
`B`?
I did mention that I do not suggest that ability initially, only virtual
functions of direct members could be overridden.
If not, why not? And if so, how does this work with virtual inheritance.
If B contains A as a member and V is a virtual base of A, B could override
virtual functions of V. I think the same approach to get A* from V* is
useful, because A is a complete object (this instance it is a member) and
as such the layout of its virtual bases is known.
Post by Nicol Bolas
C++ in no way "strictly follows OOP principles," with or without this
feature. But C++ does have an internal consistency to what it implements.
Virtual functions are not an arbitrary vtables mechanism; they're a
specific methods to extend the behavior of a type through inheritance.
All I want is to add another, sometimes more practical, approach to extend
the behavior of a type.
By introducing a way to arbitrarily alter an object's vtable. Again,
virtual functions are meant to be extended *through inheritance*. That
creates a logical foundation for virtual functions as a feature. Extending
them in some other way is merely syntactic convenience, not a logical
progression of the idea.

That is, you're piggybacking off of virtual functions because they're there
and will do the job you want done, not because it leads to a natural
relationship between B and A. You're creating a language hack.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/389229bd-7f50-4efa-b6c9-177df26bcd0a%40isocpp.org.
a***@gmail.com
2017-11-04 11:07:46 UTC
Permalink
I feed like the same arguments are being repeated without much progress on
the issues. Yes, some things that currently hold would no longer hold at
least with how I proposed to implement this.
Post by Thiago Macieira
Post by a***@gmail.com
- It's not a new class, the dynamic type of m_a is A.
Yes, it is. If it has a different vtable, then typeid() on it MUST return
a
Post by Thiago Macieira
different value. That's a different class.
That's your preconceived idea about what typeid() means. I see no technical
reason that in this case of containing class B overriding virtual functions
of a contained class A, the contained class would point to a vtable
specific to "A in B" but typeid() would still return the same thing as if A
was completely stand-alone. Remember that in the GCC (Itanium) ABI, the
result of typeid() is defined by a pointer inside the vtable (one slot
above the first virtual function), the compiler can easily have both
vtables point to the same typeinfo.

The reason I suggest that "A in B" does not have a different typeinfo is
that the user did not declare a derived class of A, they declared A as a
member of B and as such typeid() claiming that the member is an A makes
sense.

But this is not the only way, the other approach is to make the compiler
return a different typeinfo which I think is also acceptable.
Post by Thiago Macieira
That's still the same. The base class as well as any callers using a
pointer
Post by Thiago Macieira
to the base class pointer don't care how the overriding takes place, only
that
Post by Thiago Macieira
it does.
I don't see a question or issue here. I suppose there might be rare
instances of code which looks at typeid(*this) and upon seeing something
specific makes certain assumptions about the behavior of virtual functions,
however I think that it is acceptable for such code to become broken if
this feature is used.
Post by Thiago Macieira
If you modify the member sub-object just after its regular constructor
has
Post by Thiago Macieira
finished, the modifcation is happening at the exact same point in the
code
Post by Thiago Macieira
execution that a more-derived class's constructor would have run.
What point is being made here? Yes, the constructors and destructors of the
containing class would adjust the vtable pointers of the contained class in
much the same way as if the contained class was a base class.
Post by Thiago Macieira
struct A {
virtual void virtualFunction() = 0;
};
struct B {
struct A1 : A {
void virtualFunction() override;
} m_a1;
struct A2 : A {
void virtualFunction() override;
} m_a2;
};
The issue with this approach is that the implementations of
virtualFunctions() will need to get B* from a pointer to A1*/A2*. That
cannot be done in general (only when B is a standard-layout class by using
offsetof). So to be portable code, A1 and A2 would each need to have a "B
*parent" member. That adds size overhead and requires boilerplate.

A similar solution to this is for B to inherit A1 and A2, this makes things
slightly simpler because you can use static_cast to get B* from A1*/A2*.
But it has the same boilerplate problem. There is also additional annoyance
if the A type is dependent on template parameters of B, because there is no
place where you can do type aliases like "using A = typename
TemplateParameter::A".
Post by Thiago Macieira
So I ask again: aside from the syntax, what are the benefits?
Let me summarize:

- Using A1 and A2 as members of B:
- Needs parent pointers to B in A1/A2 or generally nonportable offsetof
hacks.
- Boilerplate:
- Definition of A1 and A2 including their constructors
- Assinning parent pointers
- Forwarding overridden functions to functions within B (since code
looks nicer when it doesn't need b-> everywhere).

- Using A1 and A2 as base classes of B:
- A1 and A2 are visible outside the class even with private inheritance
- This is not that bad compared to inheriting B directly
- But their members are still visible and can cause problems
- Boilerplate:
- Definition of A1 and A2 including their constructors
- static_cast<B*>(this) in overridden functions
- Forwarding overridden functions to functions within B

- With this feature:
- Member classes are effectively invisible outside this class (e.g. will
never cause base class ambiguity)
- No boilerplate (no extra classes, no static-cast, no forwarding of
calls).
- Same good size efficiency as with base classes.
Post by Thiago Macieira
Member subobjects and base class subobojects are different. Base classes
represent "is a"; members represent "has a."

This is just OOP theory. I don't see why "is a" should be the only way to
extend behavior of the subobject and not also "has a".
Post by Thiago Macieira
Passing someone an A& which is a B is a different thing from passing
someone an A& which happens to be held by a B. Logically, if you copy from
an A which is held by a B, the object you have ought to have the same
behavior as the one held by B. Similarly, if you copy from a lone A into an
A which is held by a B, the held object ought to have the same behavior as
the lone A.

In the case where my feature would be used, copying "just" an A held by a B
would not make much sense, much like copying just a base class A of B often
makes no sense. You could still generally copy the entire B including its
A. If A is an abstract class then trying to make a stand-alone copy of A
would even be a compile error.

I think these arguments boil down to "has-a should not have that capability
for such and such OOP-theoretical reasons and because that's how it is now".
Post by Thiago Macieira
The relationship is not "has a", since you cannot separate the value of A
from B and still have a functioning object. It is not "implemented in terms
of".

I disagree completely. In event-driven callback-based programming this is
clearly not the case since A needs to be able to invoke callbacks on B,
whatever mechanism is used. Even if the mechanism is std::function, you
*also* cannot just separate A from B, since the callbacks have to go
somewhere.

Also consider that one can have non-copyable objects, if this ability to
separate objects from their container was so essential as you think it is,
then this would not be allowed.
Post by Thiago Macieira
So what is this relationship? Because if you can't explain what it *means*,
then the feature is semantic nonsense. It's just "inject some functions
into the vtable". It doesn't *mean* anything.

It means "B is the owner of A" and in the context of event-driven
programming this in addition to "B can call into A" sometimes also includes
"A can call into B using defined interaction points".
Post by Thiago Macieira
I was at one point toying around with inner classes in C++
<https://github.com/NicolBolas/Proposal-Ideas/blob/master/older/Stateless%20And%20Inner%20Classes.md>.
In that idea, an inner class is a member subobject, but by being an inner
class, it is effectively part of the containing class.

I think your inner classes proposal does cover my use case. But you propose
implementation using hidden offset variables. This seems redundant when
there will be just one instance of the stateful inner class. Have you
considered not having any offset variable in this case?
Post by Thiago Macieira
That is, you're piggybacking off of virtual functions because they're
there and will do the job you want done, not because it leads to a natural
relationship between B and A. You're creating a language hack.

Of course, I want to make an existing mechanism more flexible. That seems
much more reasonable than a completely new mechanism for delegation. I
wouldn't call it a hack though. What I would call a hack is what I
currently need to do - create a derived class of something just so that I
can do delegation efficiently.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/5b06f168-cde4-45a9-8614-6025c8b16e85%40isocpp.org.
Nicol Bolas
2017-11-04 15:49:06 UTC
Permalink
Post by a***@gmail.com
I feed like the same arguments are being repeated without much progress on
the issues. Yes, some things that currently hold would no longer hold at
least with how I proposed to implement this.
Well, our position is, "this makes a mockery of the inheritance/virtual
model and plays havoc with the logical foundation of everything." Your
position is essentially "... yes, it does. But *I don't care*."

The question you've neglected to answer is why we should care more about
your use cases than the logical foundations of the system. I don't see
anything from your motivating examples that *warrants* such a radical
change to the virtual model.

Is it awkward to do what you want using existing tools? Yes. But what you
want is a sufficiently rare need that taking a sledgehammer to the
inheritance model, of allowing virtual functions to be overriden by code
that is not within its dynamic type, is just overkill.

I don't see the benefits outweighing the costs here. And I'm not just
talking about implementation costs; I'm talking about "making sense" costs.
You don't see that as a cost, but that doesn't mean it isn't one.

C++ as a language has a hard enough time "making sense" as it is. We
shouldn't be adding things to make it make even less sense.
Post by a***@gmail.com
Member subobjects and base class subobojects are different. Base classes
represent "is a"; members represent "has a."
This is just OOP theory. I don't see why "is a" should be the only way to
extend behavior of the subobject and not also "has a".
I don't see why I shouldn't be able to manipulate an object's private
variables or call private functions either. That's "just OOP theory" too.
So is the inability to inject functions into an existing class.

Theoretical foundations for features are not bad. Indeed, I've yet to see a
good language feature that didn't have a solid theoretical foundation.
Post by a***@gmail.com
The relationship is not "has a", since you cannot separate the value of A
from B and still have a functioning object. It is not "implemented in terms
of".
I disagree completely. In event-driven callback-based programming this is
clearly not the case since A needs to be able to invoke callbacks on B,
whatever mechanism is used. Even if the mechanism is std::function, you
*also* cannot just separate A from B, since the callbacks have to go
somewhere.
But if I copy an `A` which has a `std::function` referencing something in
`B`, the copy *still works* (so long as the `B` still exists). There is a
relationship between the two, but that relationship is preserved upon
copying.

So you very much can separate the two with actual callbacks. These
connections only become inseparable if you use a language mechanism based
on `virtual` functions.

Also consider that one can have non-copyable objects, if this ability to
Post by a***@gmail.com
separate objects from their container was so essential as you think it is,
then this would not be allowed.
`A` is a copyable type. `m_a` in your proposal is not a copyable *object*.
There's a huge difference there. In the first case, you're talking about a
fundamental property of the type: is the type copyable or not. In the
second case, you're talking about a property of a specific *object*.

Base class subobjects are not copyable. Member subobjects are copyable.
Post by a***@gmail.com
So what is this relationship? Because if you can't explain what it *means*,
then the feature is semantic nonsense. It's just "inject some functions
into the vtable". It doesn't *mean* anything.
It means "B is the owner of A" and in the context of event-driven
programming this in addition to "B can call into A" sometimes also includes
"A can call into B using defined interaction points".
But that's not what you've done. `A` isn't calling into `B`. Some code
calling `A` (which may or may not be part of `A`) will instead call `B`.
And we already have a way to do that: it's called "inheritance-based
polymorphism".

This whole feature would make *far more sense* to simply allow you to have
named base classes than to do things this way. Everything you're talking
about as far as the relationship between `B` and its overridden `A`
represents "is a" or "is implemented in terms of", not "has a". The
restrictions on copying and slicing. And so forth. It all points to
inheritance, not containment.

And C++ already has multiple inheritance as a basic feature. The only
reason you're not using it right now is because you can't have multiple
base classes of the same type at the same scope.

So change *that*. Don't destroy the meaning of "member"; extend the way
inheritance works.

This is why having meaningful abstractions is a good thing, rather than
just seeing them as a block of features that you can do with as you please.
Post by a***@gmail.com
I was at one point toying around with inner classes in C++
<https://github.com/NicolBolas/Proposal-Ideas/blob/master/older/Stateless%20And%20Inner%20Classes.md>.
In that idea, an inner class is a member subobject, but by being an inner
class, it is effectively part of the containing class.
I think your inner classes proposal does cover my use case.
How? In your case, there's no relationship between the class definitions of
`A` and `B`. Whereas with inner classes, the inner class *must* be declared
as a nested class of the class it is used within.

But you propose implementation using hidden offset variables. This seems
Post by a***@gmail.com
redundant when there will be just one instance of the stateful inner class.
Have you considered not having any offset variable in this case?
The offset is conceptual, not necessarily actual. That is, it is an
implementation detail. Much like vtables, there is no specific language
that says that an offset must exist. But much like the restrictions on
virtual types, we have to put restrictions in place to *allow* compilers to
put an offset there. If the implementation can avoid adding an offset in
some cases, that's fine. Just like devirtualization is fine if the
implementation can do it.

But just because a compiler can devirtualize a `final` type entirely
doesn't mean that the type suddenly becomes trivially copyable. And the
same goes here: the stateful restrictions on the behavior of the type still
have to apply even if the compiler doesn't necessarily need an offset.

Also, in order to do what you suggest, you have to remove the ability to
put inner class members in classes derived from their owner. After all, if
all you have is an `A&`, you need that offset to figure out how to convert
your `this` to the containing class, since you may not have access to the
derived class from the location the compiler can see.

Though as I type this, I suddenly realize that the same applies to
stateless inner class subobjects of classes derived from their owning
class: you can only compute the offset correctly if you know have access to
the derived class the reference comes from. And you know that the reference
comes from there. So the ability to declare an inner class member of a
class derived from an owning class may have to go.

It probably wasn't very useful anyway.
Post by a***@gmail.com
That is, you're piggybacking off of virtual functions because they're
there and will do the job you want done, not because it leads to a natural
relationship between B and A. You're creating a language hack.
Of course, I want to make an existing mechanism more flexible. That seems
much more reasonable than a completely new mechanism for delegation. I
wouldn't call it a hack though. What I would call a hack is what I
currently need to do - create a derived class of something just so that I
can do delegation efficiently.
To me, a "hack" is the use of a tool for a purpose that is wildly divergent
from its clearly intended purpose, to the point where its use there is
unsightly, awkward, or irrational. A screwdriver with a suitability hard
handle might make for a serviceable hammer, but that doesn't mean you
should use it as one.

Using the existing virtual mechanism to implement arbitrary delegation of
this sort is like using a screwdriver as a hammer. Does it work? Yes. But
it's unsightly (the `override` statement), awkward (affects copying
behavior of member subobjects), and irrational (doesn't have a well-defined
meaning the way inheritance does).

If you don't have a hammer, you shouldn't tell the screwdriver maker that
they need to make their screwdrivers more hammer-like. You should go get an
actual hammer.

Similarly, if you don't have delegation support in the language, you
shouldn't complain about virtual functions not allowing delegation. You
should instead come up with a delegation feature for the language. One that
doesn't look like a screwdriver.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/6c6eec8c-5585-4cce-9517-5012ea20bac5%40isocpp.org.
a***@gmail.com
2017-11-04 18:43:01 UTC
Permalink
Post by Nicol Bolas
Well, our position is, "this makes a mockery of the inheritance/virtual
model and plays havoc with the logical foundation of everything." Your
position is essentially "... yes, it does. But *I don't care*."
I think it is rather "it does modify the existing ideas somehow but not in
a too radical fashion, in the right context it still makes sense".
Post by Nicol Bolas
The question you've neglected to answer is why we should care more about
your use cases than the logical foundations of the system. I don't see
anything from your motivating examples that *warrants* such a radical
change to the virtual model.
Is it awkward to do what you want using existing tools? Yes. But what you
want is a sufficiently rare need that taking a sledgehammer to the
inheritance model, of allowing virtual functions to be overriden by code
that is not within its dynamic type, is just overkill.
I am far from the only person doing event-driven callback-based
programming. Look at almost every GUI toolkit. Some (Qt) even use
preprocessors to achieve concise use of callbacks.
Post by Nicol Bolas
C++ as a language has a hard enough time "making sense" as it is. We
shouldn't be adding things to make it make even less sense.
In one respect this ability I propose makes the language more regular,
because now a container can override virtual functions of *both* its base
and member subobjects.
Post by Nicol Bolas
Base class subobjects are not copyable. Member subobjects are copyable.
How are base class subobjects not copyable, what is stopping you from doing
"BaseClass(static_cast<BaseClass&>(*this))" (other than BaseClass possibly
being abstract)? And clearly the default copy-constructor of a class will
copy-construct its bases.
Post by Nicol Bolas
Post by Nicol Bolas
So what is this relationship? Because if you can't explain what it
*means*, then the feature is semantic nonsense. It's just "inject some
functions into the vtable". It doesn't *mean* anything.
It means "B is the owner of A" and in the context of event-driven
programming this in addition to "B can call into A" sometimes also includes
"A can call into B using defined interaction points".
But that's not what you've done. `A` isn't calling into `B`. Some code
calling `A` (which may or may not be part of `A`) will instead call `B`.
And we already have a way to do that: it's called "inheritance-based
polymorphism".
I think you don't imagine what kind of code I'm talking about. In my
example with a Timer class, from the perspective of the containing class,
it really is the Timer class calling the timerExpired() callback of its
containing class. Under the hood the Timer will be intimately coupled to
the event loop and it is the event loop initiating the call.
Post by Nicol Bolas
This whole feature would make *far more sense* to simply allow you to
have named base classes than to do things this way. Everything you're
talking about as far as the relationship between `B` and its overridden `A`
represents "is a" or "is implemented in terms of", not "has a". The
restrictions on copying and slicing. And so forth. It all points to
inheritance, not containment.
And C++ already has multiple inheritance as a basic feature. The only
reason you're not using it right now is because you can't have multiple
base classes of the same type at the same scope.
So change *that*. Don't destroy the meaning of "member"; extend the way
inheritance works.
What you suggest would indeed help. But I think that inheritance is not the
right solution exactly for OOP-theoretical reasons: in these use cases I am
addressing, "is-a" does not describe the relation well and "has-a" seems
more correct.

Let me show a real-world example. It is supposed to implement the handling
of a TCP connection on a server, where the server receives requests from a
client, performs a DB query for each requests and sends the results back to
the client. It also has a timeout to disconnect the client after specific
idle time.

class Client {
private:
TcpConnection m_connection; // represents a TCP connection
Timer m_timer; // timer used to disconnect
DatabaseAccess m_database; // represents the interface to a database
ClientManager &m_client_manager; // "parent" object which manages
clients

public:
Client(ClientManager &client_manager, TcpListener &listener) :
m_client_manager(client_manager)
{
m_connection.acceptConnection(listener);
m_timer.setAfter(1000);
}

private:
void dataReceived(data) override(m_connection.dataReceived) {
add data to buffer;
parseReceivedData();
}

void parseReceivedData() {
check data in buffer;
if (complete command available) {
m_timer.stop(); // stop request timeout while DB query is active
m_database.startQuery(...);
}
}

void databaseQueryCompleted(result) override(m_database.queryCompleted)
{
m_connection.send(response based on result);
m_timer.setAfter(1000); // restart request timeout while waiting
for next request
shift command from buffer;
parseReceivedData(); // try to parse any already buffered data
}

void timerExpired() override(m_timer.timerExpired) {
// Remove this client; ClientManager will remove this client from
its collection of
// clients, destructing this object.
m_client_manager.clientDisconnected(*this);
}
};


Let's consider the OOP relations between Client and its subobjects,
independently of the syntax I used (I could as well have used private
inheritance and that shouldn't affect the reasoning):
- An argument could be made that Client *is* a TcpConnection, but it's also
not wrong to say "has" especially considering that TcpConnection is
invisible to anything outside the Client class.
- But, clearly Client is not a Timer or DatabaseAccess. I would say that
Client "uses" those two, but if we're limited to "is" or "has", "has" seems
better.
Post by Nicol Bolas
Post by Nicol Bolas
I think your inner classes proposal does cover my use case.
How? In your case, there's no relationship between the class definitions
of `A` and `B`. Whereas with inner classes, the inner class *must* be
declared as a nested class of the class it is used within.
In my example above, Client could declare an inner class inheriting from
Timer, overriding timerExpired(), and use exactly one instance of that
inner class as a member of Client. The overridden timerExpired() would have
full access to Client that is needs.
Post by Nicol Bolas
Using the existing virtual mechanism to implement arbitrary delegation of
this sort is like using a screwdriver as a hammer. Does it work? Yes. But
it's unsightly (the `override` statement), awkward (affects copying
behavior of member subobjects), and irrational (doesn't have a well-defined
meaning the way inheritance does).
If you don't have a hammer, you shouldn't tell the screwdriver maker that
they need to make their screwdrivers more hammer-like. You should go get an
actual hammer.
Similarly, if you don't have delegation support in the language, you
shouldn't complain about virtual functions not allowing delegation. You
should instead come up with a delegation feature for the language. One that
doesn't look like a screwdriver.
I think that my extension does *not* break OOP principles in general. The
problem is that these principles were created without consideration for
event-driven programming. If they were, they would have the delegation
mechanism I seek.

Here's a nice analogy which I think addresses many of the conceptual
objections.
- Consider a human which has an arm. Clearly a human is not its arm, it has
an arm.
(like my Client which has a Timer)
- The arms are not meant to be separated from the body.
(Timers are not meant to exist without an object owning them)
- The arms cannot be duplicated (and the question of which body the new arm
would be connected to is moot).
(Timers cannot be copied, if some other object needs a timer it would
construct one the normal way)
- There is constant bidirectional communication between the body and the
arms.
- Motor nerves from the body to the arm.
(setAfter() and stop() calls)
- Sensory nerves from the arm to the body.
(callbackExpired())

I simply want the ability to have the same kind of relations in a program.
I think there is no inherent reason that "has-a" relations should not have
the same delegation capabilities that "is-a" relations have. And I see no
reason to have a completely separate delegation mechanism for "is-a" and
"has-a" relations.

I am not opposed to instead adding a new delegation mechanism. But it seems
like a much more radical change. Especially considering that the
implementation would probably be very similar to how virtual functions
work, if the mechanism is to be equally efficient.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/07032e79-d653-4ea3-84df-b405919ce51c%40isocpp.org.
a***@gmail.com
2017-11-04 18:57:16 UTC
Permalink
By the way, I would be very interested to hear specific ideas about how to
address the use cases I present, especially ideas about how a new
delegation mechanism could look like.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/6855eee9-fec7-4d5b-86b1-1c42038e08d6%40isocpp.org.
Ren Industries
2017-11-04 19:23:00 UTC
Permalink
How about just using std::function or boost::signal or one of the myriad of
ways to implement delegates, rather than trying to change an essential part
of how the object model works?
Post by a***@gmail.com
By the way, I would be very interested to hear specific ideas about how to
address the use cases I present, especially ideas about how a new
delegation mechanism could look like.
--
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an
To view this discussion on the web visit https://groups.google.com/a/
isocpp.org/d/msgid/std-proposals/6855eee9-fec7-4d5b-
86b1-1c42038e08d6%40isocpp.org
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/6855eee9-fec7-4d5b-86b1-1c42038e08d6%40isocpp.org?utm_medium=email&utm_source=footer>
.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAMD6iD8FyZu1%2Bcq9tCDZY5e5qHMsAxp_7RCvnC7o1DzLC5g4Hg%40mail.gmail.com.
a***@gmail.com
2017-11-04 19:26:42 UTC
Permalink
Post by Ren Industries
How about just using std::function or boost::signal or one of the myriad
of ways to implement delegates, rather than trying to change an essential
part of how the object model works?
In my original post, I explain the problems with std::function:
- Boilerplate needed to bind the callback (especially the
std::placeholder's).
- std::bind/std::function is not noexcept and may require dynamic memory
allocation, this is unacceptable for certain embedded systems.
- Less memory efficient, each callback necessarily contributes at least 2
words to the total size of the containing object (a function pointer and an
instance pointer). But with virtual functions, all virtual functions in one
subobject together contribute only 1 word (the vtable pointer). Yes I know
the program memory (.text section) overhead is different.

I am pretty confident there is no existing solution which addresses all of
these problems.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/b074840f-21a9-49e2-a664-e36c2164440e%40isocpp.org.
Ren Industries
2017-11-06 05:29:24 UTC
Permalink
Yeah, std::function without using the part that requires allocation.
Post by Ren Industries
How about just using std::function or boost::signal or one of the myriad
of ways to implement delegates, rather than trying to change an essential
part of how the object model works?
In my original post, I explain the problems with std::function:
- Boilerplate needed to bind the callback (especially the
std::placeholder's).
- std::bind/std::function is not noexcept and may require dynamic memory
allocation, this is unacceptable for certain embedded systems.
- Less memory efficient, each callback necessarily contributes at least 2
words to the total size of the containing object (a function pointer and an
instance pointer). But with virtual functions, all virtual functions in one
subobject together contribute only 1 word (the vtable pointer). Yes I know
the program memory (.text section) overhead is different.

I am pretty confident there is no existing solution which addresses all of
these problems.
--
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an
email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/
isocpp.org/d/msgid/std-proposals/b074840f-21a9-49e2-
a664-e36c2164440e%40isocpp.org
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/b074840f-21a9-49e2-a664-e36c2164440e%40isocpp.org?utm_medium=email&utm_source=footer>
.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAMD6iD909cOvn9B7HSp4TZuip1Lq1TFVQEEkto5gV3aLpz8z-Q%40mail.gmail.com.
Bengt Gustafsson
2017-11-06 22:22:45 UTC
Permalink
I agree with the intention of this proposal: To simplify overriding methods
in subobjects without having to resort to an extraneous pointer to the
outer object, UB offsetof tricks or using mulitple inheritance when not
actually implementing an is-a relationship. However, as has been noted by
many in the ongoing discussion, the idea of _not_ viewing this as
subclassing seems very scary to me. I'm pretty sure that an idea which
changes the implementation of any virtual method will have to have a typeid
of its own to be acceptible. Given that it has to have a vtable anyway this
is going to be relatively cheap to add the type_info instance.

The important part of the proposal is that we need a way to define that the
subobject (m_a in the example) can reach the containing object's members
and methods using the compiler's knowledge of the relative position of them
compared to the this of the subobject. This looks like a limited form of
inner class. Or maybe I should say a restricted form. So what we need are
two things: a) to actually restrict the declaration so that we can't have
more than one object of this new class per containing object (these would
by necessity have a different offset from their thises to the members of
the containing object) and b) a way to justify that the surrounding
object's members can be accessed from the nested instance.

The original proposal handles b) by viewing the implementation as being a
method of the outer class rather than the subobject. This is a bit awkward
as it does not allow access to the members of the subobject without
prepending their names with the subobject name. And actually, what is
implemented seeing is a hidden forwarding method in the subobject vtable
which adjusts the this pointer and then calls the actual implemented method
which is a regular non-virtual method in the outer object. The a) objective
is achieved by not mentioning any subclassing so there is no possibility to
create another instance of the subobject's class outside the outer object.

This means that the main requisites are fulfilled. The remaning problem is,
in my mind, clarity. There is no mentioning that the A m_a; declaration is
not just a plain old A close to its declaration, the method overrides can
be anywhere in the class head. The main issue I have with this is that the
method overrides are not written inside the class head of the class being
created. This creates a lot of unnecessary confusion. The solution close at
hand seems to be an anonymous class declaration:


class Client {
class : public Timer {
void timerExpired() override {
Client::m_client_manager.clientDisconnected(*Client::this);
}
} m_timer; // As the subclass has no name we know there will be no
more instances.
};



The weak part of this suggestion is probably the access of outer class
members (including this) using a class scope prefix. This seems like
mutliple inheritance except that you can't inherit from the outer class in
the inner class' declaration as it is incomplete by necessity. It may also
seem like this syntax would be usable in any nested class, which of course
it can't be. Unless... a way to achieve general inner class functionality
is to allow inheriting from the surrounding class using indirect
inheritance. This case would then become a special case of that feature and
the problem of how to access the outer object is just moved to the cast
operator to the outer class. Furthermore a new set of boilerplate is
introduced which reduces the appeal of the functionality. One obscure way
to create a syntax for the this pointer of the containing class is to
redefine this from being a pointer to being an array. Thanks to C++ array
to pointer conversion this would still work as usual but would now have an
operator[] to reach the outer class.

Combining these features and indicating indirect inheritance with a
combination of virtual inheritance and a cast operator to the same class we
get:

class Client {
class : Timer, virtual Client { // Reversed multiple inheritance
motivates accessing Client members directly.
operator Client&() { return this[1]; } // Mysterious
boilerplate...
void timerExpired() override {
m_client_manager.clientDisconnected(*this);
}
} m_a;
ClientManager& m_client_manager;
};


Note that this[1] does not mean to access the second base class, but to
access the containing object's this pointer. this[1] is only valid syntax
inside code of an unnamed nested class where the offset to the outer object
can be deduced by the compiler. Note also that the *this parameter to
clientDisconnected does not have to be written this[1] as the cast operator
to baseclass will kick in anyway (presuming that clientDisconnected takes a
Client& as parameter which I assume it does.

The virtual inheritance of Client is not limited to the case but is part of
a general inner class idea which piggybacks on a general indirect
inheritance idea where indirect inheritance is a generalization of the
current virtual inheritance concept allowing a cast operator to provide the
actual address of the base subobject rather than only the current mechanism.

So, by this I try to kill not only two but three birds with one stone! We
will get both indirect inheritance (i.e. the better operator.()), inner
classes (full Java functionality) and the OP feature with just a few small
changes:

- virtual inheritance can be generalized to indirect inheritance by having
a cast operator to the class inherited from in parallel.
- virtual inheritance of an outer class is allowed.
- this[1] can be used to access the containing object of a nested instance
if the class is anonymous and there is only one instance in the declaration.

Also, as a final coda note that the third of these features is enough to
handle the OP case:

class Client {
class : Timer { // Anonymous class inheriting from Timer
void timerExpired() override {
this[1].m_client_manager.clientDisconnected(this[1]); //
this[1] defined.
}
} m_a; // Only one by value instance.

ClientManager& m_client_manager;
};
Post by Ren Industries
Yeah, std::function without using the part that requires allocation.
Post by Ren Industries
How about just using std::function or boost::signal or one of the myriad
of ways to implement delegates, rather than trying to change an essential
part of how the object model works?
- Boilerplate needed to bind the callback (especially the
std::placeholder's).
- std::bind/std::function is not noexcept and may require dynamic memory
allocation, this is unacceptable for certain embedded systems.
- Less memory efficient, each callback necessarily contributes at least 2
words to the total size of the containing object (a function pointer and an
instance pointer). But with virtual functions, all virtual functions in one
subobject together contribute only 1 word (the vtable pointer). Yes I know
the program memory (.text section) overhead is different.
I am pretty confident there is no existing solution which addresses all of
these problems.
--
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/b074840f-21a9-49e2-a664-e36c2164440e%40isocpp.org
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/b074840f-21a9-49e2-a664-e36c2164440e%40isocpp.org?utm_medium=email&utm_source=footer>
.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/ef837718-0421-4780-948c-cc0c897cbcf8%40isocpp.org.
Arthur O'Dwyer
2017-11-06 23:42:14 UTC
Permalink
Post by Bengt Gustafsson
I agree with the intention of this proposal: To simplify overriding
methods in subobjects without having to resort to an extraneous pointer to
the outer object, UB offsetof tricks or using mulitple inheritance when not
actually implementing an is-a relationship. However, as has been noted by
many in the ongoing discussion, the idea of _not_ viewing this as
subclassing seems very scary to me. I'm pretty sure that an idea which
changes the implementation of any virtual method will have to have a typeid
of its own to be acceptible. Given that it has to have a vtable anyway this
is going to be relatively cheap to add the type_info instance.
Correct. The notion of the "dynamic type" of an object instance is
synonymous and interchangeable with the notion of that object instance's
"set of behaviors." If instances of Cat go "meow", and this Cat over here
goes "roar", then this Cat is not "just a Cat"; its dynamic type must be
different from Cat; perhaps it is a Lion.
The dynamic type must be reported correctly by typeid(), as a matter of
consistency and teachability.


The important part of the proposal is that we need a way to define that the
Post by Bengt Gustafsson
subobject (m_a in the example) can reach the containing object's members
and methods using the compiler's knowledge of the relative position of them
compared to the this of the subobject. This looks like a limited form of
inner class. Or maybe I should say a restricted form. So what we need are
two things: a) to actually restrict the declaration so that we can't have
more than one object of this new class per containing object (these would
by necessity have a different offset from their thises to the members of
the containing object) and b) a way to justify that the surrounding
object's members can be accessed from the nested instance.
This sounds *exactly* like private inheritance. Private inheritance gives
you all of the above, without exporting an "IS-A" relationship to the
outside world.
Post by Bengt Gustafsson
[snip snip]
Also, as a final coda note that the third of these features is enough to
class Client {
class : Timer { // Anonymous class inheriting from Timer
void timerExpired() override {
this[1].m_client_manager.clientDisconnected(this[1]); //
this[1] defined.
}
} m_a; // Only one by value instance.
ClientManager& m_client_manager;
};
In my code I would probably try to write something like this:

template<class Derived>
class SneakyTimer : public Timer {
Derived *as_crtp() { return static_cast<Derived*>(this); }
public:
void timerExpired() override {
as_crtp().m_client_manager.clientDisconnected(this); }
};
class Client : private SneakyTimer<Client> {
friend template<class> class SneakyTimer; // template friends, blecch
ClientManager& m_client_manager;
};

OP, what happens if you try this? Do you get the perfect codegen you're
expecting? Do you care about pursuing your core-language proposal any
further?

–Arthur
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/d52fd84c-984f-44d3-a956-86e4c5217cc7%40isocpp.org.
Thiago Macieira
2017-11-07 07:29:59 UTC
Permalink
Post by Arthur O'Dwyer
This sounds *exactly* like private inheritance. Private inheritance gives
you all of the above, without exporting an "IS-A" relationship to the
outside world.
But you can't multiply inherit from the same class.

struct SocketActivatable
{
virtual ~SocketActivatable();
virtual void activated() = 0;
};

struct Socket : private SocketActivatable, private SocketActivatable // ???
{

};

Of course, we can always apply the fundamental theorem of software
engineering:

struct SocketActivatable
{
virtual ~SocketActivatable();
virtual void activated() = 0;
};

struct ReadActivatable : SocketActivatable
{
virtual void readActivated() = 0;
void activated() override final { readActivated(); }
};
struct WriteActivatable : SocketActivatable
{
virtual void writeActivated() = 0;
void activated() override final { writeActivated(); }
};

struct Socket : private ReadActivatable, private WriteActivatable
{
};
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/3038266.teaS50zGzH%40tjmaciei-mobl1.
a***@gmail.com
2017-11-07 20:20:06 UTC
Permalink
@Bengt, please see my comments to your proposal below!
Post by Arthur O'Dwyer
OP, what happens if you try this? Do you get the perfect codegen you're
expecting? Do you care about pursuing your core-language proposal any
further?

I am aware of this solution and have been using it in a few places. It is
good from the performance perspective, but I am still invested in improving
the language to make this easier. The worst problem with this solution is
that such a wrapper would be needed for *every* class that has callbacks,
which is a lot of boilerplate.
Post by Arthur O'Dwyer
Post by Arthur O'Dwyer
This sounds *exactly* like private inheritance. Private inheritance gives
you all of the above, without exporting an "IS-A" relationship to the
outside world.
But it does make the class name visible for the purpose of name resolution.
For example, assume that class A privately inherits Timer and:
- Class B inherits A and privately inherits Timer (the same one), you will
get compile errors due to "ambiguous base class".
- Class B inherits A and uses the name Timer somewhere meaning to refer to
a Timer type outside the B class (a different type than Timer inherited by
A), but this will actually refer to the Timer type privately inherited by A.

I personally consider this visibility of private bases a design defect but
it is what it is.
Post by Arthur O'Dwyer
But you can't multiply inherit from the same class.
Of course, we can always apply the fundamental theorem of software
engineering:

This solution turns every virtual call into two virtual calls, reducing
performance. The static_cast + friend function approach proposed by Arthur
solves this, irrespective of whether you have a wrapper template or
hand-code the derived classes.
Post by Arthur O'Dwyer
I agree with the intention of this proposal: ...
Thank you for your comments, I appreciate that someone also thinks the
situation should be improved.

Given that it has to have a vtable anyway this is going to be relatively
Post by Arthur O'Dwyer
cheap to add the type_info instance.
Yes, giving it its own typeinfo is not a problem at all, whatever the
syntax is.
Post by Arthur O'Dwyer
The original proposal handles b) by viewing the implementation as being a
method of the outer class rather than the subobject. This is a bit awkward
as it does not allow access to the members of the subobject without
prepending their names with the subobject name.
I don't see a problem with having to type m_a if you want to access the
subobject's members from the overridden function. It's good to be explicit,
the code length difference would be minor unless you really like long
member names. And I think in practice you would be accessing other objects
more frequently than the calling one, as in my Client example. Probably the
reason for this is that the callback just gave you something new to work
with and the next step is likely to do something with this possibly with
the assistance of other objects.
Post by Arthur O'Dwyer
This means that the main requisites are fulfilled. The remaning problem
is, in my mind, clarity. There is no mentioning that the A m_a; declaration
is not just a plain old A close to its declaration, the method overrides
can be anywhere in the class head.
If you use private inheritance, the method overrides can also be anywhere,
they don't and can't be at the site where you specified the base class, so
I am not bothered that in my originally proposed design the overrides are
not at the site where you declared the member. However I agree that "there
is no mentioning that the A m_a; declaration is not just a plain old A
close to its declaration".

The main issue I have with this is that the method overrides are not
Post by Arthur O'Dwyer
written inside the class head of the class being created. This creates a
lot of unnecessary confusion. The solution close at hand seems to be an
class Client {
class : public Timer {
void timerExpired() override {
Client::m_client_manager.clientDisconnected(*Client::this);
}
} m_timer; // As the subclass has no name we know there will be no
more instances.
};
I would like at least the ability have the function body inside the
containing class, often that just makes more sense. For example consider
the Client/DatabaseAccess interaction, a function in Client initiated a
query so it looks right if a function in Client handles the result.
Post by Arthur O'Dwyer
The weak part of this suggestion is probably the access of outer class
members (including this) using a class scope prefix. This seems like
mutliple inheritance except that you can't inherit from the outer class in
the inner class' declaration as it is incomplete by necessity. It may also
seem like this syntax would be usable in any nested class, which of course
it can't be. Unless... a way to achieve general inner class functionality
is to allow inheriting from the surrounding class using indirect
inheritance. This case would then become a special case of that feature and
the problem of how to access the outer object is just moved to the cast
operator to the outer class. Furthermore a new set of boilerplate is
introduced which reduces the appeal of the functionality. One obscure way
to create a syntax for the this pointer of the containing class is to
redefine this from being a pointer to being an array. Thanks to C++ array
to pointer conversion this would still work as usual but would now have an
operator[] to reach the outer class.
Combining these features and indicating indirect inheritance with a
combination of virtual inheritance and a cast operator to the same class we
class Client {
class : Timer, virtual Client { // Reversed multiple inheritance
motivates accessing Client members directly.
operator Client&() { return this[1]; } // Mysterious
boilerplate...
void timerExpired() override {
m_client_manager.clientDisconnected(*this);
}
} m_a;
ClientManager& m_client_manager;
};
I see the following issues with this idea:
1) "virtual" no longer means virtual base, the meaning of virtual bases
changes based on "Mysterious boilerplate" present in the class.
2) this[1] syntax gives no indication of the meaning.
3) One cannot easily implement the overridden functions as members of the
containing class, if you want that you will have to write boilerplate to
forward each call.

I have a proposal which is an elaboration on this idea:
- Use a specific keyword, for example "member", in the declaration of the
class, e.g. "member class : Timer { ... } m_timer;". This differentiates
normal class declarations from this new feature where the class is a single
member and has access to its container.
- Functions in the nested class have access to the containing class as if
the containing class was some kind of base (but it's not). This includes
ability to static_cast<ContainingClass*>(this).
- Allow mapping callbacks to functions of the containing class by using
"=", e.g. "void timerExpired() override = Client::timerExpired;". Perhaps
also allow "auto" type to avoid having to write the arguments and return
type twice.

Here is how it would look like, with the different kinds of callback
declarations (all permitted):
class Client {
member class : Timer {
// Implementation in nested class.
void timerExpired() override {
// implicit access to Client members is possible
m_client_manager.clientDisconnected(*this);
}

// Implementation in containing class, full signature.
void timerExpired() override = Client::timerExpired;

// Implementation in containing class, signature deduced.
auto timerExpired override = Client::timerExpired;
} m_timer;

void timerExpired() {
m_client_manager.clientDisconnected(*this);
}
};
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/69b63819-3410-4662-8728-194f784aca17%40isocpp.org.
a***@gmail.com
2017-11-07 20:33:00 UTC
Permalink
Note that this[1] is currently valid syntax whose meaning should not
change. It is even possible to use this in a correct way, when you have an
array of A classes and some function in A uses this[1] to refer to the next
object. I can also imagine at least someone was getting annoyed by writing
(*this) and used this[0] instead.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/33d77b26-58d3-4cef-bd19-4c200d775711%40isocpp.org.
Bengt Gustafsson
2017-11-08 22:36:31 UTC
Permalink
Post by a***@gmail.com
Note that this[1] is currently valid syntax whose meaning should not
change. It is even possible to use this in a correct way, when you have an
array of A classes and some function in A uses this[1] to refer to the next
object. I can also imagine at least someone was getting annoyed by writing
(*this) and used this[0] instead.
Yes, someone has probably been "smart" enough to use this[1] to access
neigbouring array elements, unfortunately. this[0] however, would continue
to work as now, just as using this would. I just wanted to find a syntax we
could use to get to the outer object and which would only be available when
it made sense. A magic variable std::outer could work.

Some comments on your proposals etc:

- redefining virtual inheritance to allow combination with a cast operator
is one possible way to implement indirect inheritance. Other syntaxes are
possible, the important thing here is that given some workable syntax
instead of this[1] the indirect inheritance feature can be used to
implement this type of inner classes (and general inner classes as well).

- your use of the words 'member' and 'inner' does not seem to work in
conjunction with an anonymous class as they would be taken as the class
name! The reason final works as a context sensitive keyword is that the
parser has already seen a class name. If you write class final { ... }; you
have still declared a class called final!
(see: http://en.cppreference.com/w/cpp/language/class)

- I don't really understand why it is so important for you to be able to
define the methods of the inner class in the outer class scope. To me this
seems to be very obscure and with very little gain. I would be more
interested in tucking them away in a cpp file which is easy if you give the
inner class a name.

All in all the important part is to be able to void the unnecessary "outer"
pointer without too much syntactical noise. So in ernest the new feature
needed is the std::outer magic variable which works like this but only for
a nested class with one by value instance per outer object. Trying to
create ad hoc instances of an inner class that uses std::outer is illegal.
Unfortunately it seems hard to mandate that violating this rule must create
a compile time error as the only use of std::outer could be in an invisible
cpp file when creating the second, rouge, instance of the inner class.
Limiting the availability of the std::outer variable to the class head of
the inner class is one idea, but it prevents separate implementation
without a boilerplate forwarding method. This suggests that the usage of
the std::outer variable should be enabled by a modifier in the class
introducer that simultaneously prevents more instances than one immediately
declared by value member.

Note by the way that making the class anonymous is no guarantee against
more copies being declared nowadays:

decltype(m_a) m_b;

I would prefer if this boiled down to two features, a ways to restrict a
nested class to one by-value instance which enables using a name for its
outer class, and indirect inheritance which allows using the outer class
contents without qualification given a little boilerplate. The problem
seems to be to find a little bit of unused and logical syntax to implement
these features.

Another possibility, which I would not prefer, is to allow regular
inheritance of the outer class in the inner class head, and then just
define this (currently disallowed) construct to mean what we want:

class Client {
class : Timer, Client { // Inheriting Client motivates accessing
Client members directly.
void timerExpired() override {
m_client_manager.clientDisconnected(*this);
}
} m_a;
ClientManager& m_client_manager;
};

Not so bad actually. Fulfills the goals: Easy for the compiler to detect if
you try to make additional instances, logical that you can access the
Client's members, a wellknown way to disambiguate between Timer and Client
names, extends to full inner classes if the indirect inheritance feature is
added. No new magical variables or keywords. The problem is again that it
violates the is-a rule quite drastically... but from a practical standpoint
it should work nicely.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/96b911a3-dc74-4ed2-bc58-52e594ee1b6e%40isocpp.org.
a***@gmail.com
2017-11-09 00:38:19 UTC
Permalink
Post by Bengt Gustafsson
Post by a***@gmail.com
Note that this[1] is currently valid syntax whose meaning should not
change. It is even possible to use this in a correct way, when you have an
array of A classes and some function in A uses this[1] to refer to the next
object. I can also imagine at least someone was getting annoyed by writing
(*this) and used this[0] instead.
Yes, someone has probably been "smart" enough to use this[1] to access
neigbouring array elements, unfortunately. this[0] however, would continue
to work as now, just as using this would. I just wanted to find a syntax we
could use to get to the outer object and which would only be available when
it made sense. A magic variable std::outer could work.
I think the name lookup should be close to what Java does: you can access
outer class's variables by name, but this is not implicitly convertible to
the outer class pointer type. For this purpose we could either adopt the
same syntax OuterClass::this, or some std::outer magic like you suggest.
Post by Bengt Gustafsson
- your use of the words 'member' and 'inner' does not seem to work in
conjunction with an anonymous class as they would be taken as the class
name! The reason final works as a context sensitive keyword is that the
parser has already seen a class name. If you write class final { ... }; you
http://en.cppreference.com/w/cpp/language/class)
Yes I see that now. I've been looking through the list of reserved C++
keywords, and "inline" seems nice (inline as in, the class is an
inseparable part of its container). Like this:

inline class : Timer {
...
} m_timer;

For general outer classes, "virtual inline class", is an option (virtual
inline since it only pretends to be part of the outer class).

- I don't really understand why it is so important for you to be able to
Post by Bengt Gustafsson
define the methods of the inner class in the outer class scope. To me this
seems to be very obscure and with very little gain. I would be more
interested in tucking them away in a cpp file which is easy if you give the
inner class a name.
As I explained it is just often more natural to have a callback implemented
in the same class. Also, if you want to implement the callbacks within the
class declaration (as opposed to at namespace level), writing the code in
the declaration of the nested class can be quite ugly (for example if you
keep all your non-static member declarations together, you now have a bunch
of code in the middle of that).
Post by Bengt Gustafsson
... This suggests that the usage of the std::outer variable should be
enabled by a modifier in the class introducer that simultaneously prevents
more instances than one immediately declared by value member.
Note by the way that making the class anonymous is no guarantee against
decltype(m_a) m_b;
I would prefer if this boiled down to two features, a ways to restrict a
nested class to one by-value instance which enables using a name for its
outer class, and indirect inheritance which allows using the outer class
contents without qualification given a little boilerplate. The problem
seems to be to find a little bit of unused and logical syntax to implement
these features.
I think it should preferably be a single feature that just works the
"right" way, without being unnecessarily generic especially if that would
require boilerplate for common use cases. Think about the "inline class"
which I proposed, can this not solve everything?

- An "inline class" can only be declared as an anonymous class type in a
class member declaration (e.g. inline class X {}; is invalid).
- The actual type of an "inline class" can be retrieved by decltype(), but
it is not allowed to create any object of this type other than that member.
The compiler should easily be able to enforce this (like it can enforce not
creating abstract classes except as a base class).
- There is special syntax (Outer::this) or magic function (std::outer) to
get the outer class pointer, and this is only allowed for "inline classes"
and not regular classes (note: does not mean only functions of the inner
class can do it, something like inner->Outer::this could also be allowed).
There is no need for magic detection of whether std::outer is used
anywhere, since "inline class" specifies support for this operation.

Another possibility, which I would not prefer, is to allow regular
Post by Bengt Gustafsson
inheritance of the outer class in the inner class head, and then just
class Client {
class : Timer, Client { // Inheriting Client motivates accessing
Client members directly.
void timerExpired() override {
m_client_manager.clientDisconnected(*this);
}
} m_a;
ClientManager& m_client_manager;
};
I don't like abusing the base class list for that, the syntax is misleading.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/91d2d626-a312-40f5-a633-7794110c12c6%40isocpp.org.
Thiago Macieira
2017-11-07 20:40:17 UTC
Permalink
Post by a***@gmail.com
Post by Thiago Macieira
But you can't multiply inherit from the same class.
Of course, we can always apply the fundamental theorem of software
This solution turns every virtual call into two virtual calls, reducing
performance. The static_cast + friend function approach proposed by Arthur
solves this, irrespective of whether you have a wrapper template or
hand-code the derived classes.
Not necessarily. First of all, the performance of the virtual call itself only
shows up in microbenchmarks. Second, that's an artifact of how I wrote it,
quickly. It didn't need to be done that way, like was shown.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/20535333.nuFktXWftp%40tjmaciei-mobl1.
a***@gmail.com
2017-11-07 21:13:07 UTC
Permalink
Since Bengt mentioned general inner classes, I have an idea how to do these
as well, which I think is more clean and is consistent with the the "member
class" syntax:

class Outer {
inner class Inner {
private:
Outer *m_parent;

Outer & operator outer () const {
return *m_parent;
}

void foo () {
m_outer_var;
}

public:
Inner (Outer &parent) : m_parent(&outer) {}
};

int m_outer_var;
};

The keyword "inner" in "inner class" indicates this is an inner class. An
inner class must define an "operator outer" which is used implicitly to get
a reference to the outer class. The inner class can access the outer class
using the syntax as in my proposed "member class" (as of it was a base
class), only that here the semantic is that "operator outer" is called
whenever such an access is made.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/02f9475f-c127-4a7a-8a96-5c3fbdba1b8f%40isocpp.org.
a***@gmail.com
2017-11-07 21:50:46 UTC
Permalink
Small correction, "member" and "final" should probably appear after "class
[name]", just like "final" works:

// Member class
class member : Timer { ... } m_timer;

// Inner class
class Inner inner {... };

// Both can also "final", though "final" has no meaning for member
// classes which are implicitly final.
class member final : Timer { ... } m_timer; // redundant final
class Inner inner final { ... }; // cannot make more derived class

I suppose that an inner class could be derived from if not declared final,
and the derived class itself could be inner or not irrespective of whether
the base class is and of the corresponding outer class. Name lookup rules
would need to precisely specify which expressions refer to an outer class
and which one. When an inner class is publicly inherited, the derived class
should be able to access the corresponding outer class without itself being
declared inner.

Anyway I've strayed a bit off from what I need, I am only directly
interested in "member classes". But it's good to consider inner classes to
show that that similar syntax and semantics would work for a related
feature.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/c2a3d05a-abec-4a8c-bc6f-8ba20dc1c2d6%40isocpp.org.
a***@gmail.com
2017-11-07 22:01:12 UTC
Permalink
When an inner class is publicly inherited, the derived class should be
able to access the corresponding outer class without itself being declared
inner.
Oh wait, who should be able to access the outer class should probably be
just the matter of the access level of "operator outer".
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/122353ac-0895-425e-8536-c3c4e4bb8504%40isocpp.org.
Thiago Macieira
2017-11-04 19:25:53 UTC
Permalink
Post by a***@gmail.com
Post by a***@gmail.com
struct A {
virtual void virtualFunction() = 0;
};
struct B {
struct A1 : A {
void virtualFunction() override;
} m_a1;
struct A2 : A {
void virtualFunction() override;
} m_a2;
};
The issue with this approach is that the implementations of
virtualFunctions() will need to get B* from a pointer to A1*/A2*. That
cannot be done in general (only when B is a standard-layout class by using
offsetof). So to be portable code, A1 and A2 would each need to have a "B
*parent" member. That adds size overhead and requires boilerplate.
Correct.

And remember that offsetof is restricted for a reason. If you're not allowed
to use it, what makes you think the compiler could resolve the pointer from A
back to B without storing a pointer somewhere? Since B is not standard layout,
a subclass C of B could change the offsets from m_a1 and m_a2 to the top of B
(it doesn't happen with the major ABIs, but it is theoretically possible).

Now, I'll grant you that there are advantages to leaving this to the compiler:
1) if the distance is fixed by ABI, then the adjustment can be in code
2) even if it isn't, the adjustment may be stored in the vtable itself, as
opposed to storing it in each object.

I'm just wondering if this is worth the complexity. I see no other advantage
to your feature suggestion, except that of saving sizeof(void*) per object.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/1540337.mpxU4uUWLP%40tjmaciei-mobl1.
Bengt Gustafsson
2017-11-09 23:02:25 UTC
Permalink
I think you just about nailed it:

- inline class to indicate that there may be only one instance and it must
be by value in the surrounding class. (It would actually be useful also as
a local class in a function where it would naturally have access to the
surrounding local variables without need for an extra pointer. Like a
lambda but with the possibility to override named functions, not just
operator()).

- using Outer::this to access the outer this pointer in the (rare) occasion
that you really need it. I presume some automatic mechanism will kick in to
make the outer members available without special qualification.

General inner classes I think are better served by an indirect inheritance
feature as it seems hard to come up with a clear syntax for providing the
link from a constructor parameter to the "outer this". Possibly this could
work but it is not very logical, right:

Inner(Outer& outer) { Outer::this = outer; }
Post by a***@gmail.com
Hello,
Currently the only way to override a virtual function of a class A is by
inheriting A in class B (A is a base-class subobject of B). I propose that
the ability to override virtual functions is extended to when A is a member
object of B.
The syntax does not really matter at this point, but it could be something
struct A {
virtual void virtualFunction() = 0;
};
struct B {
A m_a;
void virtualFunctionImpl() override(m_a.virtualFunction) {
// Implementation
}
};
Semantically this would be much like if A was a base class of B and B
overrode the virtual function, except that A is a member and with that all
the usual semantics that come with this. Note how in this example the class
A is used as a field despite being abstract, since all its pure virtual
functions are implemented when embedded within B.
I propose only overriding virtual functions of a direct member class, not
more complicated cases like overriding the virtual function of a member
class inside a base class.
I think this would be useful in the context of single-threaded
callback-based event-driven design, where the virtual functions can
represent callbacks to the "owner" of a class. For example, consider a
class Timer : private boost::noncopyable {
Timer(EventLoop &);
~Timer();
void start(uint64_t timeout);
void stop();
bool isRunning() const;
virtual void timerExpired() = 0;
// implementation details...
};
Note: the Timer class provides useful guarantees such as 1) timerExpired()
is called only when the timer is actually running (stop() reliably prevents
a future callback), 2) Timer can be safely destructed from its own
timerExpired() callback.
I think this is a very nice interface, but use of virtual functions for
the callback implies that Timer must be inherited which has significant
- Problems when one class needs to use more than one Timer (wrapper
classes needed).
- Possible base class ambiguity, such as when class A inherits a Timer but
also inherits class B which itself inherits a Timer, even when private
inheritance is used. B using a Timer is an implementation detail and
shouldn't cause a compile error because A also uses a Timer.
The other way to implement the callback is with facilities like
- Boilerplate needed to bind the callback (especially the
std::placeholder's).
- std::bind/std::function is not noexcept and may require dynamic memory
allocation, this is unacceptable for certain embedded systems.
- Less memory efficient, each callback necessarily contributes at least 2
words to the total size of the containing object (a function pointer and an
instance pointer). But with virtual functions, all virtual functions in one
subobject together contribute only 1 word (the vtable pointer). Yes I know
the program memory (.text section) overhead is different.
I believe this feature can be implemented by generating a "special" vtable
for the member class for the case when the containing class overrides any
of its virtual function (it could be called "vtable for A in B"). Such a
vtable would have the same layout as the vtable for A except that the
function pointers for those overridden functions would point to member
functions of B. Note, just like if B was a base class of A at nonzero
offset, this may need generation of thunks, but this is really
ABI-dependant.
I do not propose any changes to the type system. The dynamic type of m_a
is A (not some kind of automatically generated derived class!), and
typeid(m_a) would still indicate that. The only new thing is that there is
something else that can override virtual functions.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/72f69a4b-9a32-43ca-9f4c-92256b7ddfe5%40isocpp.org.
a***@gmail.com
2017-11-13 18:21:04 UTC
Permalink
Hey,
I’d like to add that what you want here gives off a faint hint of
smelling something a bit like a limited, static version of ‘method
swizzling,’
<https://www.google.com/search?num=30&client=safari&rls=en&q=Objective-C+method+swizzling&spell=1&sa=X&ved=0ahUKEwj82eyqjvPTAhUKiFQKHfLDBu8QvwUIJCgA&biw=1920&bih=1041>
a sleight of hand available to programmers using the Objective-C language
<https://en.wikipedia.org/wiki/Objective-C>.

I am not at all familiar with Objective-C, but what you show seems to be
like an abuse of obscure runtime features. What we're proposing here is a
language extension to allow concise and efficient callbacks in the context
of event-driven code. In this context everyone is *already* doing calls
from subobjects to the parent object (be that with private inheritance or
other mechanism, it's not a hack. The proposal might look strange and
misguided to someone who has not done much event-driven programming.

I just had one last quick thought on another way one could possibly
conceptualize what’s being discussed here. In order to obtain the effects
you desire, perhaps one might be able to do something along these lines by
first constructing a wrapper class around the class with which he or she
wants to work with in this way that reimplements all parts of its wrapped
class’s interface, forwarding things along as needed and/or desired, except
for the one function they want to replace, which would then merit
re-implementation in the wrapper. This goes in the direction of ‘smart
references,’ though, and
well, `operator.()` or similar functionality
meeting its design goals is
still controversial and a work in progress
within the committee, last I recall.
I'm not sure exactly what you are proposing but different solutions with
existing language and their drawbacks have already been discussed in this
thread (including derived "wrapper" classes, either privately inherited by
the containing class or used as members). I don't understand what you mean
with "smart references" or operator.().

It is important to consider that in the kind of event-driven architecture
that I am talking about, this mechanism would be used almost everywhere,
wherever some module needs to asynchronously report events to its owner.
Any solution that requires boilerplate is unacceptable to me, since such
boilerplate would end up everywhere.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/dd3bf895-6df7-40fd-ab07-f5e81ad26333%40isocpp.org.
Loading...