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