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
<[email protected]> 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.