Possible @property compromise

Zach the Mystic reachBUTMINUSTHISzach at gOOGLYmail.com
Fri Feb 1 07:54:14 PST 2013


On Friday, 1 February 2013 at 15:29:38 UTC, Steven Schveighoffer 
wrote:
> On Fri, 01 Feb 2013 01:52:29 -0500, Zach the Mystic 
> <reachBUTMINUSTHISzach at googlymail.com> wrote:
>
>> On Friday, 1 February 2013 at 04:33:05 UTC, Steven 
>> Schveighoffer wrote:
>>> No, the struct must have data.  If it doesn't, how does it 
>>> get back to the owner type?  In other words, your n struct 
>>> must have a pointer to the myStruct instance it intends to 
>>> modify _n on.
>>
>> How does any function get hooked up with data? The compiler 
>> figures out what data is to be passed to which function. It's 
>> no different from how the compiler figures out how to pass 
>> data defined in one module to functions defined in a different 
>> module. Empty structs are just namespaces with powerful 
>> semantics. They have no pointers, unless they're nested in a 
>> struct with data, in which case they have the same pointer as 
>> any member function of that struct.
>>
>> struct A
>> {
>>   int q = 23;
>>   void incrementQ() { ++q; }
>> }
>>
>> How on earth could this function increment q when it's not 
>> even defined in the function?!?!? It must be a miracle. Oh, no 
>> wait, it needs a pointer to the struct in question. Duh.
>
> No need to get snippy :)  Especially when you are wrong.
>
> Try this:
>
> struct A
> {
>    int q;
>    struct B
>    {
>       void foo() {q++;}
>    }
> }
>
> Won't compile.  That's becuase foo is passed a reference to the 
> A.B struct, not the A struct.
>
> If you want it to compile, B will *necessarily* have to have a 
> pointer to A.
>
> If you want B's methods to be passed A's pointer, then this is 
> not a struct.  Plain and simple.  It's just a namespace, the 
> "just like any other struct" is meaningless, since it isn't.
>
> Now, try this:
>
> struct S {}
>
> pragma(msg, sizeof(S).stringof); // outputs 1 (empty structs 
> must have some size).
>
> void foo()
> {
>   int q;
>   struct A
>   {
>      void foo() {q++;}
>   }
>
>   pragma(msg, sizeof(A).stringof); // outputs 4 (or 8 on 64-bit 
> machines)
>
> }
>
> Why?  Because the reason those "miracle" nested structs work is 
> because they have a hidden context pointer.
>
> Even empty structs have size of 1 byte because they must have a 
> 'this' pointer.
>
>>
>> There's no difference with data-less structs inside regular 
>> structs.
>>
>> struct A
>> {
>>   int q;
>>   incrementQ struct
>>   {
>>     void opCall() { ++q; }
>>   }
>> }
>>
>> Where's the need for some hocus-pocus mystery pointer here? 
>> The empty struct has no data to worry about. Functions inside 
>> the empty struct get the same damn pointer as the other 
>> functions in struct A.
>
> Then this is not a normal struct, in fact it has nothing to do 
> with a struct.
>
>> But of course, you can't do this:
>>
>> struct B
>> {
>>   int _q;
>>   q struct
>>   {
>>     int opUnary(string s)() if (s == "++")
>>     {
>>       writeln("You know, I just wanted to have a conversation 
>> while I was busy incrementing q");
>>       ++_q;
>>       writeln("I did all sorts of stuff with the increment 
>> operator while I was at it");
>>       return _q;
>>     }
>>   }
>> }
>>
>> ...with normal function calls.
>
> Certainly you can:
>
> struct B
> {
>    int _q;
>    @property auto q()
>    {
>       static struct incrementer
>      {
>        int *_q;
>        int opUnary(string s)() if (s == "++")
>        {
>          writeln("You know, I just wanted to have a 
> conversation while I was busy incrementing q");
>          ++(*_q);
>          writeln("I did all sorts of stuff with the increment 
> operator while I was at it");
>          return *_q;
>        }
>      }
>      return incrementer(&_q);
>    }
> }
>
> Although, because of dumb rvalue/lvalue rules (apparently, 
> incrementer is not an lvalue, so can't have ++ called on it), 
> this doesn't actually compile...
>
> Through some finagling, I can get this to compile, but it's not 
> usable with this compiler bug:
>
> import std.stdio;
>
> struct B
> {
>     int _q;
>
>     struct incrementer
>     {
>         int *_q;
>         int opUnary(string s)() if (s == "++")
>         {
>             writeln("You know, I just wanted to have a 
> conversation while I was busy incrementing q");
>             ++(*_q);
>             writeln("I did all sorts of stuff with the 
> increment operator while I was at it");
>             return *_q;
>         }
>     }
>
>     private incrementer qinc; // needed to make incrementer an 
> lvalue.
>
>     @property ref incrementer q()
>     {
>         qinc._q = &_q;
>         return qinc;
>     }
> }
>
> void main()
> {
>     B b;
>     assert(b._q == 0);
>     ++b.q; // b.q++ doesn't work, another bug
>     assert(b._q == 1);
> }
>
> But I think actually, if we are going to define get and set in 
> C# style, it would be useful to be able to define all the 
> operators.  So that part of the plan has merit.
>
> I think you need to drop the struct moniker.  This is not a 
> struct.  You do that, and I think your proposal is on more 
> solid ground.
>
>>
>>> Unless, of course, you pass the pointer to myStruct as the 
>>> 'this' reference.
>>
>> Ain't no "this" in an empty struct. Use "outer.this" instead.
>
> outer is the pointer I am referring to.  It's not magic, it 
> must come from somewhere.  If the struct has no this pointer, 
> it's not a struct.  A data-less struct *STILL* has a this 
> pointer.
>
>>
>>> But then, this isn't a normal struct, and
>>> I'm really failing to see why we have to make this a struct 
>>> at all.
>>
>> Because it's already implemented, except for a few details, 
>> because it opens up possibilities for properties other 
>> languages could only dream of, and because it obviates the 
>> need for tags like @property to provide far weaker 
>> functionality.
>
> Hand waving doesn't solve the problems.  The details are 
> important to resolve.
>
> What it seems like you have done is hijacked the 'struct' 
> keyword for a property.  It isn't a struct, and it doesn't 
> obviate the need for a tag.
>
>> You say it's complicated, but I ask you this: does any other 
>> proposal completely eliminate the so-called eye-sore 
>> "@property" while also providing functionality with unknown 
>> potential which goes far beyond what people are used to? And 
>> the owner pointer problem is only a problem if we want to make 
>> the language complete and consistent with regard to normal 
>> non-static structs holding an outer struct pointer. I think 
>> having a use case for this type of struct would seal the deal, 
>> but as it is, I'm not sure.
>
> What I mean by complicated is that it seems like a lot more 
> work than it should be.
>
> If we want to define new syntax to define properties, let's do 
> it in a way that is concise and to the point.  I don't want to 
> make properties look like structs, they are not structs.
>
> -Steve

Okay, so you're drawing the line where you think it ends.

I want to point out again that my idea requires the language 
change which allows struct-nested structs access to their 
parent's scope. The temporary fix is to make it an error to have 
a non-static struct-nested struct which actually holds data. All 
programmers can quickly see what's wrong with their code, because 
they will have to add "static" to data-containing struct-nested 
structs to get them to compile. As I said above, this is arguably 
the behavior which should have been implemented in the first 
place - I'm actually making the language *more* consistent by 
insisting on this.

And there is simply no need for a data-less struct to allow a 
"this" pointer. There will never be any need to know the address 
of a data-less struct. Any use of it will simply give: "Error: a 
struct with no data may not contain a 'this' pointer". And any 
use requiring taking its address is statically disallowed at 
compile time. This is not a hard feature to implement.



More information about the Digitalmars-d mailing list