Re: Generic Property Implementation

2018-03-12 Thread Alex via Digitalmars-d-learn

On Monday, 12 March 2018 at 13:04:54 UTC, Simen Kjærås wrote:

On Monday, 12 March 2018 at 10:37:00 UTC, Alex wrote:

Sure, you have.
https://dlang.org/spec/struct.html#assign-overload
Point #4.
In this case,

ref S opAssign(ref S rhs)
{
return this;
}


True. Can you fix these, too?

struct S {
S* ptr;
this(int dummy) {
ptr = 
}
~this() {
assert(ptr == );
}
}

void test(S s) {}

unittest {
test(S(0));
}

S test2() {
return S(0);
}

unittest {
S s = test2();
}



ok... this is a little bit more tricky, as there is no assignment 
now. :)
But in this two cases I assume, you want to have explicit pass by 
reference, no?


struct S {

S* ptr;
this(int dummy) {
ptr = 
}

~this() {
assert(ptr == );
}
}

void test(ref S s){}

unittest {
auto s = S(0);
test(s);
/*
if I call test(S(0)) with void test(S s) as in your example,
neither the postblit nor opAssign is called... hm... no idea 
why...

*/
}

auto test2() {
return new S(0);
}

unittest {
auto s = test2();
/*
This example differs more from the post before :) test2 is a 
true factory now,

it has to grant the right setting of all members...
*/
}

Another point is, that I hope, that pointers don't move 
anywhere, as in C, by definition.


And why not? D allows for moving garbage collectors.


If it were allowed, then "contiguous memory allocation" for 
arrays would be senseless.





unittest {
auto tmp = typeof(S.a)();
}


I thought, this shouldn't be possible (at least in my mind)


It wouldn't, and such code is not possible in D today:

struct S {
int n;
auto a() { return SomeType!(() => n)(); }
}

struct SomeType(alias fn) {
int get() { return fn(); }
}


But this is clearly valid.


Yes, it's an example of code that works in D today, with 
similar semantics to those implied in the other example. It's 
meant to show that just like `auto tmp = typeof(S.a())()` 
doesn't work today, it shouldn't work with the types used in my 
other example.





unittest {
   // cannot access frame pointer of 
foo.S.a.SomeType!(delegate () => this.n).SomeType

   auto tmp = typeof(S.a())();
}

For sure, tmp cannot be defined without an instance of S. So 
the correct unittest in my eyes would be:

unittest {
   S s;
   auto res = s.a;
   assert(res.get == S.init.n);
}


I think we may be speaking past one another. Yes, your unittest 
would be expected to pass.


If we go back to the example code:

struct S1 {
int n, m;
SomeType!(() => n + m) a;
}

vs.

struct S2 {
int n, m;
auto a() { return SomeType!(() => n + m)(); }
}

I would expect typeof(S1.a) and typeof(S2.a()) to be 
equivalent. I don't see a good reason why I should be able to 
construct an instance of typeof(S1.a), since I can't construct 
a typeof(S2.a()).


I see your point that "The latter closures above the current 
values inside of [S2]". I'm saying I want the former to do the 
same. I understand that it currently doesn't, and I argue that 
having it do the same would be a much more useful behavior. 
Apart from the fact it's impossible, I don't see any good 
reason not to make it work. :p


I see your point too :)
But the latter form just grants the existence of an instance of 
S, whereas the first form doesn't.

By the way, this would work:

struct S1 {
static int n, m; // added a static here.
SomeType!(() => n + m) a;
}

struct SomeType(alias fn){}


--
  Simen


Re: Generic Property Implementation

2018-03-12 Thread Simen Kjærås via Digitalmars-d-learn

On Monday, 12 March 2018 at 10:37:00 UTC, Alex wrote:

Sure, you have.
https://dlang.org/spec/struct.html#assign-overload
Point #4.
In this case,

ref S opAssign(ref S rhs)
{
return this;
}


True. Can you fix these, too?

struct S {
S* ptr;
this(int dummy) {
ptr = 
}
~this() {
assert(ptr == );
}
}

void test(S s) {}

unittest {
test(S(0));
}

S test2() {
return S(0);
}

unittest {
S s = test2();
}


Another point is, that I hope, that pointers don't move 
anywhere, as in C, by definition.


And why not? D allows for moving garbage collectors.




