C++ Logo

liaison

Advanced search

Re: [wg14/wg21 liaison] Allow '.' to behave like '->' where it is currently an error

From: J Decker <d3ck0r_at_[hidden]>
Date: Fri, 28 Feb 2020 19:25:21 -0800
On Fri, Feb 28, 2020 at 10:07 AM Patrice Roy via Liaison <
liaison_at_[hidden]> wrote:

> My initial point in response to your message was to point out that in C++,
> there are cases where making '.' behave as '->' does today breaks otherwise
> correct code and introduces ambiguity.
>
> C++ users, contrary to C# or Java users, have the option of using objects
> directly or indirectly. In C# (excluding structs) and Java, all objects are
> accessed through indirect means. On the other hand, for a C++ programmer
> using a smart pointer such as unique_ptr<T>, the ability to distinguish the
> services exposed by that object (using '.') and those exposed by the
> pointee (using '->') carries meaning, and that distinction is not readily
> accessible in some other languages this proposal refers to.
>
> Please also note that operator-> can be (and is sometimes) overloaded in
> C++ classes.
>


I'm failing to communicate 'where it is now an error'. The proposed change
really only affects things which right now generate a compiler error; maybe
demote to a warning, and do what the error message suggests and use -> ?
Then the warning can be disabled, or kept so you have compiler diagnostics
without implementing a linter into the build chain.

If the value is a (raw) pointer the first -> applies to dereference the
pointer, and '.' is an error,
if the value is an instance of a struct/class/union -> accesses the
operator and . accesses the members of the struct.
There is no occasion where '.' would also mean 'call operator()'; and
having analyzed this I should entirely rewrite comments about that.

The smart pointer types are instances of classes that represent pointers
that have properties, but '.' just accesses that instance. If there was
was a `unique_ptr<C> *pupC`? and in that case '.' could be used instead of
->... but the compiler doesn't have to consider the operator() yet because
it's still a pointer to a class.


--- test.cc ----

struct S {
   struct S *next;
   struct S **me;
   operator->()
};

struct S_with_functions {
   struct S_width_functions *next;
   struct S_width_functions **me;
   void unlink( void ) { me[0] = next; }
   S_with_functions() {
      /* structs are the same as classes, except default protection... */
      next = 0;
      me = &next;
   }
};

class C {
public:
   class C *next;
   class C **me;
   C() {
   }
};

