Possible @property compromise

Steven Schveighoffer schveiguy at yahoo.com
Fri Feb 1 07:29:39 PST 2013


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


More information about the Digitalmars-d mailing list