unittest {
auto tmp = typeof(S.a)();
}


I thought, this shouldn't be possible (at least in my mind)


It wouldn't, and such code is not possible in D today:

struct S {
int n;
auto a() { return SomeType!(() => n)(); }
}

struct SomeType(alias fn) {
int get() { return fn(); }
}


But this is clearly valid.


Yes, it's an example of code that works in D today, with similar 
semantics to those implied in the other example. It's meant to 
show that just like `auto tmp = typeof(S.a())()` doesn't work 
today, it shouldn't work with the types used in my other example.





unittest {
   // cannot access frame pointer of 
foo.S.a.SomeType!(delegate () => this.n).SomeType

   auto tmp = typeof(S.a())();
}

For sure, tmp cannot be defined without an instance of S. So 
the correct unittest in my eyes would be:

unittest {
   S s;
   auto res = s.a;
   assert(res.get == S.init.n);
}


I think we may be speaking past one another. Yes, your unittest 
would be expected to pass.


If we go back to the example code:

struct S1 {
int n, m;
SomeType!(() => n + m) a;
}

vs.

struct S2 {
int n, m;
auto a() { return SomeType!(() => n + m)(); }
}

I would expect typeof(S1.a) and typeof(S2.a()) to be equivalent. 
I don't see a good reason why I should be able to construct an 
instance of typeof(S1.a), since I can't construct a 
typeof(S2.a()).


I see your point that "The latter closures above the current 
values inside of [S2]". I'm saying I want the former to do the 
same. I understand that it currently doesn't, and I argue that 
having it do the same would be a much more useful behavior. Apart 
from the fact it's impossible, I don't see any good reason not to 
make it work. :p


--
  Simen


Re: Generic Property Implementation

2018-03-12 Thread Alex via Digitalmars-d-learn

On Monday, 12 March 2018 at 09:54:20 UTC, Simen Kjærås wrote:


But I don't have a hook to update the pointer when the struct 
is moved. The GC may move my struct without informing me in any 
way. In fact, even just a copy construction will break this:


struct S {
S* ptr;
this(int dummy) {
ptr = 
}
~this() {
// This assert will fail.
assert(ptr == );
}
}

unittest {
S s1 = S(0);
S s2 = S(0);

assert( == s1.ptr);
assert( == s2.ptr);

s1 = s2;
}

The reason is copy construction makes a copy[1] of the lhs, 
then performs the copy construction[2]. If the copy 
construction[2] succeeds, the copy[1] is destroyed. If not, the 
copy[1] is blitted back over the original, to give the 
impression that nothing ever happened.


When the destructor is called on the copy[1],  returns a 
different address from what it did before, since it's a copy 
and logically resides at a different address.




Sure, you have.
https://dlang.org/spec/struct.html#assign-overload
Point #4.
In this case,

ref S opAssign(ref S rhs)
{
return this;
}

Another point is, that I hope, that pointers don't move anywhere, 
as in C, by definition.





struct SomeType(alias fn) {}

is (or has to be) lowered to something like

struct SomeType
{
  typeof(fn)* fn;
}

Even if fn contains a frame pointer to S it is perfectly legal 
to have such a type. SomeType would contain a delegate then.


Indeed. But stack frames aren't copied or moved the way structs 
are.




Yes. This is how the structs are meant to be, I thought :)




However, I think, the syntax


struct S {
int n, m;
SomeType!(() => n + m) a;
}


is still invalid and


struct S {
int n, m;
auto a() { return SomeType!(() => n + m)(); }
}


has another semantics.

The latter closures above the current values inside of S.
The former has to be constructible without the context, as it 
is perfectly legal to write


struct Outer
{
struct Inner{}
}

void main()
{
  auto i = Outer.Inner();
}

but would be impossible with this syntax.


I'm not using any inner structs in my examples. SomeType is 
assumed to be a separate type that knows nothing about S. If 
you're saying this should work:




Ah... I was unclear, I think...


unittest {
auto tmp = typeof(S.a)();
}


I thought, this shouldn't be possible (at least in my mind)



It wouldn't, and such code is not possible in D today:

struct S {
int n;
auto a() { return SomeType!(() => n)(); }
}

struct SomeType(alias fn) {
int get() { return fn(); }
}


But this is clearly valid.