void f( void ) {
   struct S c_S;
   S cpp_S;
   C cpp_C;
   c_S.me = &c_S.next;
   cpp_S.me = &cpp_S.next;
   cpp_C.me = &cpp_C.next;

   c_S.me[0]->next = &cpp_S; // the old way; legal
   c_S.me[0].next = cpp_S.me.next; // error
/*
# gcc test.cc
test.cc: In function 'void f()':
test.cc:35:12: error: request for member 'next' in '* c_S.S::me', which is
of pointer type 'S*' (maybe you meant to use '->' ?)
  c_S.me[0].next = cpp_S.me.next; // error
            ^~~~
test.cc:35:28: error: request for member 'next' in 'cpp_S.S::me', which is
of non-class type 'S**'
  c_S.me[0].next = cpp_S.me.next; // error
                            ^~~~
*/

   cpp_S.me[0]->next = &cpp_S; // the old way; legal
   cpp_S.me[0].next = c_S.me.next; // error
/*
test.cc:38:14: error: request for member 'next' in '* cpp_S.S::me', which
is of pointer type 'S*' (maybe you meant to use '->' ?)
  cpp_S.me[0].next = c_S.me.next; // error
              ^~~~
test.cc:38:28: error: request for member 'next' in 'c_S.S::me', which is of
non-class type 'S**'
  cpp_S.me[0].next = c_S.me.next; // error
                            ^~~~
*/
}
----
J
> I think it would at least be necessary to address these (important)
> questions in the proposal, should it become something more formal.
>
> Cheers!
>
> Le ven. 28 févr. 2020 à 11:41, J Decker via Liaison <
> liaison_at_[hidden]> a écrit :
>
>>
>>
>> On Fri, Feb 28, 2020 at 5:34 AM Bjarne Stroustrup <bjarne_at_[hidden]>
>> wrote:
>>
>>> How do you know that having . and -> roughly mean the same would be a
>>> benefit to the community at large?
>>>
>>> For example, people assume that -> implies reference semantic and
>>> potential sharing, whereas . implies value semantic (except for non-const
>>> reference parameters) and lack of sharing. That could be more significant
>>> than the convenience of having just a single operator.
>>>
>>> The burden of proof is on the proposer of a change.
>>>
>> It's hard to show how a change to C might play out; given 50 some years
>> of history it's hardly even worth bothering to change.
>> I'm understanding that it is less meaningful of a change to C++ other
>> than as a compatibility should C accept such a thing.
>> It's encouraged practice to use newer smart pointer types, and I have a
>> module for V8 which uses those all over, and certainly the use of '.' or
>> '->' means different things, and it's still enforced by the compiler to use
>> the right one.  I'm not entirely sure that there would even be much change
>> to code developed in C++.
>>
>> This is honestly a new idea only a few months old, as I was reflecting on
>> 'why IS `((Something*)ptr).` an error' ?  Why are there two operators that
>> aren't even interchangeable in context, and that there's only one choice to
>> use.  I have spent a few years with C#, and recently ES6+(not even
>> technically JS), and '.' seems easy enough to use, even when there's a
>> mixture of C# structs(instance value types) and classes (reference, always
>> referred by pointer), and it's not really a loss to inadvertently look in a
>> value type, but then, the debugger reports 'null exception on this part of
>> this expression' so you rarely even have to guess what it was.  I can't
>> really demonstrate what sort of effect this would have on development; or
>> even how it would change existing things.  (libz is still written with K&R
>> style function declarations).  Certainly everything that has worked would
>> still work.
>>
>> Certainly, having already assigned a sort of meaning to them back in the
>> 90's, I haven't ever really even questioned it until now.
>>
>> It's my hypothesis that at the time this was developed, data driven
>> programming (where the type of the data is [with] the data), and type
>> inspection was not a consideration, but more, that one type needs to be
>> another type ( float a = (float)213 ), and that C was more about coercion
>> than inspection, so since a pointer to a thing and a thing definitely
>> aren't the same, they have to be operated on differently; while conversely
>> it can be said just look at the type of the operand and do the right thing.
>>
>>> On 2/28/2020 7:57 AM, J Decker via Liaison wrote:
>>>
>>>
>>>
>>> On Thu, Feb 27, 2020 at 10:21 PM Uecker, Martin <
>>> Martin.Uecker_at_[hidden]> wrote:
>>>
>>>> Am Donnerstag, den 27.02.2020, 22:15 -0800 schrieb J Decker:
>>>> > On Thu, Feb 27, 2020 at 10:09 PM Uecker, Martin via Liaison <
>>>> > liaison_at_[hidden]> wrote:
>>>> >
>>>> > >
>>>> > > It is useful to have the information about
>>>> > > whether something is a pointer or not and the
>>>> > > difference between '.' and '->' makes it clear.
>>>> > >
>>>> >
>>>> >
>>>> https://gist.github.com/d3x0r/f496d0032476ed8b6f980f7ed31280da#the-meaning-of---and--
>>>> <https://urldefense.proofpoint.com/v2/url?u=https-3A__gist.github.com_d3x0r_f496d0032476ed8b6f980f7ed31280da-23the-2Dmeaning-2Dof-2D-2D-2Dand-2D-2D&d=DwMFaQ&c=u6LDEWzohnDQ01ySGnxMzg&r=-ypbxfA15hWRWp5mAlnCkA&m=r-p2sPYtgW9vx52cY3llBBeul0O7y8wIcs4dNMJTs-s&s=aS1qWqzUrPnjhstqWNjf-6xlWXnxaeOmcvoKpEEsvNQ&e=>
>>>> >
>>>> > it reduces the points you have to check, but it's really not all that
>>>> > useful when dealing with a variety of languages.. '.' just becomes
>>>> another
>>>> > place to check validity of the expression's values... So yes, it
>>>> increases
>>>> > the potential things to check... but then, nothing stylistically
>>>> prevents
>>>> > you from continuing to denote that.
>>>>
>>>> If it not enforced by the language, the information
>>>> is unreliable. This is even worse than not having it.
>>>>
>>>
>>> I know, this is still really just me... https://github.com/d3x0r/sack
>>> <https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_d3x0r_sack&d=DwMFaQ&c=u6LDEWzohnDQ01ySGnxMzg&r=-ypbxfA15hWRWp5mAlnCkA&m=r-p2sPYtgW9vx52cY3llBBeul0O7y8wIcs4dNMJTs-s&s=cj_DZuKqU0h8EMZ-cbdK1zFGeB5MXNE8gH2GRuwSDXg&e=> and
>>> some arbitrary source...
>>> https://github.com/d3x0r/SACK/blob/master/src/idlelib/idle.c
>>> <https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_d3x0r_SACK_blob_master_src_idlelib_idle.c&d=DwMFaQ&c=u6LDEWzohnDQ01ySGnxMzg&r=-ypbxfA15hWRWp5mAlnCkA&m=r-p2sPYtgW9vx52cY3llBBeul0O7y8wIcs4dNMJTs-s&s=NfvK6o7zRd6CS7kuhTho1ll5pWV7u8pgpWd9EJKALi8&e=>
>>>
>>> there's a flags structure that is just instanced in another struct
>>> check->flags.bDispatched = 1
>>>
>>> that's used a few times, all other accesses are `->`
>>>
>>> can you provide an example of where this information is useful?
>>>  Porting the above to c2x it wouldn't change how many things have to be
>>> looked at as suspect...
>>> OR what is the information that distinct operators provides rather than
>>> using '.' everywhere (except, I suppose in C++ where you want to access an
>>> operator override on a pointer type.. )
>>>
>>> class x {
>>>    /* operaror->() ... */
>>> };
>>> class x *px;
>>>
>>> px->x; (use operator?)
>>>
>>>
>>>
>>>> Best,
>>>> Martin
>>>
>>> _______________________________________________
>> Liaison mailing list
>> Liaison_at_[hidden]
>> Subscription: https://lists.isocpp.org/mailman/listinfo.cgi/liaison
>> Link to this post: http://lists.isocpp.org/liaison/2020/02/0054.php
>>
> _______________________________________________
> Liaison mailing list
> Liaison_at_[hidden]
> Subscription: https://lists.isocpp.org/mailman/listinfo.cgi/liaison
> Link to this post: http://lists.isocpp.org/liaison/2020/02/0055.php
>

Received on 2020-02-28 21:28:16