pointers, functions, and uniform call syntax

Carl Sturtivant sturtivant at gmail.com
Mon Sep 3 12:57:42 PDT 2012


On Monday, 3 September 2012 at 19:49:14 UTC, monarch_dodra wrote:
> On Monday, 3 September 2012 at 18:45:42 UTC, Jonathan M Davis 
> wrote:
>> On Monday, September 03, 2012 14:13:10 monarch_dodra wrote:
>>> I was playing around with a very big struct, and told myself I
>>> wanted it allocated on the heap. This meant I was now
>>> manipulating S* instead of an S.
>>> 
>>> I thought "this should have zero impact, because in D, because
>>> "." is supposed to deference for you if needed."
>>> 
>>> I find it strange though that when trying to call a function 
>>> with
>>> a pointer, the pointer isn't automatically dereferenced if 
>>> needed:
>>> 
>>> ----
>>> struct S
>>> {
>>>   void foo();
>>> }
>>> void bar(S);
>>> void main()
>>> {
>>>   auto r = new S;
>>>   r.foo();
>>>   bar(r);  //derp
>>>   r.bar(); //derp
>>> };
>>> ----
>>> I find it strange, because I thought the entire point was to
>>> abstract way something was allocated to the way it was used: 
>>> EG.
>>> From a caller perspective, I don't care if r is on the stack 
>>> or
>>> on the heap: I just want to call the method bar on the object 
>>> in
>>> question.
>>> 
>>> Why does one consider a "free standing" function more 
>>> ambiguous
>>> than a member function?
>>> 
>>> Things get even stranger if you mix in uniform call syntax.
>>> "r.foo()" works, but "r.bar()" doesn't?
>>> 
>>> Am I really forced into:
>>> ----
>>> struct S
>>> {
>>>   void foo();
>>> }
>>> void bar(S);
>>> void main()
>>> {
>>>   auto r = new S;
>>>   r.foo();
>>>   bar(*r);    //Groan
>>>   (*r).bar(); //Super Groan.
>>> };
>>> ----
>>> I feel as if I'm just back at square 1...
>>
>> All that UFCS does is make it so that if the first parameter 
>> of a function is a
>> given type, you can call that function on that type as if it 
>> were a member
>> function. It's purely syntactic convenience.
>>
>> void bar(S) {}
>>
>> takes an S, not as S*, so I wouldn't expect UFCS to work with 
>> it and an S*.
>>
>> . dereferences a pointer when accessing a member function or 
>> variable, because
>> it works quite nicely to have it work that way and generally 
>> negates the need
>> for a second operator (->). It's certainly _not_ true that the 
>> automatic
>> dereferencing with . allows you to forget that something is a 
>> pointer. An
>> operation which _could_ be on the pointer (e.g ==) will 
>> operate on the
>> pointer, forcing you to dereference it. It's just that adding 
>> -> on top of .
>> is unnecessary and complicates the language.
>>
>> The choice to have . automatically dereference pointers when 
>> access members
>> predates the idea of UFCS considerably, and I don't think that 
>> it was ever
>> really considered how they two would interact.  The automatic 
>> dereferencing of
>> a pointer doesn't really have anything to do with UFCS except 
>> for similarites
>> of syntax. It's simply that if a variable is a pointer, and it 
>> points to a
>> type which has a member with the same name as what's on the 
>> right-hand side of
>> the dot, then that member function gets called. And 
>> technically, it doesn't
>> even dereference the pointer, because the member function 
>> takes a pointer (as
>> the invisible this pointer). If there is no such member 
>> function, then free
>> functions are checked to see if they take the variable's type 
>> as their first
>> argument. If there's such a function with the right name and 
>> number of
>> arguments, then it's used. For there to be any dereferencing 
>> involved would
>> require special casing pointers, which doesn't currently 
>> happen.
>>
>> I think that the way that it currently works is completely 
>> consistent. It's a
>> perfectly valid enhancement request to want
>>
>> void func(S s, int i) {...}
>>
>> to be be callable with S*, given that normally function calls 
>> on an S* don't
>> require you to dereference anything. But it's not like what we 
>> have is broken.
>> It's just that there's a corner case which forces you to deal 
>> with pointers
>> specially (which isn't exactly new, because things like == and 
>> assignment
>> already require you to treat pointer specially). So, feel free 
>> to create an
>> enhancement request. You've found a corner case that I suspect 
>> was never fully
>> though through, and Walter may very well think that the change 
>> is worth
>> making.
>>
>> However, one thing to remember that complicates this a bit is 
>> that it's
>> perfectly possible to declare a function which is overloaded 
>> with one function
>> taking a pointer and one not.
>>
>> void func(S* s, int i) {...}
>> void func(S s, int i) {...}
>>
>> in which case, there's an ambiguity, and I would then expect 
>> UFCS to _not_
>> compile when using S*, or you'd risk function call hijacking. 
>> That's not
>> necessarily a big deal, but it _does_ complicate things a bit.
>>
>> - Jonathan M Davis
>
> TY for the reply, I'll consider asking for it.
>
> In the mean time, is there a way to access that variable with 
> value semantics? I mean, in the scope where my pointer was 
> declared, I *know* it is non null.
>
> In C++, I often did it with iterators:
> ----
> for(it ...)
> {
>     int& val = *it;
>     //now, we can access it with value semantics through val.
> }
> ----
>
> ?

Good question, I've proceeded in the same way in C++ many times. 
I hope there's a way, but I think it likely there is not, except 
by passing *it to a function with a reference parameter.




More information about the Digitalmars-d mailing list