Topic: ->" == "." proposal
Author: steve@taumet.com (Stephen Clamage)
Date: 18 Nov 90 01:10:26 GMT Raw View
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
Author: jamiller@hpcupt1.cup.hp.com (Jim Miller)
Date: 30 Oct 90 18:15:22 GMT Raw View
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"?
Author: cowan@marob.masa.com (John Cowan)
Date: 1 Nov 90 15:54:43 GMT Raw View
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
Author: jimad@microsoft.UUCP (Jim ADCOCK)
Date: 5 Nov 90 19:32:32 GMT Raw View
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
Author: jimad@microsoft.UUCP (Jim ADCOCK)
Date: 5 Nov 90 19:53:32 GMT Raw View
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
Author: jimad@microsoft.UUCP (Jim ADCOCK)
Date: 6 Nov 90 19:25:40 GMT Raw View
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.
Author: db@tc.fluke.COM (Dan Banay)
Date: 12 Nov 90 15:52:47 GMT Raw View
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 |
+------------------------------------------------------------------------+