unittest {
   // cannot access frame pointer of foo.S.a.SomeType!(delegate 
() => this.n).SomeType

   auto tmp = typeof(S.a())();
}

For sure, tmp cannot be defined without an instance of S. So the 
correct unittest in my eyes would be:

unittest {
   S s;
   auto res = s.a;
   assert(res.get == S.init.n);
}



--
  Simen




Re: Generic Property Implementation

2018-03-12 Thread Simen Kjærås via Digitalmars-d-learn

On Monday, 12 March 2018 at 08:59:49 UTC, Alex wrote:
An incomplete type is perfectly ok, so there should be no 
problem with a pointer of the same type inside a struct.
If accidentally the pointer refers "this", then it must have 
been set after construction. As before construction the value 
of "this"-pointer does not exist and after the construction the 
pointer to "this" has the default value of null.


Therefore, you are aware of this circumstance and appropriate 
movings of the pointer value are also up to you.


But I don't have a hook to update the pointer when the struct is 
moved. The GC may move my struct without informing me in any way. 
In fact, even just a copy construction will break this:


struct S {
S* ptr;
this(int dummy) {
ptr = 
}
~this() {
// This assert will fail.
assert(ptr == );
}
}

unittest {
S s1 = S(0);
S s2 = S(0);

assert( == s1.ptr);
assert( == s2.ptr);

s1 = s2;
}

The reason is copy construction makes a copy[1] of the lhs, then 
performs the copy construction[2]. If the copy construction[2] 
succeeds, the copy[1] is destroyed. If not, the copy[1] is 
blitted back over the original, to give the impression that 
nothing ever happened.


When the destructor is called on the copy[1],  returns a 
different address from what it did before, since it's a copy and 
logically resides at a different address.




struct SomeType(alias fn) {}

is (or has to be) lowered to something like

struct SomeType
{
  typeof(fn)* fn;
}

Even if fn contains a frame pointer to S it is perfectly legal 
to have such a type. SomeType would contain a delegate then.


Indeed. But stack frames aren't copied or moved the way structs 
are.




However, I think, the syntax


struct S {
int n, m;
SomeType!(() => n + m) a;
}


is still invalid and


struct S {
int n, m;
auto a() { return SomeType!(() => n + m)(); }
}


has another semantics.

The latter closures above the current values inside of S.
The former has to be constructible without the context, as it 
is perfectly legal to write


struct Outer
{
struct Inner{}
}

void main()
{
  auto i = Outer.Inner();
}

but would be impossible with this syntax.


I'm not using any inner structs in my examples. SomeType is 
assumed to be a separate type that knows nothing about S. If 
you're saying this should work:


unittest {
auto tmp = typeof(S.a)();
}

It wouldn't, and such code is not possible in D today:

struct S {
int n;
auto a() { return SomeType!(() => n)(); }
}

struct SomeType(alias fn) {
int get() { return fn(); }
}

unittest {
   // cannot access frame pointer of foo.S.a.SomeType!(delegate 
() => this.n).SomeType

   auto tmp = typeof(S.a())();
}

--
  Simen


Re: Generic Property Implementation

2018-03-12 Thread Alex via Digitalmars-d-learn

On Monday, 12 March 2018 at 07:04:19 UTC, Simen Kjærås wrote:

On Saturday, 10 March 2018 at 17:43:06 UTC, Simen Kjærås wrote:
I'm not sure how fixable this is, but I am sure that there's 
plenty of benefit to being able to write code like this:


struct S {
int n, m;
SomeType!(() => n + m) a;
}

over this:

struct S {
int n, m;
auto a() { return SomeType!(() => n + m)(); }
}

Anyways, I filed a bug for it:
https://issues.dlang.org/show_bug.cgi?id=18587


Actually, this is not fixable. If we consider for a moment the 
internals of SomeType in the above code, it's something like 
this:


struct SomeType(alias fn) {
S* context;
}

The full layout of S then becomes this:

struct S {
int n;
int m;
S* a.context; // points to 'this'.
}

Since structs in D are defined to be movable by blitting only, 
with no cleanup afterwards, internal pointers are a no-no. Ref 
https://dlang.org/spec/garbage.html:


Do not have pointers in a struct instance that point back to 
the same instance. The trouble with this is if the instance 
gets moved in memory, the pointer will point back to where it 
came from, with likely disastrous results.


