Topic: current_class: Submitted Public Comment
Author: p150651@proffa.cc.tut.fi (Pulkkinen Esa)
Date: 1995/07/17 Raw View
In article <3u3aa4$slg@steel.interlog.com>,
Herb Sutter <herbs@interlog.com> wrote:
>Let me put it another way: Is there any benefit to allowing the user to
>override it (that can't be done with existing template functionality)? When
>would we want a template parm which defaults to current_class be something
>other than current_class, and still not violate base class assumptions?
>
I tried to make an example, but couldn't come up with anything
that couldn't be made with existing template functionality. I was
thinking something like using multiple template parameters defaulting
to current_class in one function's definitions, and then allowing
(for example) overriding them one at a time in derived classes to
provide means to gradually fix the type parameters. But as said,
every example I could provide could already be made with
ordinary templates or using virtual functions.
>Very interesting! I would offer a slight refinement (though I suspect that
>you meant something along these lines anyway): Define Register_server itself
>as a template, and then:
>
> Register_server<X> X<T>::id(&X::Create);
I assume you meant:
Register_server<X> X<T>::id(&T::Create);
>Good point; I made a similar observation. But let me know what you think of
>the other argument I make in that other article, namely that while we
>could require a declaration (e.g., "template<current_class>")
>to make the compiler's job easier (knowing which classes/functions
>use it based on their declarations
>alone), I'm leaning more strongly against having it be a default to a usual
>template argument since in several important ways it does not behave
>like one (e.g., it isn't meaningful in function templates).
I think some kind of notification for the compiler is a minimum
requirement. I'm now more critical about the default argument, since
I found an example leading to a paradox (infinite virtual function table)
when allowing the user to override the template argument:
class X
{
template <class T=current_class>
void do_f() { f(dynamic_cast<T*>(this)); }
};
class Y : public X
{
template <class T=current_class>
void do_f() { g(dynamic_cast<T*>(this)); }
};
To implement this and overriding the default argument
assumes potentially infinite virtual function table.
Consider calling the do_f function when given a pointer:
X* obj=new Y;
obj->do_f(); // calls g(Y*) (dynamic)
obj->X::do_f(); // calls f(Y*) (dynamic)
obj->template do_f<X>(); // calls g(X*) (still dynamic!, requires
// a virtual function table
// entry for each possible
// template argument)
obj->template X::do_f<X>(); // calls f(X*) (static call)
--
Esa Pulkkinen | C++ programmers do it virtually
E-Mail: esap@cs.tut.fi | always with classes, resulting
WWW : http://www.cs.tut.fi/~esap/ | in multiple inheritance.
Author: herbs@interlog.com (Herb Sutter)
Date: 1995/07/12 Raw View
In article <3tu472$7he@usenet.rpi.edu>,
vandevod@avs.cs.rpi.edu (David Vandevoorde) wrote:
>In article <3ttt91$oqu@steel.interlog.com>,
>Herb Sutter <herbs@interlog.com> wrote:
>>>[ in the following, multiple versions of the regular function f(X) are
>>> assumed to exist ]
>>>
>>>> class A {
>>>> public:
>>>> void do_f() { f(dynamic_cast<current_class*>(this)); }
>>>> };
>[...]
>>Mmm, now I see where you're coming from. Good point. In another article in
>>this thread, Esa wrote:
>>
>>>class X
>>>{
>>> template <class T=current_class> void do_f(void)
>>> {
>>> f(dynamic_cast<T*>(this));
>>> }
>>>};
>>A small concern: this form would require a minor change to existing rules to
>>prevent users from filling in that (now-visible) template parm.
>
>Well, I had the same reflex at first and thus thought the "better
>syntax" should be:
> template<this_class> ...
(Note: I'm going to argue strongly for this in the rest of this article,
though I'll call the declaration "template<current_class>".)
>but when I thought it over, I saw no reason to prevent users from
>specifying the 'T' in
> template<class T = this_class> ...
Hmmm! I'm afraid I definitely do want to prevent users from specifying or
even knowing about the template; besides the fact that a user's specifying it
manually would break the whole scheme (and hence renders the approach
fragile), it would cause all sorts of derivation problems (as you point out
later on, see below for more discussion).
>BTW, we could avoid the introduction of a new keyword with:
> template<class T = this> ...
Urk. I see your point, but isn't C++ already replete with overloaded keywords
(e.g., static)? :-( I can see the confusion already: "But 'this' is a pointer
to a type, yet here it's the actual type...?"
At any rate, I've been thinking over the whole scheme (of introducing it as an
explicit template), and there seems to me to be one very major problem from
which all of the afore- and later-mentioned problems flow:
The current_class type cannot be used exactly like a regular template
type. It has more restrictions.
Specifically:
a) a class writer may not declare a member variable of type current_class
(see the public comment's Questions & Answers section);
b) (as above) the template may never be supplied explicitly, but rather
the compiler is responsible for supplying it in this and all subsequent
(instantiated and derived) classes;
c) current_class makes sense only within classes, not within standalone
functions, and so could not be used in standalone function templates.
Of these, (a) is quite unintuitive if current_class is expressed as a normal
templated type. Yet we would need additional special rules to prevent the
class writer from instantiating a member variable of type current_class
because it will change size in every derived class and "would wreak havoc with
existing rules" (as I put it in the public comment). (Yes, there are ways
around it, at the expense of compiler complexity: e.g., we could require the
compiler to transparently change member variables of type current_class to be
pointers only, automatically new'd in the ctor and deleted in the dtor, which
would completely solve the size problem but which puts yet another burden on
compiler writers -- indeed, why would an object reasonably want to contain
another object of the same class? If it did, the recursion would seem to be
infinite, making the class uncompilable/unrepresentable.)
At any rate, it seems to me that since current_class would not behave like a
normal explicit template, it probably should not be one.
That said, one of the original reasons for trying this alternative was to give
the compiler more information: specifically, to let the compiler know when
current_class implicit-template information was being used. In this case, I
like your original suggest ('way above there):
template<current_class> /* class or member function declaration follows */
That way: a) the compiler knows the class/function uses current_class info; b)
the client code cannot accidentally/ill-advisedly supply it (and no extra
rules to support this would be required, other than skipping the optional
current_class entry in the template parm list if it appears); c) in the actual
code, where current_class is used it always actually appears as that, as
"current_class" (not as some other parameterised type where you have to go
back to the header and remember which of the template args was current_class),
making things more readable.
Does this compromise sound reasonable? The current_class template is still
"mostly" implicit, other than that now it is declared. I suppose it should be
an error for a user to declare "template<current_class>" and then not use it,
since just declaring current_class invokes template (and vtable? haven't quite
thought that one through yet) overhead.
> template<typename T = this>
> struct singleton {
/* elided */
> };
>
> struct B1: singleton {
> };
>
> template<typename T = this>
> struct B2: singleton<T> {
> };
>
> struct D1: B1 {
> };
>
> struct D2: B2 {
> };
>
> f() {
> D2* d2 = D2::instance(); // OK
> D1* d1 = D1::instance(); // Not OK, requires a cast.
> }
>
>Note that D2 is derived from B2<D2> which in turn is derived from
>singleton<D2>. D1 on the other hand, is derived from the non-template
>class B1 which has a base class singleton<B1>.
Right, this is the problem... it's only a slight improvement over what we can
already do today, but it still requires programmer interference (er, I mean,
"discipline" <g>) to propagate the template and get it right. That's why I
really don't like it.
To prove that we can already do this much today, consider (using the existing
language only, modifying your example slightly):
template<typename T>
struct singleton {};
template<typename T = B2> // <--- the interesting part
struct B2: singleton<T> {}; // each derived class must do this
template<typename T = D2> // <--- again
struct D2: B2<T> {};
f() {
D2* d2 = D2::instance(); // OK
}
All I've done is replace the default for the template arg with the class
itself, for each derived class. Yet this is equivalent to your approach above
for implementing current_class, and so that approach actually doesn't seem to
buy us anything in this example: we can achieve similar complexity today,
without that particular implementation of a current_class keyword. The
current_class keyword was supposed to save us from requiring subclasses to do
these kinds of gymnastics, and it can, but it seems not with this kind of
implementation. This mechanism uses template default args to propagate
derived-class information up the inheritance tree, class by class.
However, you've now demonstrated something important, namely one detail I
claimed in the comment seems to be wrong: Using deep derivation and
propagating current_class-like information manually does not after all seem to
preclude polymorphism between the derived types (you just have to rely on
default args and never forget to supply them properly for each class, as
above). Singleton, B2, and D2 can still be used polymorphically in this
scheme.
(Aside: However, perhaps this scheme demonstrates a simple and viable compiler
implementation of current_class; could not the compiler be given the job of
doing exactly this and getting the types right? Just a thought on a possible
implementation.)
However, even with that problem gone, it relies completely on programmer
interfe-- er, that is, discipline -- and my broken record still plays on:
"Programmer discipline is a poor substitute for language support." (HS, 1994)
This still doesn't give us true mixinability, and there's no real reason why
we shouldn't be able to mix in this way in C++, simply "struct B2: singleton
{}; struct D2: B2 {};" etc. without having to be impacted by implementations
upstream.
Heck, consider: What if a base class currently doesn't use current_class info
and we have derived classes like "struct Derived: Base"? All's well, but now
what if the base is changed because it needs current_class for a certain
function -- ought we to be forced to go through every class that derived from
Base (directly or indirectly) and supply this template and retrofit the
implementations? That's what current_class is supposed to give us:
transparent reusability, without base changes careening madly through the
object model. If it were up to the compiler, fine, it's a recompile; but if
we do it manually it's up to us to catch and fix all of these repercussions,
and heaven help us if we miss one because the omission might not even flag a
warning.
>You're welcome. This thread is a lot more stimulating than many I've
>seen in the comp.*.c++ groups.
Thanks, and regards,
Herb
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Herb Sutter 2228 Urwin, Ste 102 voice (416) 618-0184
Connected Object Solutions Oakville ON Canada L6L 2T2 fax (905) 847-6019
Author: herbs@interlog.com (Herb Sutter)
Date: 1995/07/13 Raw View
In article <3tvefo$41j@peippo.cs.tut.fi>,
esap@cs.tut.fi (Pulkkinen Esa) wrote:
>In article <3ttsia$oqu@steel.interlog.com>,
>Herb Sutter <herbs@interlog.com> wrote:
>
>>2) The template parm still should not be entered by any client code... if
>>there are other templated parms, perhaps the one defaulting to current_class
>>should always be last and never be instantiable directly by the user. IOW,
>>in your example the user would always write something like "X::do_f()",
never
>>"X::do_f<??>()". If there were additional parameters, like:
>>
>> template<class A, class T=current_class> void do_f(void) { /*...*/ };
>>
>>then the user would always write something like "X::do_f<MyClass>()", and
>>again never be allowed to supply the parameter defaulting to current_class.
>
>I can't see any reason why the template parameter couldn't be provided
>by the user (this was pointed out already in other thread),
>current_class (clearly?) just can't be used as an explicit template
>parameter, only as a default.
My response to that is probably on its way to you now. Basically, I'm highly
sceptical because allowing the user to supply the parm may break assumptions
(esp. that the parm is in fact the current_class -- the user could conceivably
throw something else in there, perhaps not even inherited from us... like
int!). The purpose of current_class was to let base classes know the exact
type of the derived class in which they are currently being used; there would
have to be a good reason to open this up to subversion.
Let me put it another way: Is there any benefit to allowing the user to
override it (that can't be done with existing template functionality)? When
would we want a template parm which defaults to current_class be something
other than current_class, and still not violate base class assumptions?
>>3) That said, sometimes classes themselves (not just member functions)
>>need to
>>know current_class, and therefore you might have "template<class
>>T=current_class> class X { /* ...uses current_class info, say to store a
>>pointer...*/ };".
>
>This is a good observation. First application that comes to my mind is
>registration of classes through inheritance:
> template <class T=current_class>
> class X
> {
> static Register_server id;
> public:
> static current_class *Create() { return new T; }
> // ...
> };
> template <class T=current_class>
> Register_server X<T>::id( &X::create );
>
> class Y : public X { };
> // nothing else needed, there is an individual id
> // object in Y also
>
>Now, this is a powerful technique, with proper definition of Register_server,
>allows the base class (or code using the base class) to get information
>about (all) derived classes, create objects of derived classes they
>have never heard of, and impose various constraints to them
>(like if you declared a static (data or function) member
>but left it undefined except for the base class, all derived classes
>,whether directly or indirectly inherited, would have to implement it.).
Very interesting! I would offer a slight refinement (though I suspect that
you meant something along these lines anyway): Define Register_server itself
as a template, and then:
Register_server<X> X<T>::id(&X::Create);
That way Register_server's ctor can throw all id's derived from X into the
same ServerRegistry<X> which can then be queried by X (or anyone else knowing
about X, for that matter). This way Register_server and ServerRegistry are
generic, and any future classes that want to use them, as X does today, get
their own entries/instantiations.
Thanks very much for this example: this is another powerful technique that
can't be done without current_class information, and I'll add it to future
versions of the public comment if they're ever needed.
>btw, to respond to the another thread that suggested using 'this'
>instead of 'current_class' to prevent inventing a new keyword:
>I find 'this' keyword somewhat confusing, since it has traditionally
>been used as a pointer, and it already has a meaning as template
>default argument. I would think 'virtual' would be better choice,
>if inventing a new keyword is such a bad thing.
Good point; I made a similar observation. But let me know what you think of
the other argument I make in that other article, namely that while we could
require a declaration (e.g., "template<current_class>") to make the compiler's
job easier (knowing which classes/functions use it based on their declarations
alone), I'm leaning more strongly against having it be a default to a usual
template argument since in several important ways it does not behave like one
(e.g., it isn't meaningful in function templates).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Herb Sutter 2228 Urwin, Ste 102 voice (416) 618-0184
Connected Object Solutions Oakville ON Canada L6L 2T2 fax (905) 847-6019
Author: jbuck@synopsys.com (Joe Buck)
Date: 1995/07/13 Raw View
I wrote:
>>Consider the clone operation: we want all objects derived from a
>>particular base class to be able to duplicate themselves.
>>
>>class Clonable
>>{
>>public:
>> template <class T=current_class> virtual T * clone() const
>> {
>> return new T(*this);
>> }
>>};
>>
>>Now each class derived from Clonable has a proper clone method.
>>
>>Here we see why this is very different from a template: you can safely
>>make a virtual function this way because there's exactly one function
>>in each class, where existing member templates correspond to an indefinite
>>number of functions.
esap@cs.tut.fi (Pulkkinen Esa) writes:
>No. There _could_ be many of the functions in the same class, consider
>a call to the clone method:
>
>Clonable *obj;
>OtherClonable *cloned=obj->template clone<OtherClonable>();
I don't understand what this notation means. What is OtherClonable?
What is "obj->template clone<OtherClonable>()" ?
The way I was thinking about it, each class derived from Clonable would
have exactly one clone() method and there would be no way to make another:
the current_class style of template would be expanded exactly once for
each class. If you think it should be some other way, I wish you would
explain it more clearly.
Currently member templates cannot be virtual because this would result
in a potentially infinite virtual function table. If you want some method
of generating virtual functions for templates, I suspect that clone()
would be one of the most common uses, but you have to assure that it
turns into exactly one function (so the vtable can be built).
--
-- Joe Buck <jbuck@synopsys.com> (not speaking for Synopsys, Inc)
Anagrams for "information superhighway": Enormous hairy pig with fan
A rough whimper of insanity
Author: schuenem@informatik.tu-muenchen.de (Ulf Schuenemann)
Date: 1995/07/14 Raw View
There are two discernable ideas we are talking about:
1. A memberfunction that is reinstanciated in each derived class (let's
call it auto-inst-memfct) like a template is instanciated (once) when
statically used. Such a memfct can be virtual or static or neither.
2. The usage of current_class in the declaration and implementation
of memfcts. This can, with the technique of C++compilers, only be
done, when the implementation of such a memfct is reinstanciated
in each derived class. Ie. current_class => auto-inst-memfcts.
IMHO it would not be in the spirit of C++ to say that memfct with
current_class must automatically be virtual.
How about a template member declaration without <> meaning
auto-inst-memfcts (BTW: auto-inst would be more powerful by
having also other auto-inst-members like data...):
class B {
template size_t numinst;
public:
typedef B current_class; // workaround for 2.
template static size_t get_numinst ()
{ return numinst; }
template static size_t allinst_sizeof ()
{ return numinst * sizeof current_class; }
template virtual size_t dyn_sizeof () const
{ return sizeof this; }
template virtual current_class Clone () const
{ return new this; }
B ()
{ numinst++; }
};
template B::size_t numinst = 0;
class D {
public:
typedef D current_class; // workaround for 2.
D ()
{ numinst++; } // referes to D::numinst
};
But unfortunately 1. drives us into the trap of the anti-inherited-keyword
gang: They will propose us to use a typedef to current_class in each class.
But current_class is needed for nearly every reasonable usage of auto-inst.
So auto-inst and current_class must be instroduced together (or never).
Ulf Schuenemann
--------------------------------------------------------------------
Ulf Sch nemann
Fakult t f r Informatik, Technische Universit t M nchen, Germany.
email: schuenem@informatik.tu-muenchen.de
Author: esap@cs.tut.fi (Pulkkinen Esa)
Date: 1995/07/14 Raw View
In article <3u3j9a$1p4@hermes.synopsys.com>,
Joe Buck <jbuck@synopsys.com> wrote:
>I wrote:
>>No. There _could_ be many of the functions in the same class, consider
>>a call to the clone method:
>>
>>Clonable *obj;
>>OtherClonable *cloned=obj->template clone<OtherClonable>();
>
>I don't understand what this notation means. What is OtherClonable?
>What is "obj->template clone<OtherClonable>()" ?
Maybe I chose the name for the other class wrong.
Well, the clone-function was declared as:
template <class T=current_class> T* clone() {return new T(this);}
now you might want to use the same function to create objects of other
classes that had the proper constructor. for example, assuming
class X is defined:
class X {
// ...
X(const Clonable&);
};
Now to use the clone (member) template to create an X from a clonable
object, you could use:
Clonable* obj;
obj = new X;
X* obj2= obj->template clone<X>();
That is, a member template call [temp.arg.explicit]/2 is
used to call the clone function instantiated with explicit parameter
X, so the call "obj->template clone<X>()" will call the X(const Clonable&)
constructor instead of the copy constructor.
Of course all this kind of calls would be static (since explicit
class name is provided).
--
Esa Pulkkinen | C++ programmers do it virtually
E-Mail: esap@cs.tut.fi | everywhere with a class, resulting
WWW : http://www.cs.tut.fi/~esap/ | in multiple inheritance.
Author: esap@cs.tut.fi (Pulkkinen Esa)
Date: 1995/07/10 Raw View
In article <3trctf$aql@steel.interlog.com>,
Herb Sutter <herbs@interlog.com> wrote:
[ a code example with use of current_class in different translation unit
with the declaration of the class ]
>Good point, but first let me repeat my intended definition: using
>current_class is an implicitly parameterised type (template) always filled
>with the current most-derived class. That said, when you write a template
>that uses a parameterised class' member function (e.g., operator++) it must
>be visible to all instantiated/derived classes; i.e., it must be in the
>header, and not in a different xlation unit.
That makes sense.
[ My explanation of how using current_class in different translation
unit from class's declaration leads to difficult parsing problem removed ]
>Aha, no! :-) If it could be in a different translation unit, that would
>break
>everything... and clearly it has to be in the same translation unit, for
>that's how templates work and to use current_class is to use a template (just
>one that you happen to never need to fill in manually, that's the only
>difference).
If I understand you correctly, current_class is just a strange way
of writing a template _parameter_, which the compiler automatically
fills with the current class's name dynamically by just generating a call
to the right function. Maybe there should
be more verbose indications in the syntax that it's a template, otherwise
people write code using it as if it were a variable (type), and
cause unnecessary code bloat, or problems with instantiation.
My suggestion:
class X
{
template <class T=current_class> void do_f(void)
{
f(dynamic_cast<T*>(this));
}
};
This would indicate that it's a template more than it is a dynamic
dispatch, maybe even allow using the same template also for static calls.
--
Esa Pulkkinen | C++ programmers do it virtually
E-Mail: esap@cs.tut.fi | everywhere with a class, resulting
WWW : http://www.cs.tut.fi/~esap/ | in multiple inheritance.
Author: herbs@interlog.com (Herb Sutter)
Date: 1995/07/11 Raw View
In article <3trs8e$ru@peippo.cs.tut.fi>, esap@cs.tut.fi (Pulkkinen Esa) wrote:
>
>If I understand you correctly, current_class is just a strange way
>of writing a template _parameter_, which the compiler automatically
>fills with the current class's name dynamically by just generating a call
>to the right function. Maybe there should
>be more verbose indications in the syntax that it's a template, otherwise
>people write code using it as if it were a variable (type), and
>cause unnecessary code bloat, or problems with instantiation.
>My suggestion:
>
>class X
>{
> template <class T=current_class> void do_f(void)
> {
> f(dynamic_cast<T*>(this));
> }
>};
>This would indicate that it's a template more than it is a dynamic
>dispatch, maybe even allow using the same template also for static calls.
This is a good point, and I think a good alternative. A few thoughts on the
ramifications of making this an explicit template:
1) It seems to solve the problems some mentioned about "how does the compiler
know" (whether current_class is used). True, the compiler could still look
through the source which would be in the same xlation unit, but making the
template explicit may make things easier for compiler writers (though perhaps
a little harder in other ways, see next point).
2) The template parm still should not be entered by any client code... if
there are other templated parms, perhaps the one defaulting to current_class
should always be last and never be instantiable directly by the user. IOW, in
your example the user would always write something like "X::do_f()", never
"X::do_f<??>()". If there were additional parameters, like:
template<class A, class T=current_class> void do_f(void) { /*...*/ };
then the user would always write something like "X::do_f<MyClass>()", and
again never be allowed to supply the parameter defaulting to current_class.
This would require changing the default-arg rules slightly to accomodate this
special case; I'm not against that in principle, but I did like the original
(implicit template) idea because it didn't require any changes to existing
rules.
3) That said, sometimes classes themselves (not just member functions) need to
know current_class, and therefore you might have "template<class
T=current_class> class X { /* ...uses current_class info, say to store a
pointer...*/ };".
Overall, it seems to me that both the implicit and explicit forms have
advantages: the implicit form prevents the user from ever supplying the
template parm without requiring the compiler to do extra work to prevent it
(since the template parm is not visible to the user); the explicit form may
make things easier for the compiler by declaring which classes/functions use
current_class information. I'm still leaning toward the implicit form just
because it doesn't require any existing rules to change, but if it's not as
easy to implement as it appears to a non-compiler writer like me, then
supplying the functionality through a special form of template default parm
should be a workable alternative.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Herb Sutter 2228 Urwin, Ste 102 voice (416) 618-0184
Connected Object Solutions Oakville ON Canada L6L 2T2 fax (905) 847-6019
Author: herbs@interlog.com (Herb Sutter)
Date: 1995/07/11 Raw View
In article <3trn2i$25s@usenet.rpi.edu>,
vandevod@avs.cs.rpi.edu (David Vandevoorde) wrote
[some of my own quotes elided]:
>
>[ in the following, multiple versions of the regular function f(X) are
> assumed to exist ]
>
>> class A {
>> public:
>> void do_f() { f(dynamic_cast<current_class*>(this)); }
>> };
>
>But if you use "current_class" only outside the class declaration, this
>implicit template character cannot be recognized from the declaration
>alone. This would pretty much rule out repository-based instantiation
>mechanisms (and my understanding of Stroustrup's "Design & Evolution of
>C++" is that this repository approach is more "in the spirit of C++"
>than the manual inclusion of member definitions).
Mmm, now I see where you're coming from. Good point. In another article in
this thread, Esa wrote:
>class X
>{
> template <class T=current_class> void do_f(void)
> {
> f(dynamic_cast<T*>(this));
> }
>};
I've followed it up in another article, but this seems to be a viable
alternative and looks like it would solve the problem you mention, since now
the declaration alone is enough -- i.e., the templating has been made
explicit.
A small concern: this form would require a minor change to existing rules to
prevent users from filling in that (now-visible) template parm.
That concern aside, would such a declaration solve the problems you mentioned?
I'm not a compiler writer, so I'm relying on those who know more about it to
let me know how implementable any particular solution is.
>However, even _if_ inclusion of definitions along with declarations of
>parameterized classes were mandated, I suspect it would burden the
>compiler significantly because it could never be sure of whether a
>declaration was a true class of a class template until it saw all
>its member definitions... but how would it know that it saw _all_
>thoses definitions when some members could be inherited or over-
>ridden.
Hmm. What if we made the templating explicit, as above: doesn't the last also
apply to current instantiated/inherited/overridden templates? So how is it
being resolved now? I've never seen code that tries to override a template
member function, so I can't say whether it's legal or not; but whatever
solution is in place now should still work, shouldn't it? This is after all a
template -- one that happens to have a little more compiler support because
the compiler fills it in rather than the user, and the compiler knows to
change pointer types down the hierarchy, but other than that what works for
templates today ought to work for current_class.
>In summary, it is the _implicit_ template character of current_class
>that makes it unviable (IMHO): templates are currently identified as
>through their declaration only (independently of their definition).
>(I think that was also the point of another posting; I didn't read
>it carefully enough):
Thanks again, Daveed. You're making me more aware of the implementation
issues. Please let me know whether you think making the template explicit
would solve the problems.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Herb Sutter 2228 Urwin, Ste 102 voice (416) 618-0184
Connected Object Solutions Oakville ON Canada L6L 2T2 fax (905) 847-6019
Author: jbuck@synopsys.com (Joe Buck)
Date: 1995/07/11 Raw View
esap@cs.tut.fi (Pulkkinen Esa) writes:
>If I understand you correctly, current_class is just a strange way
>of writing a template _parameter_, which the compiler automatically
>fills with the current class's name dynamically by just generating a call
>to the right function. Maybe there should
>be more verbose indications in the syntax that it's a template, otherwise
>people write code using it as if it were a variable (type), and
>cause unnecessary code bloat, or problems with instantiation.
>My suggestion:
class X
{
template <class T=current_class> void do_f(void)
{
f(dynamic_cast<T*>(this));
}
};
>This would indicate that it's a template more than it is a dynamic
>dispatch, maybe even allow using the same template also for static calls.
This is a very special kind of template, one which is re-implemented for
every deriving class. I agree that the template notation is better. It
seems to me, though, that if you consider it that way, for many uses you
no longer need the dynamic casts: the templates are expanded at compile
time so we have the compile-time type of the objects. We already have
covariant return types.
Consider the clone operation: we want all objects derived from a
particular base class to be able to duplicate themselves.
class Clonable
{
public:
template <class T=current_class> virtual T * clone() const
{
return new T(*this);
}
};
Now each class derived from Clonable has a proper clone method.
Here we see why this is very different from a template: you can safely
make a virtual function this way because there's exactly one function
in each class, where existing member templates correspond to an indefinite
number of functions.
While this is interesting, I think it's too late for this round of
standardization. It might be worth more investigation and
experimentation.
--
-- Joe Buck <jbuck@synopsys.com> (not speaking for Synopsys, Inc)
Anagrams for "information superhighway": Enormous hairy pig with fan
A rough whimper of insanity
Author: vandevod@avs.cs.rpi.edu (David Vandevoorde)
Date: 1995/07/11 Raw View
In article <3ttt91$oqu@steel.interlog.com>,
Herb Sutter <herbs@interlog.com> wrote:
>In article <3trn2i$25s@usenet.rpi.edu>,
> vandevod@avs.cs.rpi.edu (David Vandevoorde) wrote
>[some of my own quotes elided]:
>>
>>[ in the following, multiple versions of the regular function f(X) are
>> assumed to exist ]
>>
>>> class A {
>>> public:
>>> void do_f() { f(dynamic_cast<current_class*>(this)); }
>>> };
[...]
>Mmm, now I see where you're coming from. Good point. In another article in
>this thread, Esa wrote:
>
>>class X
>>{
>> template <class T=current_class> void do_f(void)
>> {
>> f(dynamic_cast<T*>(this));
>> }
>>};
>
>I've followed it up in another article, but this seems to be a viable
>alternative and looks like it would solve the problem you mention, since now
>the declaration alone is enough -- i.e., the templating has been made
>explicit.
... in the declaration (as opposed to the definition).
>
>A small concern: this form would require a minor change to existing rules to
>prevent users from filling in that (now-visible) template parm.
Well, I had the same reflex at first and thus thought the "better
syntax" should be:
template<this_class> ...
but when I thought it over, I saw no reason to prevent users from
specifying the 'T' in
template<class T = this_class> ...
BTW, we could avoid the introduction of a new keyword with:
template<class T = this> ...
>
>That concern aside, would such a declaration solve the problems you mentioned?
Yes. However, it may behave slightly differently from your proposal.
Let's semi-formalize the specifications of the new approach.
A type-parameter for a class template or a member function
template can have a default value "this", indicating that the
type-parameter should be subsituted by the complete class
for which the template is instantiated.
The grammar ([temp.param]) would be updated with:
_type-parameter_:
...
class _identifier_ = this
typename _identifier_ = this
Example:
template<typename T = this>
struct singleton {
static T* instance() const
{ return instance_? instance_: (instance_ = new T); }
protected:
singleton() {}
private:
static T* instance_;
};
struct B1: singleton {
};
template<typename T = this>
struct B2: singleton<T> {
};
struct D1: B1 {
};
struct D2: B2 {
};
f() {
D2* d2 = D2::instance(); // OK
D1* d1 = D1::instance(); // Not OK, requires a cast.
}
Note that D2 is derived from B2<D2> which in turn is derived from
singleton<D2>. D1 on the other hand, is derived from the non-template
class B1 which has a base class singleton<B1>.
No relaxations as to what can be templated should be introduced
(I hope :). In particular, there can still be no virtual member
function templates.
>
>>However, even _if_ inclusion of definitions along with declarations of
>>parameterized classes were mandated, I suspect it would burden the
>>compiler significantly because it could never be sure of whether a
>>declaration was a true class of a class template until it saw all
>>its member definitions... but how would it know that it saw _all_
>>thoses definitions when some members could be inherited or over-
>>ridden.
>
>Hmm. What if we made the templating explicit, as above: doesn't the last also
>apply to current instantiated/inherited/overridden templates? So how is it
No, since once the declaration of a class is parsed, it is unambiguous
which member variables and member functions are parameterized and which
are not.
Let me clarify with an example. Assuming the "implicit scheme":
struct B {
void f();
};
g() {
B b;
b.f(); // OK, no problem B is a simple non-template class.
}
void B::f() {
h(static_cast<current_class*>(this));
// ?? Oh ?? B was a template after all?? Too late... :-P
}
You could do even dirtier tricks by inheriting from B...
>being resolved now? I've never seen code that tries to override a template
[...]
>Thanks again, Daveed. You're making me more aware of the implementation
>issues. Please let me know whether you think making the template explicit
>would solve the problems.
You're welcome. This thread is a lot more stimulating than many I've
seen in the comp.*.c++ groups.
I think the explicit scheme is workable and worthwile. Of course, to have
it added to the language at this point, there still should be a strong
justification/motivation in terms of usability. I don't have the patterns
book by Vlissides et al.; I'll try to save some cash this week to remedy
that handicap ;-)
>Herb Sutter 2228 Urwin, Ste 102 voice (416) 618-0184
>Connected Object Solutions Oakville ON Canada L6L 2T2 fax (905) 847-6019
All the best,
Daveed
Author: esap@cs.tut.fi (Pulkkinen Esa)
Date: 1995/07/12 Raw View
In article <3ttsia$oqu@steel.interlog.com>,
Herb Sutter <herbs@interlog.com> wrote:
>2) The template parm still should not be entered by any client code... if
>there are other templated parms, perhaps the one defaulting to current_class
>should always be last and never be instantiable directly by the user. IOW,
>in your example the user would always write something like "X::do_f()", never
>"X::do_f<??>()". If there were additional parameters, like:
>
> template<class A, class T=current_class> void do_f(void) { /*...*/ };
>
>then the user would always write something like "X::do_f<MyClass>()", and
>again never be allowed to supply the parameter defaulting to current_class.
I can't see any reason why the template parameter couldn't be provided
by the user (this was pointed out already in other thread),
current_class (clearly?) just can't be used as an explicit template
parameter, only as a default.
>3) That said, sometimes classes themselves (not just member functions)
>need to
>know current_class, and therefore you might have "template<class
>T=current_class> class X { /* ...uses current_class info, say to store a
>pointer...*/ };".
This is a good observation. First application that comes to my mind is
registration of classes through inheritance:
template <class T=current_class>
class X
{
static Register_server id;
public:
static current_class *Create() { return new T; }
// ...
};
template <class T=current_class>
Register_server X<T>::id( &X::create );
class Y : public X { };
// nothing else needed, there is an individual id
// object in Y also
Now, this is a powerful technique, with proper definition of Register_server,
allows the base class (or code using the base class) to get information
about (all) derived classes, create objects of derived classes they
have never heard of, and impose various constraints to them
(like if you declared a static (data or function) member
but left it undefined except for the base class, all derived classes
,whether directly or indirectly inherited, would have to implement it.).
btw, to respond to the another thread that suggested using 'this'
instead of 'current_class' to prevent inventing a new keyword:
I find 'this' keyword somewhat confusing, since it has traditionally
been used as a pointer, and it already has a meaning as template
default argument. I would think 'virtual' would be better choice,
if inventing a new keyword is such a bad thing.
--
Esa Pulkkinen | C++ programmers do it virtually
E-Mail: esap@cs.tut.fi | everywhere with a class, resulting
WWW : http://www.cs.tut.fi/~esap/ | in multiple inheritance.
Author: esap@cs.tut.fi (Pulkkinen Esa)
Date: 1995/07/12 Raw View
In article <3tuv75$79p@hermes.synopsys.com>,
Joe Buck <jbuck@synopsys.com> wrote:
>
>Consider the clone operation: we want all objects derived from a
>particular base class to be able to duplicate themselves.
>
>class Clonable
>{
>public:
> template <class T=current_class> virtual T * clone() const
> {
> return new T(*this);
> }
>};
>
>Now each class derived from Clonable has a proper clone method.
>
>Here we see why this is very different from a template: you can safely
>make a virtual function this way because there's exactly one function
>in each class, where existing member templates correspond to an indefinite
>number of functions.
No. There _could_ be many of the functions in the same class, consider
a call to the clone method:
Clonable *obj;
OtherClonable *cloned=obj->template clone<OtherClonable>();
Of course this requires that OtherClonable class has a corresponding
constructor that takes Clonable& as parameter.
I see no reason to disallow such code. BUT, the function can only
be "virtual" in those instances, where the actual template parameter
is the same as the current class's name (the current_class keyword
ensures that) This is why I didn't write the function virtual in
my original example. The current_class-
keyword would have to be enough to inform compiler that the function
actually called is decided run-time, and static calls could be made
by supplying the template argument. (one of the reasons I would think
replacing 'current_class' with 'virtual' might work, but since
the whole discussion so far speaks of current_class, I do too...)
--
Esa Pulkkinen | C++ programmers do it virtually
E-Mail: esap@cs.tut.fi | everywhere with a class, resulting
WWW : http://www.cs.tut.fi/~esap/ | in multiple inheritance.
Author: herbs@interlog.com (Herb Sutter)
Date: 1995/07/09 Raw View
In article <3tn2mm$sq4@usenet.rpi.edu>,
vandevod@avs.cs.rpi.edu (David Vandevoorde) wrote:
>In article <3tmsck$gnv@steel.interlog.com>,
>Herb Sutter <herbs@interlog.com> wrote:
>> class Singleton {
>> static Singleton* Instance() {
>> if (!_instance)
>> _instance = new Singleton;
>> return _instance;
>> }
>> private:
>> static Singleton* _instance; // omitting for now the issue
>> }; // of how to even manage this
>>
>> class MySingle : public Singleton {
>> public:
>> MyMethod (...);
>> }; // not quite good enough
>>
>>The main problem is that this solution is not equivalent to writing
>>our singleton class manually, for now applications can no longer
>>call:
>>
>> MySingle::Instance()->MyMethod(); // error
>
>Do it via a regular function template instead:
>
> template<class C> inline
> C* instance<C>() {
> return static_cast<C*>(C::Instance());
> }
>
>so your above call becomes:
>
> instance<MySingle>()->MyMethod();
Thanks for the comment. I've gome through several phases of reactions and
have spent much more time than I originally intended thinking about it. :-)
Hopefully I've successfully cancelled my earlier (incorrect) followups.
Here's the analysis I ended up with:
This has several problems. From least to greatest, they are: a) it seems
inelegant because it removes pattern functions from the pattern; b) it is
error-prone since it relies on programmer discipline in the client code; c) it
does not solve the problem since Singleton still needs derived class
information for other reasons (i.e., to declare its _instance pointer with the
correct type).
It also does not solve the other Examples: For instance, we cannot get Example
2 s mainline, but would end up with something like:
template<class C>
do_f(C c) { f(c); };
main() {
B b;
do_f<B>(b); // essentially doing manual polymorphism instead of b.do_f()
// also, doesn't protect against do_f<A>(b)
}
For the commented reasons, this isn t equivalent to the proposed current_class
solution. Speaking of Example 2:
>>EXAMPLE 2: MULTIPLE DISPATCH
>[...]
>>In article <DB9CtB.6Er@eunet.ch>,
>> xar@pax.eunet.ch (Benjamin Rosenbaum) wrote:
>>>Wouldn't it be nice if function overloading were
>>>polymorphic by arguments, so that in...
>>>
>>> class A {
>>> void do_f() {f(this);}
>>> };
>>> class B: public A {};
>>>
>>> void f(A &){cout << "this gets called...";};
>>> void f(B &){cout << "...but wouldn't this be better?"};
>
>Should be "A*" and "B*" I guess.
I meant to leave the original poster's code as-is; but you're right, and I've
corrected it (and the three other errors) in the comment.
>>>
>>> main ()
>>> {
>>> B b;
>>> b.do_f();
>>> }
>>>
>>>....f(B &) would get called?
<snip>
>>However, consider:
>>
>> class A
>> {
>> public:
>> void do_f() { f(dynamic_cast<current_class*>(this)); }
>> };
>>
>>Now this program works as desired, with the rest of the code
>
>And how must a compiler/linker resolve that call?
>I think that for this to work, we would have to add a significant burden
>to compiler writers.
What, specifically? If we treat current_class as an implicit parameterised
type (template) that is specially supported to be always filled in with the
current type (which the compiler already knows about in all the cases where it
can be used), why won't the existing template rules work?
>I feel C++ often offers alternatives to
>"mixins" which are more orthogonal to the inheritance concept.
Thanks for the comments. Please elaborate.
> Daveed
Regards,
Herb
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Herb Sutter 2228 Urwin, Ste 102 voice (416) 618-0184
Connected Object Solutions Oakville ON Canada L6L 2T2 fax (905) 847-6019
Author: esap@cs.tut.fi (Pulkkinen Esa)
Date: 1995/07/09 Raw View
In article <3toq7e$qb2@steel.interlog.com>,
Herb Sutter <herbs@interlog.com> wrote:
>>>However, consider:
>>>
>>> class A
>>> {
>>> public:
>>> void do_f() { f(dynamic_cast<current_class*>(this)); }
>>> };
[a question about how to implement it removed]
>What, specifically? If we treat current_class as an implicit parameterised
>type (template) that is specially supported to be always filled in with the
>current type (which the compiler already knows about in all the cases where it
consider: (this is your example with definition of do_f moved to some other
file).
class X
{
public:
void do_f();
};
// In some other file:
void X::do_f()
{
f(dynamic_cast<current_class*>(this));
}
Now, how would you implement this?
a) make a new virtual function table entry for f() in X and
call it through it? No, since the virtual function table must
be known after complete class declaration. Only linker could
compose virtual function tables, which has repeatedly been
observed as unaffordable.
b) make a table of possible functions that can be called when the
function do_f is defined. Can't, because you can't know what
classes might derive from X, all such classes would have to be known.
c) generate new X::do_f() functions for each derived class.
This would require a new virtual function table entry for
do_f, otherwise the dynamic_cast would be equal to
"dynamic_cast<X*>(this)", and the call would still go to
wrong destination. That is, do_f would have to be virtual.
But even if it were virtual, only the linker could build the derived
class's virtual function table, because the compiler doesn't
know that X::do_f() has used current_class until it has seen
the definition, which might be in different translation unit from
all derived classes.
d) using some extralinguistic magic, change the program as if it were:
(i.e. I think it can't be done without explicitly declaring somehow
which functions use current_class.)
class X
{
template <class T> void __do_f_impl();
virtual void do_f() { this->template __do_f_impl<current_class>(); }
// this is easier to generate, assuming the definition is
// in the class declaration.
};
// in the translation unit defining do_f:
template <class T> void X::__do_f_impl<T>()
{
f(dynamic_cast<T*>(this));
}
e) what other alternatives are there?
[Note followups only to comp.std.c++]
--
Esa Pulkkinen | C++ programmers do it virtually
E-Mail: esap@cs.tut.fi | everywhere with a class, resulting
WWW : http://www.cs.tut.fi/~esap/ | in multiple inheritance.
Author: vandevod@troi.cs.rpi.edu (David Vandevoorde)
Date: 1995/07/09 Raw View
In article <3toq7e$qb2@steel.interlog.com>,
Herb Sutter <herbs@interlog.com> wrote:
>In article <3tn2mm$sq4@usenet.rpi.edu>,
> vandevod@avs.cs.rpi.edu (David Vandevoorde) wrote:
>>In article <3tmsck$gnv@steel.interlog.com>,
>>Herb Sutter <herbs@interlog.com> wrote:
>>> class Singleton {
>>> static Singleton* Instance() {
>>> if (!_instance)
>>> _instance = new Singleton;
>>> return _instance;
>>> }
>>> private:
>>> static Singleton* _instance; // omitting for now the issue
>>> }; // of how to even manage this
>>>
>>> class MySingle : public Singleton {
>>> public:
>>> MyMethod (...);
>>> }; // not quite good enough
>>>
>>>The main problem is that this solution is not equivalent to writing
>>>our singleton class manually, for now applications can no longer
>>>call:
>>>
>>> MySingle::Instance()->MyMethod(); // error
>>
>>Do it via a regular function template instead:
>>
>> template<class C> inline
>> C* instance<C>() {
>> return static_cast<C*>(C::Instance());
>> }
>>
>>so your above call becomes:
>>
>> instance<MySingle>()->MyMethod();
>
>Thanks for the comment. I've gome through several phases of reactions and
>have spent much more time than I originally intended thinking about it. :-)
>Hopefully I've successfully cancelled my earlier (incorrect) followups.
>Here's the analysis I ended up with:
>
>This has several problems. From least to greatest, they are: a) it seems
>inelegant because it removes pattern functions from the pattern; b) it is
>error-prone since it relies on programmer discipline in the client code; c) it
>does not solve the problem since Singleton still needs derived class
>information for other reasons (i.e., to declare its _instance pointer with the
>correct type).
a) I suppose that's a matter of taste. There is indeed a certain
"aesthetism" in keeping a whole pattern in a single compact unit
(in casu: a class), but - as a aluded to in my former comment -
this may not be a preferred approach in C++. I feel the STL is a
nice example demonstrating that C++ idioms often benefit from a
(loose) collaboration between classes and regular functions.
b) Why? If you don't provide a way for the client to instantiate or
access the singleton other than the instance<C> function that you
provide, the client will have no choice but to use the pattern
correctly (or will have compile errors otherwise).
c) Not really. You can keep the "_instance" pointer to he base subobject
and rely on the safe cast in the instance<C> function.
>
>It also does not solve the other Examples: For instance, we cannot get Example
>2 s mainline, but would end up with something like:
I intended the comments above only for that particular idiom. I admit I
commented on that idiom mostly because I recognized it and have used and
refined it quite a bit in the past.
>
> template<class C>
> do_f(C c) { f(c); };
>
> main() {
> B b;
> do_f<B>(b); // essentially doing manual polymorphism instead of b.do_f()
> // also, doesn't protect against do_f<A>(b)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A matter of taste (again :) maybe, but I do not want protection from it
because I feel its a natural case of clipping; in fact, it _explicitly_
requests the clipping (though off hand, I'm not sure it's legal :).
> }
>
>For the commented reasons, this isn t equivalent to the proposed current_class
>solution. Speaking of Example 2:
>
>>>EXAMPLE 2: MULTIPLE DISPATCH
>>[...]
>>>In article <DB9CtB.6Er@eunet.ch>,
>>> xar@pax.eunet.ch (Benjamin Rosenbaum) wrote:
>>>>Wouldn't it be nice if function overloading were
>>>>polymorphic by arguments, so that in...
>>>>
[ errors purposefully left in for sake of integrity ]
>>>> class A {
>>>> void do_f() {f(this);}
>>>> };
>>>> class B: public A {};
>>>>
>>>> void f(A &){cout << "this gets called...";};
>>>> void f(B &){cout << "...but wouldn't this be better?"};
>>>>
>>>> main ()
>>>> {
>>>> B b;
>>>> b.do_f();
>>>> }
>>>>
>>>>....f(B &) would get called?
>
><snip>
>
>>>However, consider:
>>>
>>> class A
>>> {
>>> public:
>>> void do_f() { f(dynamic_cast<current_class*>(this)); }
>>> };
>>>
>>>Now this program works as desired, with the rest of the code
>>
>>And how must a compiler/linker resolve that call?
>>I think that for this to work, we would have to add a significant burden
>>to compiler writers.
>
>What, specifically? If we treat current_class as an implicit parameterised
>type (template) that is specially supported to be always filled in with the
>current type (which the compiler already knows about in all the cases where it
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I guess this is where I am a bit confused. How can the compiler know what
"current_class" will be in class A when the derived classes haven't been
compiled (or, for that sake written) yet?
If the inheritance mechanism has to be aware of the dependencies of
certain member functions on "current_class" I fear things may get very
complex. Especially when current_class is only used outside the class
declaration; in that case the inheriting class is totally unaware of
the template_like character in its base.
I suppose I'm missing a point somewhere...?
>can be used), why won't the existing template rules work?
>
>Thanks for the comments. Please elaborate.
You're welcome. I tried clarifying my thoughts above. My main concerns
are about realizability; I haven't studied you example patterns well
enough to come up with satisfactory (to me :) work-arounds for each of
them (the singleton being an exception).
>Herb Sutter 2228 Urwin, Ste 102 voice (416) 618-0184
>Connected Object Solutions Oakville ON Canada L6L 2T2 fax (905) 847-6019
Daveed
Author: herbs@interlog.com (Herb Sutter)
Date: 1995/07/10 Raw View
In article <3tp6du$qfh@usenet.rpi.edu>,
vandevod@troi.cs.rpi.edu (David Vandevoorde) wrote:
>>>> class Singleton {
>>>> static Singleton* Instance() {
>>>> if (!_instance)
>>>> _instance = new Singleton;
>>>> return _instance;
>>>> }
>>>> private:
>>>> static Singleton* _instance; // omitting for now the issue
>>>> }; // of how to even manage this
>>>>
>>>> class MySingle : public Singleton {
>>>> public:
>>>> MyMethod (...);
>>>> }; // not quite good enough
>>>>
>>>>The main problem is that this solution is not equivalent to writing
>>>>our singleton class manually, for now applications can no longer call:
>>>> MySingle::Instance()->MyMethod(); // error
>>>
>>>Do it via a regular function template instead:
>>> template<class C> inline
>>> C* instance<C>() {
>>> return static_cast<C*>(C::Instance());
>>> }
>>>so your above call becomes:
>>> instance<MySingle>()->MyMethod();
>>
>>This has several problems. From least to greatest, they are: a) it seems
>>inelegant because it removes pattern functions from the pattern; b) it is
>>error-prone since it relies on programmer discipline in the client code; c)
>>it does not solve the problem since Singleton still needs derived class
>>information for other reasons (i.e., to declare its _instance pointer with
>>the correct type).
>
>a) I suppose that's a matter of taste. There is indeed a certain
> "aesthetism" in keeping a whole pattern in a single compact unit
> (in casu: a class), but - as a aluded to in my former comment -
> this may not be a preferred approach in C++. I feel the STL is a
> nice example demonstrating that C++ idioms often benefit from a
> (loose) collaboration between classes and regular functions.
True. Specifically what I was getting at was that doing it this way tends to
draw away from the design goal of simply mixing in behaviour a la ": public
Pattern" with no extra work/knowledge required for either the derived-class
programmer or for the client code.
Another point in this line is that, this way, every function requiring
current_class information would have to be split out into its own templated
function. I can think of simple mixins where every function requires it,
leaving little for the mixin to do.
>b) Why? If you don't provide a way for the client to instantiate or
> access the singleton other than the instance<C> function that you
> provide, the client will have no choice but to use the pattern
> correctly (or will have compile errors otherwise).
In this case, you're quite right (which is why I spent so much time thinking
about your suggestion). However, I was thinking more of the other examples
(as below) where we will be relying on programmer discipline to "get the class
right", which would be unfortunate. (You see, in this case, we only need to
pass a class to the function; in Example 2 we need to pass both a class and an
instance.)
>c) Not really. You can keep the "_instance" pointer to he base subobject
> and rely on the safe cast in the instance<C> function.
Hmm. True, but... hmm. Let me think about this one so that I have time to
get over my initial "but that's ugly" reaction. :-,
One quick thought: In the other examples, we have classes storing (non-static)
pointers to their current_class; if we then rely on function templates, we get
back to the "programmer discipline" problem if client code calls one of our
valid functions with a valid class and a valid instance, but the class and
instance don't go together (i.e., dynamic_cast fails; perhaps static_cast
would help alleviate this, but I want to think some more).
At any rate, my initial gut feel is that we're relying on client code too
much, and putting a burden on the client programmer... even if we can arrange
it so that all illegal things are trapped as errors (which I'm not convinced
we can), it seems to me the actual error messages he'll get aren't terribly
enlightening (consider what you get sometimes when using STL the wrong way).
IOW, the client programmer will need to become familiar with our
implementation details to diagnose errors easily... he shouldn't need to do
that.
>>It also does not solve the other Examples: For instance, we cannot get
>>Example 2 s mainline, but would end up with something like:
>
>I intended the comments above only for that particular idiom. I admit I
>commented on that idiom mostly because I recognized it and have used and
>refined it quite a bit in the past.
BTW, don't get me wrong... you're quite right that your suggestion can pretty
much solve Singleton using existing rules. The unfortunate part is that
Singleton is (as mentioned in the writeup) a very simple example and it took
some thinking for me to prove to myself that the solution doesn't help equally
with the other cases. I do appreciate your making the point.
>> template<class C>
>> do_f(C c) { f(c); };
>>
>> main() {
>> B b;
>> do_f<B>(b); // essentially doing manual polymorphism instead of b.do_f()
>> // also, doesn't protect against do_f<A>(b)
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>A matter of taste (again :) maybe, but I do not want protection from it
>because I feel its a natural case of clipping; in fact, it _explicitly_
>requests the clipping (though off hand, I'm not sure it's legal :).
It should be; with "class B: public A" you can throw a B& into an A& slot.
At any rate, I agree that something you want to slice the object; but if so,
you can always use this kind of design. My thought was that in the original
we are specifically concerned with matching up types properly and preventing
slicing; we ought to have a way to implement that without relying on the
client's good graces.
>>>> class A {
>>>> public:
>>>> void do_f() { f(dynamic_cast<current_class*>(this)); }
>>>> };
>>>
>>>And how must a compiler/linker resolve that call?
>>>I think that for this to work, we would have to add a significant burden
>>>to compiler writers.
>>
>>What, specifically? If we treat current_class as an implicit parameterised
>>type (template) that is specially supported to be always filled in with the
>>current type (which the compiler already knows about in all the cases where
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>I guess this is where I am a bit confused. How can the compiler know what
>"current_class" will be in class A when the derived classes haven't been
>compiled (or, for that sake written) yet?
When compiling class A, it knows it's an A* and doesn't need any other
information. When it's compiling class B (derived from A) it knows it's a B*
and doesn't need any other information... etc. This falls out of the fact
that "using current_class" is implicitly parameterising on (templating) a type
which happens to be always filled in by the compiler as the current type. All
it is is an implicit template with a little automatic support to fill it in
transparently so the derived classes don't have to (since according to the
language rules, as shown, they cannot fill it in properly).
>If the inheritance mechanism has to be aware of the dependencies of
>certain member functions on "current_class" I fear things may get very
>complex. Especially when current_class is only used outside the class
>declaration; in that case the inheriting class is totally unaware of
>the template_like character in its base.
>I suppose I'm missing a point somewhere...?
This doesn't seem much different than a template's using member functions of a
parameterised class, such as an operator++... they are required to be visible
so that the compiler does know about them (and therefore are placed in the
header), and I can't offhand see how this would be different.
Again, these are good comments; but is there any case where the template rules
fail us? It seems to me that current_class is just an implicit template, and
the only difference is that it gets a little special compiler support for one
feature that the programmer can't easily supply himself: it's always
instantiated as the current class. Does saying it this way make any more
sense? I could be missing something, too, but if so I can't quite see what it
is.
Regards,
Herb
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Herb Sutter 2228 Urwin, Ste 102 voice (416) 618-0184
Connected Object Solutions Oakville ON Canada L6L 2T2 fax (905) 847-6019
Author: vandevod@avs.cs.rpi.edu (David Vandevoorde)
Date: 1995/07/10 Raw View
[as suggested by another contributor - name escapes me - I put the
followup field to comp.std.c++; I hope it works ]
In article <3trcdu$aql@steel.interlog.com>,
Herb Sutter <herbs@interlog.com> wrote:
>In article <3tp6du$qfh@usenet.rpi.edu>,
> vandevod@troi.cs.rpi.edu (David Vandevoorde) wrote:
[ discussion on elegant implementation of singletons elided ]
[ in the following, multiple versions of the regular function f(X) are
assumed to exist ]
>>>>> class A {
>>>>> public:
>>>>> void do_f() { f(dynamic_cast<current_class*>(this)); }
>>>>> };
>>>>
>>>>And how must a compiler/linker resolve that call?
>>>>I think that for this to work, we would have to add a significant burden
>>>>to compiler writers.
>>>
>>>What, specifically? If we treat current_class as an implicit parameterised
>>>type (template) that is specially supported to be always filled in with the
>>>current type (which the compiler already knows about in all the cases where
>> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>>I guess this is where I am a bit confused. How can the compiler know what
>>"current_class" will be in class A when the derived classes haven't been
>>compiled (or, for that sake written) yet?
>
>When compiling class A, it knows it's an A* and doesn't need any other
>information. When it's compiling class B (derived from A) it knows it's a B*
>and doesn't need any other information... etc. This falls out of the fact
>that "using current_class" is implicitly parameterising on (templating) a type
But if you use "current_class" only outside the class declaration, this
implicit template character cannot be recognized from the declaration
alone. This would pretty much rule out repository-based instantiation
mechanisms (and my understanding of Stroustrup's "Design & Evolution of
C++" is that this repository approach is more "in the spirit of C++"
than the manual inclusion of member definitions).
However, even _if_ inclusion of definitions along with declarations of
parameterized classes were mandated, I suspect it would burden the
compiler significantly because it could never be sure of whether a
declaration was a true class of a class template until it saw all
its member definitions... but how would it know that it saw _all_
thoses definitions when some members could be inherited or over-
ridden.
I really the concept is only viable if C++ was equipped with a
"true" module system... but I understand such a radical feature
will not be added to C++ anytime soon.
>which happens to be always filled in by the compiler as the current type. All
>it is is an implicit template with a little automatic support to fill it in
>transparently so the derived classes don't have to (since according to the
>language rules, as shown, they cannot fill it in properly).
>
[ removed my vaguer previous argument ]
>
>This doesn't seem much different than a template's using member functions of a
>parameterised class, such as an operator++... they are required to be visible
>so that the compiler does know about them (and therefore are placed in the
>header), and I can't offhand see how this would be different.
Actually, the definitions are not required to be in the header according
to the draft submitted for review. Note also that there is a distinction
between a "(standard) header" (which can be seen as a language feature) and
a "header file" which is a purely conventional use of a preprocessor
feature. Unfortunately, many compilers impose the inclusion of template
declarations and definitions into a single translation unit.
>
>Again, these are good comments; but is there any case where the template rules
>fail us? It seems to me that current_class is just an implicit template, and
>the only difference is that it gets a little special compiler support for one
>feature that the programmer can't easily supply himself: it's always
>instantiated as the current class. Does saying it this way make any more
>sense? I could be missing something, too, but if so I can't quite see what it
>is.
In summary, it is the _implicit_ template character of current_class
that makes it unviable (IMHO): templates are currently identified as
through their declaration only (independently of their definition).
(I think that was also the point of another posting; I didn't read
it carefully enough):
>Herb Sutter 2228 Urwin, Ste 102 voice (416) 618-0184
>Connected Object Solutions Oakville ON Canada L6L 2T2 fax (905) 847-6019
Daveed
Author: herbs@interlog.com (Herb Sutter)
Date: 1995/07/10 Raw View
In article <3tpau2$8ne@peippo.cs.tut.fi>,
esap@cs.tut.fi (Pulkkinen Esa) wrote:
>In article <3toq7e$qb2@steel.interlog.com>,
>Herb Sutter <herbs@interlog.com> wrote:
>>>>However, consider:
>>>>
>>>> class A
>>>> {
>>>> public:
>>>> void do_f() { f(dynamic_cast<current_class*>(this)); }
>>>> };
>[a question about how to implement it removed]
>>What, specifically? If we treat current_class as an implicit parameterised
>>type (template) that is specially supported to be always filled in with the
>>current type (which the compiler already knows about in all the cases where
>
>consider: (this is your example with definition of do_f moved to some other
> file).
>class X
>{
>public:
> void do_f();
>};
>
>// In some other file:
>void X::do_f()
>{
> f(dynamic_cast<current_class*>(this));
>}
>
>Now, how would you implement this?
Good point, but first let me repeat my intended definition: using
current_class is an implicitly parameterised type (template) always filled
with the current most-derived class. That said, when you write a template
that uses a parameterised class' member function (e.g., operator++) it must be
visible to all instantiated/derived classes; i.e., it must be in the header,
and not in a different xlation unit.
> c) generate new X::do_f() functions for each derived class.
Exactly, since using current_class is to use an implicit template.
> This would require a new virtual function table entry for
> do_f, otherwise the dynamic_cast would be equal to
> "dynamic_cast<X*>(this)", and the call would still go to
> wrong destination. That is, do_f would have to be virtual.
Good point. I had assumed this, but never stated it explicitly: any function
using current_class would be implicitly virtual, or we would require that only
virtual functions may use current_class.
> But even if it were virtual, only the linker could build the derived
> class's virtual function table, because the compiler doesn't
> know that X::do_f() has used current_class until it has seen
> the definition, which might be in different translation unit from
> all derived classes.
Aha, no! :-) If it could be in a different translation unit, that would break
everything... and clearly it has to be in the same translation unit, for
that's how templates work and to use current_class is to use a template (just
one that you happen to never need to fill in manually, that's the only
difference).
>[Note followups only to comp.std.c++]
Yes, good point; I suppose it's beyond the general-interest stage now.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Herb Sutter 2228 Urwin, Ste 102 voice (416) 618-0184
Connected Object Solutions Oakville ON Canada L6L 2T2 fax (905) 847-6019
Author: vandevod@avs.cs.rpi.edu (David Vandevoorde)
Date: 1995/07/08 Raw View
In article <3tmsck$gnv@steel.interlog.com>,
Herb Sutter <herbs@interlog.com> wrote:
>This is a copy of the public comment I've submitted regarding the proposed
>"current_class" keyword (a better name than the original "actual_class").
[...]
>EXAMPLE 1: IMPLEMENTING THE SINGLETON PATTERN
>
>The first example considers implementing a generic version of an
>example from Design Patterns: the Singleton pattern. It will
[...]
>Consider designing a class MySingle that should behave as a
>singleton; that is, we want it to have one (or a controlled set of)
>instance(s) with a global point of reference. To implement it
>today, we supply the proper private static _instance pointer and
>public static Instance() function manually:
[...]
> class Singleton
> {
> static Singleton* Instance()
> {
> if (!_instance)
> _instance = new Singleton;
> return _instance;
> }
>
> private:
> static Singleton* _instance; // omitting for now the issue
> // of how to even manage this
> };
>
> class MySingle : public Singleton
> {
> public:
> MyMethod (...);
> }; // not quite good enough
>
>The main problem is that this solution is not equivalent to writing
>our singleton class manually, for now applications can no longer
>call:
>
> MySingle::Instance()->MyMethod(); // error
Do it via a regular function template instead:
template<class C> inline
C* instance<C>() {
return static_cast<C*>(C::Instance());
}
so your above call becomes:
instance<MySingle>()->MyMethod();
If you don't like the (first) empty parentheses, you can obtain the same
effect with an "instance" helper class template and an appropriately
overloaded operator-> (may require a little more from the optimizer if
performance is critical).
>
>as they should because MySingle::Instance() returns a Singleton*,
>not a MySingle*. This solution requires the application to use
>RTTI on the returned pointer:
>
> (dynamic_cast<MySingle*>MySingle::Instance())->MyMethod()
Why require RTTI when you know the type anyway? (see also above)
>
>which is tedious and error-prone.
Not if you provide a function for it.
[...]
>EXAMPLE 2: MULTIPLE DISPATCH
[...]
>In article <DB9CtB.6Er@eunet.ch>,
> xar@pax.eunet.ch (Benjamin Rosenbaum) wrote:
>>Wouldn't it be nice if function overloading were
>>polymorphic by arguments, so that in...
>>
>> class A {
>> void do_f() {f(this);}
>> };
>> class B: public A {};
>>
>> void f(A &){cout << "this gets called...";};
>> void f(B &){cout << "...but wouldn't this be better?"};
Should be "A*" and "B*" I guess.
>>
>> main ()
>> {
>> B b;
>> b.do_f();
>> }
>>
>>....f(B &) would get called?
^^^^^^^^^^^^^^^^^^^^^^^^
Personally, I would find that very confusing.
>
>Again the template workaround could be applied, but again for the
>same reasons deriving more than one level deep would fail (i.e.,
>the workaround would work for this example, but fails in the
>general case, for example if we had a class C : public B).
>However, consider:
>
> class A
> {
> public:
> void do_f() { f(dynamic_cast<current_class*>(this)); }
> };
>
>Now this program works as desired, with the rest of the code
And how must a compiler/linker resolve that call?
I think that for this to work, we would have to add a significant burden
to compiler writers.
[...]
>SUMMARY
>
>For the price of a single keyword (one that consistently uses
Certainly, a "keyword" is only part of the "price to pay". One of the
many other aspects that matter is the translation complexity. If I
understand the proposed requirements well (which may very well not be
the case), this additional complexity would be considerable.
[...]
>such as the ProtectedObject in Example 4. Partial (and fragile)
>workarounds exist that work in some cases, but they fail even in
>normal use, forcing us to choose between mixins and polymorphism.
[...]
Although that can be true, I feel C++ often offers alternatives to
"mixins" which are more orthogonal to the inheritance concept.
>Herb Sutter 2228 Urwin, Ste 102 voice (416) 618-0184
>Connected Object Solutions Oakville ON Canada L6L 2T2 fax (905) 847-6019
Daveed
Author: herbs@interlog.com (Herb Sutter)
Date: 1995/07/08 Raw View
This is a copy of the public comment I've submitted regarding the proposed
"current_class" keyword (a better name than the original "actual_class").
I've left comp.object in the distribution list because the original
discussion was also repeated there, and because the contents affect
techniques for writing generic OO patterns.
Thanks again to all who participated in the "Proposal for Writing Generic
Patterns" thread, which proposed the new keyword. Your comments have made
this a more well-rounded proposal, particularly in demonstrating examples
requiring this new functionality.
Although this proposal has already been submitted, I'd be interested in
additional comments since I intend to continue supporting this proposal
through the standards process as I am able.
Herb
===================================================================
Draft Standard Comment:
The Case For a current_class Keyword
Submitted by: Herb Sutter, Connected Object Solutions
email: herbs@interlog.com
Friday, July 7, 1995
2228 Urwin, Suite 102, Oakville ON Canada, L6L 2T2
Tel.: (416) 618-0184 Fax: (905) 847-6019
===================================================================
-------------------------------------------------------------------
ABSTRACT
Allowing base classes to know the exact type of the current most-
derived class in which they are being used is a great advantage at
low cost, because it is both:
a) essential for writing entire classes of mixin designs,
particularly generic patterns; and
b) implementable with one keyword without impacting the existing
language rules.
I will describe the motivation for a proposed current_class
keyword, whose equivalent functionality already exists in other
languages (e.g., Eiffel's like current, Sather's SAME). I will
then demonstrate current_class's usefulness by implementing several
mixin/pattern designs from current literature (e.g., Gamma et al.'s
"Design Patterns"), showing, through failed workaround attempts,
that the same results cannot be obtained without current_class
information.
To address the questions in Section 6.4.1 of "Design and Evolution"
in a nutshell: The proposed change will be shown to be widely
applicable (the entire industry is getting into patterns and
generic patterns now), general-purpose (applicable to more than
just pattern mixins, see Example 4), implementable on all
reasonable hardware and systems, essential for certain pattern
mixin programming styles, specifically beneficial for the
design/implementation/use of libraries, free of side effects, and
not preventing any existing programming styles or breaking any
existing programs. The proposed change does not affect the
efficiency of code that does not use it, acts as a specialised
compiler-supported template (and adds a vtable) to classes which do
use it, requires no recompilation of existing programs, and does
not affect external linkage. The change is a single keyword which
has already proven easy to grasp in Usenet discussions, and is
unlikely to lead to demands for further extensions because it is a
simple self-contained concept.
-------------------------------------------------------------------
SUGGESTED FEATURE: DESCRIPTION
To implement transparent pattern mixins, we need a mechanism for a
base class to know the type of (though not being able to declare a
member variable of) the current derived class. I propose that this
take the form of an current_class keyword. For example, consider:
class A {
current_class* Function() { return
dynamic_cast<current_class*>(this); };
};
class B : public A {...}; // a B's Function() returns a B*
class C : public B {...}; // a C's Function() returns a C*
This example illustrates three main advantages that I will
illustrate further:
a) current_class allows generic reuse of a function (e.g.,
A::Function()) in derived classes without requiring redundant
redefinitions to supply correct current types;
b) together with the existing dynamic_cast, it allows a base class
to know and use the true this pointer for the current object
(via dynamic_cast<current_class*>(this));
c) it also allows further polymorphic dispatch of a base member
function's return value (e.g., the result of Function()).
The next four examples will illustrate that this is not possible
with current methods, despite attempts at workarounds using the
"class D : public B<D>" idiom, which fails in each case. What will
be illustrated is that, in essence, using current_class is
implicitly parameterising on a type (in this case, that parameter
is always filled in with the current class), and therefore current
template rules continue to work properly and as expected when
applied to classes invoking current_class.
-------------------------------------------------------------------
EXAMPLE 1: IMPLEMENTING THE SINGLETON PATTERN
The first example considers implementing a generic version of an
example from Design Patterns: the Singleton pattern. It will
illustrate the key points, including current (and failed)
workaround attempts. (Because this pattern is so simple, some have
questioned why one would put such a simple design into its own
generic pattern; but this example is for illustration and the same
issues arise with patterns supplying more complex behaviour.)
Consider designing a class MySingle that should behave as a
singleton; that is, we want it to have one (or a controlled set of)
instance(s) with a global point of reference. To implement it
today, we supply the proper private static _instance pointer and
public static Instance() function manually:
class MySingle
{
public:
static MySingle* Instance()
{
if (!_instance)
_instance = new MySingle;
return _instance;
}
private:
static MySingle* _instance;
// ...plus MySingle-specific attributes and behaviour, for example:
//
public:
MyMethod (...);
}
MySingle* MySingle::_instance = 0;
However, this has a serious drawback: We have to respecify and
reimplement the pattern member variable and function for every
class we want to have behave as a Singleton, which prevents generic
reuse. Instead, we would like to generalise the Singleton pattern
into a class or template that we can just inherit from or
instantiate, like:
class MySingle : public Singleton // if only we could do this!
{
public:
MyMethod (...);
}; // complete definition, equivalent in every way to the above
The problem is that we can't write such a generic Singleton pattern
under the draft standard. A naive attempt would be to duplicate
the core Singleton in its own class (identically to the above):
class Singleton
{
static Singleton* Instance()
{
if (!_instance)
_instance = new Singleton;
return _instance;
}
private:
static Singleton* _instance; // omitting for now the issue
// of how to even manage this
};
class MySingle : public Singleton
{
public:
MyMethod (...);
}; // not quite good enough
The main problem is that this solution is not equivalent to writing
our singleton class manually, for now applications can no longer
call:
MySingle::Instance()->MyMethod(); // error
as they should because MySingle::Instance() returns a Singleton*,
not a MySingle*. This solution requires the application to use
RTTI on the returned pointer:
(dynamic_cast<MySingle*>MySingle::Instance())->MyMethod()
which is tedious and error-prone. Or, alternatively we could foist
off the work onto the MySingle programmer by making
Singleton::Instance() pure virtual, forcing (and relying on) him to
override it in MySingle and all its derivatives; yet even then the
function will be identical in every class except for its return
type and it is wasteful (and dangerous) to try to force the
programmer to repeat it even with a macro. And, even if it was
workable, it would still not address the issue of proper
transparent reuse.
The next obvious question is, What if we used templates?
template <class T>
class Singleton
{
static T* Instance()
{
if (!_instance)
_instance = new T;
return _instance;
}
private:
static T* _instance;
};
template<class T>
T* Singleton<T>::_instance = 0;
Can we derive what we want? Yes, as long as we always inherit
directly from the mixin:
class MySingle : public Singleton<MySingle>
{
public:
MyMethod (...);
}; // better, but must derive from Singleton<> directly
This works properly as long as we don't try to inherit further from
MySingle, in which case it fails miserably because further-derived
classes will have as their ancestor Singleton<MySingle>, not
Singleton<WhatIReallyAm>. In general, consider:
template<class T> class Pattern { /*...uses actual type T...*/ };
class FirstClass : public Pattern<FirstClass> { /*...so far so good...*/ };
class OtherClass : public FirstClass { /*...oops...*/ };
With a Pattern implemented as above to imitate the current_class
information, only immediately derived classes behave properly.
OtherClass now is derived from a Pattern instantiated with MyClass,
which is wrong (note this problem would not appear if Pattern had
real current_class information). To fix this, you need to resort
to a parallel class hierarchy like:
Pattern FirstClassProto
| \ / |
| \ / |
| FirstClass OtherClassProto
\ /
\ /
OtherClass
The fatal problem here is not that this workaround doubles the size
of the class hierarchy or even that it's error-prone; the fatal
problem is that it destroys polymorphism between FirstClass and
OtherClass and so forces a very inelegant tradeoff: We may either
have polymorphism or mix in generic patterns, but we may not do
both. This restriction seems unreasonable, especially when
current_class solves the whole problem simply and elegantly:
class Singleton
{
static current_class* Instance()
{
if (!_instance)
_instance = new current_class;
return _instance;
}
private:
static current_class* _instance;
};
current_class* Singleton::_instance = 0; // see below
Now we can go ahead and cleanly mix in our generic pattern:
class MySingle : public Singleton
public:
MyMethod (...);
}; // ...and we're done, cleanly and elegantly.
Now, getting back to that static variable: Can we deal with it
easily? Yes, simply by remembering that the use of current_class
can be viewed as an implicit type parameterisation; in other words,
as a specially supported implicit template. Since clearly each
class should have its own static _instance member (since the type
will differ), each class should indeed get its own copy. For
example:
class B { public: static current_class* _p; /*...*/ };
current_class* B:_p = 0;
/* generates B* B::_p = 0; */
class D1 : public B { /*...*/ };
/* transparently generates D1* D1::_p = 0; as would a template */
class D2 : public D1 { /*...*/ };
/* transparently generates D2* D2::_p = 0; as would a template */
-------------------------------------------------------------------
EXAMPLE 2: MULTIPLE DISPATCH
Here is an actual recent Usenet question that can be satisfied only
with current_class information, and it illustrates another key
advantage:
In article <DB9CtB.6Er@eunet.ch>,
xar@pax.eunet.ch (Benjamin Rosenbaum) wrote:
>Wouldn't it be nice if function overloading were
>polymorphic by arguments, so that in...
>
> class A {
> void do_f() {f(this);}
> };
> class B: public A {};
>
> void f(A &){cout << "this gets called...";};
> void f(B &){cout << "...but wouldn't this be better?"};
>
> main ()
> {
> B b;
> b.do_f();
> }
>
>....f(B &) would get called?
Again the template workaround could be applied, but again for the
same reasons deriving more than one level deep would fail (i.e.,
the workaround would work for this example, but fails in the
general case, for example if we had a class C : public B).
However, consider:
class A
{
public:
void do_f() { f(dynamic_cast<current_class*>(this)); }
};
Now this program works as desired, with the rest of the code
unchanged. This demonstrates that the current_class facility
allows not only easy multiple dispatch, but in general allows
proper polymorphism on the return type of a mixed-in member
function, which is a very powerful tool in many situations.
In the same article came the following example:
-------------------------------------------------------------------
EXAMPLE 3: IMPLEMENTING THE VISITOR PATTERN
>I want to implement the Visitor pattern from
>Gamma et.al.'s Design Patterns, as shown below
>(it's for double-dispatch-in-C++.) Without the
>macro, you have to make sure to type the same
>damn thing in every subclass of A, since
>the only thing we want to vary is the scope
>of "this"! Is there any way to do it with
>templates? Even better, is there a way to really
>make sure it gets done in every subclass of A?
>(I guess it could be pure virtual in A).
<example using macro to redefine accept() member function in
every subclass elided>
Using the template workaround and the parallel-hierarchy structure,
we get (the following is a modified version of the poster's
original code):
class Visitor
{
public:
void visit (A &) {cout << "Visited A" << endl;}
void visit (B &) {cout << "Visited B" << endl;}
};
template<class T>
class Visitee
{
public:
virtual void accept (Visitor &v) {
v.visit(dynamic_cast<T&>(*this)); };
};
class A_impl {};
class A : public A_impl, public Visitee<A> {};
class B_impl : public A_impl {}; // keep polymorphism in ?_impl classes
// for whatever that's worth
class B: public B_impl, public Visitee<B> {};
main()
{
A a;
B b;
Visitor v;
a.accept(v); // should call v.visit(A&)
b.accept(v); // should call v.visit(B&)
}
Again, the problem is that to get mixins we are forced to give up
polymorphism (here between A and B). But with current_class we can
define a proper Visitee mixin that works properly in all cases:
class Visitee
{
public:
virtual void accept (Visitor &v)
{ v.visit(dynamic_cast<current_class&>(*this)); };
};
class A: public Visitee {};
class B: public A {}; // inherits the mixin naturally, can't be done today
// note, no dual class hierarchy, simple/elegant design
This single change eliminates the need for the double class
hierarchy, simplifies the design, and allows not only polymorphic
multiple dispatch but also the writing of an easily reusable
generic pattern.
-------------------------------------------------------------------
EXAMPLE 4: A ProtectedObject MIXIN
Consider a mixin class, called ProtectedObject, which supplies
behaviour for storing a checksum of an object's physical bits so
that the object can detect unauthorised modifications (e.g.,
through rogue pointers elsewhere in the program, or through cosmic-
ray strikes in software controlling a comm satellite, etc.). To
protect an object, we would like to simply include ProtectedObject
in its class' inheritance list to mix in the behaviour; then all we
should need to do is call the generic SetCheck function at the end
of each of our own functions that can change the internal state,
and call the generic Check() function at the start of each of our
own functions to ensure no tampering has taken place.
This cannot be done in a generic base class without letting it
somehow: a) get the correct this pointer for the current object;
and b) determine the size of the object. With current_class
information, we can create such a generic mixin:
class ProtectedObject
{
public:
bool Check() { return (_check == CalcCheck()); };
void SetCheck() { _check = CalcCheck(); };
protected:
int CalcCheck()
{ /* calculate and return a checksum based on bytes in memory from
dynamic_cast<current_class*>(this) up to
dynamic_cast<current_class*>(this) + sizeof(current_class) - 1,
correcting for the checksum's own value as needed */
};
int _check;
};
This kind of mixin is not possible under the current draft for the
same reasons as in the other examples: Derived class information
cannot be brought into the base class without using a special
template as a workaround, which workaround itself breaks
polymorphism (among other drawbacks).
-------------------------------------------------------------------
QUESTIONS & ANSWERS
Q1 (from Usenet):
>I guess this is inspired by Eiffel's "like current" feature?
A1: No, as I don't know Eiffel, though I guess this means I'm not
the first to see this need. Matt Kennell mentioned the same thing,
and that similar functionality exists in Sather -- clearly, then,
this seems to be a useful construct to have if it is indeed already
present in other well-known OO languages.
Q2 (from Usenet):
>Should the following be type-correct?
>
> class A {
> current_class* Function() { return new A; };
> };
A2: No, for the new keyword doesn't change existing rules.
Specifically, since there's no implicit conversion from a base
pointer to a derived pointer, this would only be right when A is
concrete and is itself the current class (therefore the above
definition, while legal, would simply prevent derivation from A).
Again, remember that using current_class is implicitly
parameterising on a type, and therefore the template and typing
rules still work fine.
Q3: Can I declare a member variable of type current_class? For
example:
class Pattern
{
private:
current_class myVariable; // can cause size problems if allowed
};
A3: No. I propose that this should be illegal, because: a) it
would mean the base class portion would actually change size in
each derived class, wreaking havoc with existing rules; and b) if a
class does need to create/use an instance of current_class, it can
already do so (by storing a pointer to it and allocating it on the
heap, as does Singleton in Example 1 above). In short, base
classes may use and store pointers or references to objects of type
current_class, but may not store member variables of the type
itself.
-------------------------------------------------------------------
SUMMARY
For the price of a single keyword (one that consistently uses
existing template and other rules), we gain an enormous advantage
in writing reusable code: full generic patterns. However, the use
is not limited to patterns only; it is useful in many cases where
we would like to transparently mix in behaviour from base classes,
such as the ProtectedObject in Example 4. Partial (and fragile)
workarounds exist that work in some cases, but they fail even in
normal use, forcing us to choose between mixins and polymorphism.
Please consider this proposal. I am personally convinced of its
merits -- indeed, of its necessity -- and I hope I have been able to
clearly present some of the reasons why this is a simple, powerful,
and essential language feature for a popular and growing style of
generic programming.
Regards, and thanks for all the hard work you've all already put
into this standard on our behalf,
Herb Sutter
herbs@interlog.com
Connected Object Solutions
2228 Urwin, Suite 102 Oakville ON Canada L6L 2T2
Tel.: (416) 618-0184 Fax: (905) 847-6019
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Herb Sutter 2228 Urwin, Ste 102 voice (416) 618-0184
Connected Object Solutions Oakville ON Canada L6L 2T2 fax (905) 847-6019