[comp.std.c++] "->" == "." proposal

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