One possible solution would be for the above code to generate 
the function a() instead of making 'a' a member. This has its 
own set of issues, though - suddenly something that looks like 
a member isn't one, and its address can't be taken, it can't be 
passed by ref, etc.


--
  Simen


This is strange...

An incomplete type is perfectly ok, so there should be no problem 
with a pointer of the same type inside a struct.
If accidentally the pointer refers "this", then it must have been 
set after construction. As before construction the value of 
"this"-pointer does not exist and after the construction the 
pointer to "this" has the default value of null.


Therefore, you are aware of this circumstance and appropriate 
movings of the pointer value are also up to you.


On the other side:

Isn't it the case, that

struct SomeType(alias fn) {}

is (or has to be) lowered to something like

struct SomeType
{
  typeof(fn)* fn;
}

Even if fn contains a frame pointer to S it is perfectly legal to 
have such a type. SomeType would contain a delegate then.


However, I think, the syntax


struct S {
int n, m;
SomeType!(() => n + m) a;
}


is still invalid and


struct S {
int n, m;
auto a() { return SomeType!(() => n + m)(); }
}


has another semantics.

The latter closures above the current values inside of S.
The former has to be constructible without the context, as it is 
perfectly legal to write


struct Outer
{
struct Inner{}
}

void main()
{
  auto i = Outer.Inner();
}

but would be impossible with this syntax.


Re: Generic Property Implementation

2018-03-12 Thread Simen Kjærås via Digitalmars-d-learn

On Saturday, 10 March 2018 at 17:43:06 UTC, Simen Kjærås wrote:
I'm not sure how fixable this is, but I am sure that there's 
plenty of benefit to being able to write code like this:


struct S {
int n, m;
SomeType!(() => n + m) a;
}

over this:

struct S {
int n, m;
auto a() { return SomeType!(() => n + m)(); }
}

Anyways, I filed a bug for it:
https://issues.dlang.org/show_bug.cgi?id=18587


Actually, this is not fixable. If we consider for a moment the 
internals of SomeType in the above code, it's something like this:


struct SomeType(alias fn) {
S* context;
}

The full layout of S then becomes this:

struct S {
int n;
int m;
S* a.context; // points to 'this'.
}

Since structs in D are defined to be movable by blitting only, 
with no cleanup afterwards, internal pointers are a no-no. Ref 
https://dlang.org/spec/garbage.html:


Do not have pointers in a struct instance that point back to 
the same instance. The trouble with this is if the instance 
gets moved in memory, the pointer will point back to where it 
came from, with likely disastrous results.


One possible solution would be for the above code to generate the 
function a() instead of making 'a' a member. This has its own set 
of issues, though - suddenly something that looks like a member 
isn't one, and its address can't be taken, it can't be passed by 
ref, etc.


--
  Simen


Re: Generic Property Implementation

2018-03-10 Thread Simen Kjærås via Digitalmars-d-learn

On Friday, 9 March 2018 at 23:34:56 UTC, Mike Franklin wrote:

On Friday, 9 March 2018 at 14:46:04 UTC, Simen Kjærås wrote:

1) Wrong return type:
unittest {
S s;
auto a = s.field;
// Fails - typeof(a) is Property!((get) => this.n, (set) 
=> this.n = set)

assert(is(typeof(a) == int));
}


This looks like a related issue:  
https://issues.dlang.org/show_bug.cgi?id=16657.  That's is a 
deal-breaker for me, but I think it could be fixed.


This has nothing to do with alias this, opCmp, or opEquals. It's 
simply that instead of returning an int, like the original 
property did, s.field returns a wrapper type. There's no real way 
around this.


Actually, there may be a more interesting way to implement the 
DIP I mentioned - what if named mixin templates could have 
overloaded operators and alias this? What if this worked:


struct S {
int n;
mixin property!(get => n, set => n = set) field;
}

template property(fns...) {
alias getter = findGetter!fns;
alias setter = findSetter!fns;

auto get() { return getter(DummyType.init); }
alias get this;
alias setter opAssign;

auto opOpAssign(string op, T)(T value) { /* ... */ }
// other overloads
}

unittest {
S s;
auto a = s.field; // Not a type, so alias this is used.
assert(is(typeof(a) == int));
// overloaded operators
s.field += 4;
++s.field;
}

