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