jamiller@hpcupt1.cup.hp.com (Jim Miller) (10/31/90)
I'd like suggest a modification for C++:
Make "." and "->" equivalent.
"a.b", if "a" is a pointer, becomes "(*a).b".
"x->y", if "x" is a structrue/class, becomes "x.y" --
unless "->" has been overloaded for x.
Any problems?
Commentary:
A while ago, due to a design error, I had to change a pointer to a
structure to a local structure, and similarly a local structure to a
pointer to a structure. The compiler told me in each case that the
"." or "->" was wrong.
Good, but if it can tell me that a.b is an error because a is a pointer,
why can't it turn it into "a->b" or "(*a).b)"?
Similarly, it tells me that x->y is an error because x is not a pointer
but a structure, why can't it turn the "x->y" into a "x.b"?
From my point of view, what I'm doing is getting an element out of
a structure, and having to decide if the variable is the structure or
a pointer to a structure is a silly thing for me to have to know,
especially since the compiler knows explicitly, since I told it so
earlier. No ambiguity.
In the case of an overloaded "x->", "x->y" would not become "x.y" if
"x" was a structure instead of a pointer.
Responses? Problems? Opinions?
jim miller
jamiller@hpmpeb7.cup.hp.com
(a.k.a James A. Miller; Jim the JAM; stupid; @!?$$!; ... )
Anything I say will be used against me ...
But my company doesn't know or approve or condone anything of mine here.cowan@marob.masa.com (John Cowan) (11/01/90)
In article <52390002@hpcupt1.cup.hp.com>, jamiller@hpcupt1.cup.hp.com (Jim Miller) writes: > >I'd like suggest a modification for C++: > Make "." and "->" equivalent. I would strongly support the above proposal with one change. Currently, -> can be overloaded in classes but "." cannot. So leave -> alone, and make "." an operator that accepts either a class/struct or a pointer to one, with automatic dereferencing of the pointer. This has no impact on existing code, and does not change the compiler much either -- instead of reporting an error, it just installs a dereference coercion. It seems to me that C would have supported this feature from day one, but for the weak type-checking of the C compiler, where the left argument to -> could be anything whatever, even an int (interpreted as a raw machine address), and the right argument could be any structure tag, even one belonging to a different structure. This misfeature has long been removed from all sane compilers, and is explicitly forbidden by the ANSI C standard. Implicit dereferencing does exist elsewhere in the C language: a pointer to function is implicitly dereferenced when an attempt is made to call using it, so that (*pfi)() and pfi() mean the same thing, where pfi is of type "pointer to function returning int". Also, though I hate to mention the P-word around here, in Mesa (Xerox's extended dialect of Pascal) this overloading of "." is also done. (Standard Pascal has no analogue of -> but requires explicit dereferencing with ^, Pascal's version of unary *.) Would a knowledgeable person tell me by e-mail how to get this idea before the ANSI committee? I know the answer is "send in a recommendation"; what I want is address, format of request, etc. -- cowan@marob.masa.com (aka ...!hombre!marob!cowan) e'osai ko sarji la lojban
jimad@microsoft.UUCP (Jim ADCOCK) (11/06/90)
In article <27304544.3658@marob.masa.com> cowan@marob.masa.com (John Cowan) writes: [regards overloadable operator dot] >Would a knowledgeable person tell me by e-mail how to get this idea before >the ANSI committee? I know the answer is "send in a recommendation"; >what I want is address, format of request, etc. In theory, what one is suppose to do is send the request to Lenkov or Miller. I have already done so, in regards to overloadable operator dot. [ I'll post a plain-text copy of the request here. ] I say "in theory" because I have never received any acknowledgement from either, either in regards to my overloaded operator dot submission, nor in response for request for [non-voting] membership information. [ What Gives, Guys ??? ] Please join me in lobbying for overloadable operator dot. The people who I've sent copies of the operator dot request to, who are suppose to be responsible are: Dmitry Lenkov HP California Language Lab 19447 Pruneridge Avenue MS 47 LE Cupertino CA 95014 William M. Miller Glockenspiel Ltd. PO Box 366 Sudbury, MA 01776-0003
jimad@microsoft.UUCP (Jim ADCOCK) (11/06/90)
Below find a copy of the request for consideration that I sent to Lenkov and Miller. Like I said, I've received no form of acknowledgement, so I can't say where this is at right now. ---- Dmitry Lenkov HP California Language Lab 19447 Pruneridge Avenue MS 47 LE Cupertino CA 95014 William M. Miller GLockenspiel Ltd. PO Box 366 SudBury, MA 01776-0003 Oct 8, 1990 Request for Consideration: Overloadable Unary operator.() I humbly request the C++ standardization committee consider allowing overloadable operator.(), operator to work analogous to overloaded operator->(). Discussion as follows: With few exceptions, C++ allows all its operators be overloaded. The few exceptions are: . [dot,] .* [dot star,] :: [colon colon,] and ?: [binary selection.] The commentary on page 330 ARM, gives the following "explanation" : The reason for disallowing the overloading of ., .*, and :: is that they already have a predefined meaning for objects of any class as their first operand. Overloading of ?: simply didn't seem worthwhile. I agree I can't see any worthwhile reason for overloading ?:, but the reason given for disallowing the other operators cannot hold, because there is already a counterexample: unary operator& already had a predefined meaning, yet it is overloadable. Three questions to be answered in considering operator.() as a candidate for overloading are: 1) would doing so cause any great problem? 2) are there any compelling reasons to allow it? 3) what overloadings of operator.() should be permitted? I claim these questions can be easily answered as follows: 1) allowing operator.() to be overloaded causes no great problems. 2) there are compelling reasons to allow it and 3) unary operator overloading analogous to what is permitted of unary operator->() should be permitted. IE unary member overloaded operator.(), which can be called with an object or reference of the class it is defined in, or derived class, on the left hand side, returning a reference or object of a class to which . can be applied again. Discussion of these claims: "Allowing operator.() to be overloaded causes no great problems." Unary operator& demonstrates that there is no problem overloading a function for which there is already a pre-defined meaning. The implementation of operator.() in other respects is similar to operator- >() which also has been successfully implemented by several compilers, demonstrating that no new technology is required to implement unary operator.(). Like operator&, and operator->(), operator.() is never invoked except on a lhs object of a class explicitly overloading operator.(), thus no existing code can be affected by this change. Some people have expressed concern that if operator.() is overloadable, then how does one specify member selection of that class members themselves necessary to implement operator.() ? In practice, this does not prove to be a problem. Operator.() is invoked only in situations where . [dot] is explicitly used, and when writing smart reference classes, proxies, and other simple classes one typically accesses members via "implied this->", thus one doesn't use . [dot]. In situations where one would normally use . [dot], such as when a class instance is passed as a parameter to a member function, getting an overloaded operator.() can be sidestepped via pointer syntax: use (&ob)->member, rather than ob.member. People next complain that this means it will be difficult to simultaneously overload operator->() and operator.() to which I reply: Thank God! We don't need classes that try to act simultaneously as pointers and references! That's the whole point of allowing operator.() to be overloaded: so that objects that act like pointers can use pointer syntax, and objects that act like references can follow reference syntax! "There are compelling reasons to allow it" Overloading operator.() is necessary in practice to allow "smart reference" classes similar to the "smart pointer" classes permitted by overloading operator- >(). Overloading operator->() to access objects following reference semantics is pretty workable: RefCntPtr pob; pob = pobOther; pob->DoThis(); pob->DoThat(); However, if the object needs to follow value semantics, then this solution becomes onerous: RefCntHugeIntPtr pA, pB, pC; //.... *pC = *pA + *pB; int digit104 = (*pC)[104]; pC->truncateNdigits(100); What you really want to be able to do for objects that require value semantics is create smart references as follows: RefCntHugeIntRef a, b, c; /.... c = a + b int digit104 = c[104]; c.truncateNDigits(100); Another common case where you'd rather have overloaded operator.() rather than operator->() is in creating proxy classes. The proxy class just forwards messages to its destination classes. A proxy class can be used in many ways. An example is a proxy member, allowing a runtime decision of the actual implementation of that member. Or a class can even inherit from a proxy, allowing the behavior of its parent be specified at run. [Behavior, but not protocol, that is] Of course, pointer syntax can be consistently used for these proxy cases, but the underlying implication is that object instances are being created dynamically on the heap, not statically, nor on the stack. Note, that at a relatively high leve of pain, classes obeying reference semantics can already be created: One simply writes a class that contains a pointer that can be assigned to the forwarding object, and write an inline forwarder function for each and every member function in the protocol: class FooProxy { foo* pfoo; public: FooProxy(foo* pfooT) : pfoo(pfooT) {} void DoThis() { pfoo -> DoThis(); } void DoThat(int i) { pfoo -> DoThat(i); } int ReturnInt() { pfoo -> ReturnInt(); } // .... void NthMemberOfProtocol() {pfoo -> NthMemberOfProtocol();} }; Needless to say, when most class writers are faced with the prospect of manually writing a forwarding function for each and every function in a protocol, they don't! They punt instead, and overload operator->(), even when the rest of their class follows value semantics. Thus, class users end up having to guess whether to use . or -> as the member selector in every context. If operator.() is overloadable as well as operator->(), then customers can learn the simple convention: "Use . whenever dealing with object obeying value semantics, use -> whenever dealing with objects obeying reference semantics." In short, lacking operator.(), class writers are forced to violate convention meaning of operator->(). Instead, we should enable a complete set of overloadable operators, so that class writers can maintain historical meanings and usages of these operators. I therefore ask due consideration be given to allowing operator.() to be overloaded analogous to operator->(). I suspect that the committee should then consider also whether operator.*() be overloadable analogous to operator->*(). However, I am not asking for that, since I do not consider myself sufficiently experienced with member pointers to be aware of the ramifications. Let someone else propose the necessary changes for operator.*(), if they so choose. The necessary changes to the Annotated Reference Manual to support operator.() are listed below. I list the changes necessary in ARM, rather than the product reference manual, since the changes necessary to ARM are a pure superset of the changes necessary to the product reference manual. Jim Adcock, Oct. 8, 1990 _______________________ Section 7.2.1c Compiler vendors would need to add a convention for encoding operator. [dot.] But this is not an issue for the standardization effort. Section 12.3c Add the following table entry: . [operator dot] | yes | yes | yes | member | no Chapter 13, page 307, line 6, change to: and unary class member accessors -> and . [dot]) when at least one operand is a class object. Page 330: operator: one of .... -- add . [dot] to the list The following operators cannot be overloaded: .... remove . [dot] from the list. The reason for disallowing the overloading of ., .*, and :: is that they already have a predefined meaning for objects of any class as their first operand. Overloading of ?: simply didn't seem worthwhile. Change To: The reason for disallowing the overloading of :: and ?: is that it simply didn't seem worthwhile. Section 13.4.6 Add the following text: Class member access using . [dot] primary-expression . primary-expression is considered a unary operator. An expression x.m is interpreted as (x.operator.()).m for a class object x. It follows that operator.() must return either a reference to a class or an object of or a reference to a class for which operator.() is defined. operator.() must be a nonstatic member function. ___________________________ Commentary: Note that Ellis and Stroustrup's annotated notes on page 337 could have been just as well written as follows: Consider creating classes of object intended to behave like what one might call "smart references" -- references that do some additional work, like updating a use counter on each access through them. struct Y { int m; }; class Yref { Y* p; // information public: Yref(const char* arg); Y& operator.(); }; Yref::Yref(const char* arg) { p = 0; // store away information // based on 'arg' } Y& Yref::operator.() { if (p) { // check p // update information } else { // initialize p using information } return *p; } Class Yref's . [dot] operator could be used as follows: void f(Yref y, Yref& yr, Yref* yp) { int i = y.m; // y.operator.().m i = yr.m; // yr.operator.().m i = yp.m; // error: Yref does not have a member m } Class member access is a unary operator. An operator.() must return something that can be used as an object or reference. Note that there is nothing special about the binary operator .* [dot star.] The rules in this section apply only to . [dot]. End Commentary. ___________________________ Thank you for your consideration, and please inform me of your decisions. James L. Adcock Microsoft One Microsoft Way Redmond, WA 98052
jimad@microsoft.UUCP (Jim ADCOCK) (11/07/90)
In article <58813@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: > >I say "in theory" because I have never received any acknowledgement from >either, either in regards to my overloaded operator dot submission, nor >in response for request for [non-voting] membership information. Okay, I received a pile of documents today, and my name is on the membership list, so I guess they acknowledge my existence :-) Still don't know anything about the status of operator dot, though.
db@tc.fluke.COM (Dan Banay) (11/12/90)
In article <27304544.3658@marob.masa.com>, cowan@marob.masa.com (John Cowan) writes: > In article <52390002@hpcupt1.cup.hp.com>, > jamiller@hpcupt1.cup.hp.com (Jim Miller) writes: > > > >I'd like suggest a modification for C++: > > Make "." and "->" equivalent. > > I would strongly support the above proposal with one change. Currently, > -> can be overloaded in classes but "." cannot. So leave -> alone, and > make "." an operator that accepts either a class/struct or a pointer to > one, with automatic dereferencing of the pointer. This has no impact > on existing code, and does not change the compiler much either -- instead > of reporting an error, it just installs a dereference coercion. > I also support the proposed extension -- I can't see any problems it would cause. > It seems to me that C would have supported this feature from day one, but for > the weak type-checking of the C compiler, where the left argument to -> could be > anything whatever, even an int (interpreted as a raw machine address), and > the right argument could be any structure tag, even one belonging to a different > structure. This misfeature has long been removed from all sane compilers, > and is explicitly forbidden by the ANSI C standard. One of the (ab)uses of the -> operator (combined with a well-known anachronism of C), was to determine the offset of a member within a structure. Case in point: struct blah { int a; double d; char c; float e; }; main() { printf("The offset of c is: %d\n",&0->c); } A good number of compilers still accept this -- although most will display some sort of warning. > > Also, though I hate to mention the P-word around here, > in Mesa (Xerox's extended dialect of Pascal) this overloading of "." > is also done. (Standard Pascal has no analogue of -> but requires explicit > dereferencing with ^, Pascal's version of unary *.) The programming language Oberon (Wirth's successor to Modula-2) also does this overloading. > > Would a knowledgeable person tell me by e-mail how to get this idea before > the ANSI committee? I know the answer is "send in a recommendation"; > what I want is address, format of request, etc. ditto. -- +------------------------------------------------------------------------+ | Dan Banay | | db@tc.fluke.COM {microsoft,sun,uw-beaver}!fluke!db +1 206 356 6285 | +------------------------------------------------------------------------+ | John Fluke Mfg. Co., MS 223B, PO Box 9090, Everett, WA 98206-9090 USA | +------------------------------------------------------------------------+
steve@taumet.com (Stephen Clamage) (11/18/90)
db@tc.fluke.COM (Dan Banay) writes: >In article <27304544.3658@marob.masa.com>, cowan@marob.masa.com (John Cowan) writes: >> Would a knowledgeable person tell me by e-mail how to get this idea before >> the ANSI committee? I know the answer is "send in a recommendation"; >> what I want is address, format of request, etc. >ditto. The ANSI C++ committee (X3J16) is already considering proposals to allow overloading of '.' (dot). So far as I know, there is no pending proposal to make '.' and '->' equivalent. One of the committee members has been tasked to promulgate a procedure to the public for making proposals to the committee. Watch comp.lang.c++ and comp.std.c++ for more information. The committee met this past week (12-16 November), and will meet again next in March 1991. -- Steve Clamage, TauMetric Corp, steve@taumet.com