From what I can see, the only necessary change would be that 
alias this and operator overloads be looked for in named mixin 
templates. Sounds doable. This would both enable 
auto-decaying/rvalue types, and allow for a bunch of interesting 
stuff with operator overloads.


...but there's an issue with this compared to real rvalue types - 
you can't return them from a function. One use case for rvalue 
types is short-circuiting when (a op1 b op2 c) can be done faster 
than ((a op1 b) op2 c), for instance when a, b, and c are 
BigInts, and op1 = ^^, op2 = %. Named mixin templates with 
overloaded operators cannot do this.



2) Noisy syntax:
If I had my druthers, mixin templates would be mixin'd 
automatically, and eponymous mixin templates would be a thing.


Yes, this would be nice, but I don't think it's a deal-breaker.


Agreed. I filed an issue on the eponymous mixin templates:
https://issues.dlang.org/show_bug.cgi?id=18586



3) Stringy functions:
The string literal functions are an eyesore, but would require 
some compiler work to fix.


Yeah, that's quite unfortunate; writing code in strings stinks.
 I actually prefer the DIP for this issue alone.


I'm not sure how fixable this is, but I am sure that there's 
plenty of benefit to being able to write code like this:


struct S {
int n, m;
SomeType!(() => n + m) a;
}

over this:

struct S {
int n, m;
auto a() { return SomeType!(() => n + m)(); }
}

Anyways, I filed a bug for it:
https://issues.dlang.org/show_bug.cgi?id=18587


If all of the issues you've identified were addressed, you'd 
end up with something like this (formatted in a C# way).


struct S {
int n;
property!
(
get =>
{
n
},
set =>
{
n = set
}
) field;
}

That's actually pretty darn good.  It makes me wonder if I 
should work on fixing those issues or continue with the DIP.


The real benefit to doing it outside the DIP is that the features 
can be used in other contexts. Rvalue types definitely have some 
more uses, the 'this.n' issue is simply a bug, eponymous mixin 
templates seem useful to me, and being able to use delegates as 
template parameters outside of member functions would make many 
things clearer.



If you don't have any objections I'd like to incorporate this 
implementation and your analysis into the DIP.


Of course.


Thank you again for doing this; you've saved me a awful lot of 
time, and have done more than I probably could have.


Pleasure. I've been spending some time on this myself, so it's 
not the first time I've tried to make a palatable implementation.


--
  Simen


Re: Generic Property Implementation

2018-03-09 Thread Mike Franklin via Digitalmars-d-learn

On Friday, 9 March 2018 at 14:46:04 UTC, Simen Kjærås wrote:

This is the best I've come up with in the current language:

struct S {
int n;
mixin property!("field", "get => this.n", "set => this.n = 
set");

}


Not bad.  Not good, but not bad either.


Sadly, there are issues:

1) Wrong return type:
unittest {
S s;
auto a = s.field;
// Fails - typeof(a) is Property!((get) => this.n, (set) => 
this.n = set)

assert(is(typeof(a) == int));
}


This looks like a related issue:  
https://issues.dlang.org/show_bug.cgi?id=16657.  That's is a 
deal-breaker for me, but I think it could be fixed.



2) Noisy syntax:
If I had my druthers, mixin templates would be mixin'd 
automatically, and eponymous mixin templates would be a thing.


Yes, this would be nice, but I don't think it's a deal-breaker.


3) Stringy functions:
The string literal functions are an eyesore, but would require 
some compiler work to fix. This fails:


struct S {
int n;
mixin property!("field", get => this.n, set => this.n = 
set);

}

The reason is there's no 'this' at the point of instantiation, 
since we're in the context of the type, not a function where 
'this' makes sense. It seems to me this should be fixable, but 
I have no idea how much work would be required.


Yeah, that's quite unfortunate; writing code in strings stinks.  
I actually prefer the DIP for this issue alone.



4) 'this' in function bodies
It should be possible to write "get => this.n" as "get => n". 
There's no ambiguity as to which 'n' I'm referring to. Filed a 
bug:

https://issues.dlang.org/show_bug.cgi?id=18584


Thanks for filing the issue.  I just might be able to fix it; 
I'll try.



Implementation:
https://gist.github.com/Biotronic/5849af011cbe9c7ea05515011d5995bf


You've done some great work here.  I spent about an hour on this 
yesterday, and my implementation started to require more and more 
mixing strings to get it to work.


