a***@gmail.com
2017-11-02 22:35:27 UTC
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.
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.
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.