If all of the issues you've identified were addressed, you'd end 
up with something like this (formatted in a C# way).


struct S {
int n;
property!
(
get =>
{
n
},
set =>
{
n = set
}
) field;
}

That's actually pretty darn good.  It makes me wonder if I should 
work on fixing those issues or continue with the DIP.


If you don't have any objections I'd like to incorporate this 
implementation and your analysis into the DIP.


Thank you again for doing this; you've saved me a awful lot of 
time, and have done more than I probably could have.


Mike





Re: Generic Property Implementation

2018-03-09 Thread Simen Kjærås via Digitalmars-d-learn

On Friday, 9 March 2018 at 01:22:15 UTC, Mike Franklin wrote:

* binary assignment operators (e.g. +=)
* unary assignment operators (e.g. ++)
* @safe, @nogc, and -betterC compatible
* at least as good code generation as that proposed in the DIP 
when optimizations are enabled.
* And should be scalable to data that isn't addressable (e.g. 
bitfields).  See

https://issues.dlang.org/show_bug.cgi?id=16187


This is the best I've come up with in the current language:

struct S {
int n;
mixin property!("field", "get => this.n", "set => this.n = 
set");

}

unittest {
import std.conv : to;
S s;
s.field = 3;
assert(s.field == 3);
s.field += 2;
assert(s.field == 5);
s.field++;
assert(s.field == 6);
assert(s.field.to!string == "6");
}

Sadly, there are issues:

1) Wrong return type:
unittest {
S s;
auto a = s.field;
// Fails - typeof(a) is Property!((get) => this.n, (set) => 
this.n = set)

assert(is(typeof(a) == int));
}
Not a whole lot to do about this. I've pondered writing a DIP on 
auto-decaying/rvalue types, which would decay to a different type 
the moment they're passed to a function or assigned to a 
variable. The feature would probably not be worth the trouble, 
though.



2) Noisy syntax:
If I had my druthers, mixin templates would be mixin'd 
automatically, and eponymous mixin templates would be a thing. 
That would give us this syntax:


struct S {
int n;
property!("get => this.n", "set => this.n = set") field;
}


3) Stringy functions:
The string literal functions are an eyesore, but would require 
some compiler work to fix. This fails:


struct S {
int n;
mixin property!("field", get => this.n, set => this.n = set);
}

The reason is there's no 'this' at the point of instantiation, 
since we're in the context of the type, not a function where 
'this' makes sense. It seems to me this should be fixable, but I 
have no idea how much work would be required.



4) 'this' in function bodies
It should be possible to write "get => this.n" as "get => n". 
There's no ambiguity as to which 'n' I'm referring to. Filed a 
bug:

https://issues.dlang.org/show_bug.cgi?id=18584

Implementation:
https://gist.github.com/Biotronic/5849af011cbe9c7ea05515011d5995bf

--
  Simen


Re: Generic Property Implementation

2018-03-08 Thread Mike Franklin via Digitalmars-d-learn

On Friday, 9 March 2018 at 01:22:15 UTC, Mike Franklin wrote:

I would like to know if this can be improved to support the 
following:


* binary assignment operators (e.g. +=)
* unary assignment operators (e.g. ++)
* @safe, @nogc, and -betterC compatible
* at least as good code generation as that proposed in the DIP 
when optimizations are enabled.


 * And should be scalable to data that isn't addressable (e.g. 
bitfields).  See https://issues.dlang.org/show_bug.cgi?id=16187





Re: Generic Property Implementation

2018-03-08 Thread Mike Franklin via Digitalmars-d-learn

On Tuesday, 20 February 2018 at 14:34:53 UTC, bauss wrote:
Would there be a reason why this wouldn't be a good 
implementation?


If so what and how could it be improved?

Are there flaws in an implementation like this?
[... snip ...]


I am very interested in this as a potential alternative to the 
binary assignment operators for properties DIP 
(https://github.com/JinShil/DIPs/blob/master/DIPs/DIP1xxx-mvf.md)


I would like to know if this can be improved to support the 
following:


* binary assignment operators (e.g. +=)
* unary assignment operators (e.g. ++)
* @safe, @nogc, and -betterC compatible
* at least as good code generation as that proposed in the DIP 
when optimizations are enabled.


D has the philosophy that the language should strive to provide 
composable primitives, and delegate syntactic sugar to libraries. 
 I like that philosophy and I think it would prevent an expense 
of limited resources if we could find a good library 
implementation for this.


Mike


Re: Generic Property Implementation

2018-02-20 Thread Simen Kjærås via Digitalmars-d-learn

On Tuesday, 20 February 2018 at 14:34:53 UTC, bauss wrote:
Would there be a reason why this wouldn't be a good 
implementation?


What is the intended use case for this? The main feature seems to 
be an ability to have read-only members, which is nice. Are there 
other benefits?




If so what and how could it be improved?

Are there flaws in an implementation like this?


[snip]


bool opEquals(T other)


Why not use the default == implementation? It's supposed to do 
the right thing. Yours will fail for structs and classes. Since 
you're using alias this, it shouldn't even be necessary to 
implement opEquals - the right one should be called automagically.




static if (!(isArray!T) && !(isAssociativeArray!T))
{
int opCmp(T other)


This will give unexpected errors when comparing structs or 
classes that don't overload opCmp. Better to replace the static 
if with is(typeof(T.init < T.init)), or better yet, 
std.traits.isOrderingComparable.


Also, it's not necessary - alias this ensures that the correct 
opCmp function is called.




string toString()


Why not use std.typecons.to for everything in here? Its 
implementors probably have thought of many more corner cases than 
you have. There is an argument to be made for compile-time 
overhead, I guess.



So, my version of the same:

struct Property(T, bool readOnly = false)
{
private T _value;

static if (readOnly)
T __GET() { return _value; }
else
ref T __GET() { return _value; }

alias __GET this;

string toString()
{
import std.conv : to;
return _value.to!string;
}
}

One more thing: while you provided some very nice examples of how 
to use the type, it would be nice to have these more test-like. 
Something like this:


unittest
{
auto foo = new Foo;
foo.baz = "Hello";

// Check that value was set correctly:
assert(foo.baz == "Hello");

// You can even check if something will compile:
assert(!__traits(compiles, foo.bar = 24));
}

Sorry if this came off a bit crass - I don't understand when I 
should use it, so it's hard to give feedback on that.


--
  Simen


Generic Property Implementation

2018-02-20 Thread bauss via Digitalmars-d-learn
Would there be a reason why this wouldn't be a good 
implementation?


If so what and how could it be improved?

Are there flaws in an implementation like this?

struct Property(T, bool readOnly = false)
{
import std.traits : isScalarType, isArray, 
isAssociativeArray, isSomeString;


private T _value;

T __GET() { return _value; }

static if (readOnly)
{
private
{
void __SET(T newValue) { _value = newValue; }

void opAssign(T newValue)
{
__SET(newValue);
}
}
}
else
{
void __SET(T newValue) { _value = newValue; }

void opAssign(T newValue)
{
__SET(newValue);
}
}

bool opEquals(T other)
{
static if (isScalarType!T || isArray!T || 
isAssociativeArray!T)

{
return _value == other;
}
else
{
if (_value is other) return true;
if (_value is null || other is null) return false;
if (typeid(_value) == typeid(other)) return 
_value.opEquals(other);
return _value.opEquals(other) && 
other.opEquals(_value);

}
}

static if (!(isArray!T) && !(isAssociativeArray!T))
{
int opCmp(T other)
{
static if (isScalarType!T)
{
if (_value < other) return -1;
if (_value > other) return 1;

return 0;
}
else
{
return _value.opCmp(other);
}
}
}

string toString()
{
static if (isArray!T && isSomeString!T)
{
return _value;
}
else static if (__traits(hasMember, T, "toString"))
{
return _value.toString();
}
else
{
import std.conv : to;

return to!string(_value);
}
}

alias __GET this;
}

alias ReadOnlyProperty(T) = Property!(T, true);

---

This would allow something like this:

class Foo
{
ReadOnlyProperty!int bar;

Property!string baz;

Property!int faz;

Property!Bar bar2;
}

class Bar
{
int boo;
}

Which can be used like:

auto foo = new Foo;
foo.baz = "Hello";
foo.baz = foo.baz ~ " World!";
foo.faz = 250 + foo.bar;

foo.bar2 = new Bar;
foo.bar2.boo = 200;

import std.stdio;

writeln(foo.bar);
writeln(foo.baz);
writeln(foo.faz);
writeln(foo.bar2.boo);