Re: Reset all Members of a Aggregate Instance

2015-12-08 Thread Marc Schütz via Digitalmars-d-learn

On Tuesday, 8 December 2015 at 05:13:51 UTC, Chris Wright wrote:

On Tue, 08 Dec 2015 14:12:02 +1100, Daniel Murphy wrote:


On 4/12/2015 8:38 AM, Chris Wright wrote:
An object reference is just a pointer, but we can't directly 
cast it. So we make a pointer to it and cast that; the type 
system allows it. Now we can access the data that the object 
reference refers to directly.


Casting is fine too: cast(void*)classRef


Amazing. I assumed that this wouldn't be allowed because it's 
not exactly nice to the type system, but apparently you can 
cast anything but a user- defined value type to void*. Bool, 
dchar, associative arrays, normal arrays, the good void* casts 
them all.


A class can theoretically overload `opCast`. Therefore, to be 
100% sure it works in all cases, you should use `*cast(void**) 
`.


Re: Reset all Members of a Aggregate Instance

2015-12-07 Thread Daniel Murphy via Digitalmars-d-learn

On 4/12/2015 8:38 AM, Chris Wright wrote:

An object reference is just a pointer, but we can't directly cast it. So
we make a pointer to it and cast that; the type system allows it. Now we
can access the data that the object reference refers to directly.


Casting is fine too: cast(void*)classRef



Re: Reset all Members of a Aggregate Instance

2015-12-07 Thread Chris Wright via Digitalmars-d-learn
On Tue, 08 Dec 2015 14:12:02 +1100, Daniel Murphy wrote:

> On 4/12/2015 8:38 AM, Chris Wright wrote:
>> An object reference is just a pointer, but we can't directly cast it.
>> So we make a pointer to it and cast that; the type system allows it.
>> Now we can access the data that the object reference refers to
>> directly.
> 
> Casting is fine too: cast(void*)classRef

Amazing. I assumed that this wouldn't be allowed because it's not exactly 
nice to the type system, but apparently you can cast anything but a user-
defined value type to void*. Bool, dchar, associative arrays, normal 
arrays, the good void* casts them all.


Re: Reset all Members of a Aggregate Instance

2015-12-05 Thread Ali Çehreli via Digitalmars-d-learn

On 12/05/2015 01:32 AM, Observer wrote:


Won't clear(c); do the trick?  ((pp187-188 of TDPL)


clear() has been renamed as destroy() but it won't work by itself 
because the OP wants a reusable object. I think, in addition to 
destroy(), the default constructor should be run:


  https://dlang.org/phobos/object.html#.destroy

Ali



Re: Reset all Members of a Aggregate Instance

2015-12-05 Thread Chris Wright via Digitalmars-d-learn
On Sat, 05 Dec 2015 07:48:16 -0800, Ali Çehreli wrote:

> On 12/05/2015 01:32 AM, Observer wrote:
> 
>> Won't clear(c); do the trick?  ((pp187-188 of TDPL)
> 
> clear() has been renamed as destroy() but it won't work by itself
> because the OP wants a reusable object. I think, in addition to
> destroy(), the default constructor should be run:
> 
>https://dlang.org/phobos/object.html#.destroy
> 
> Ali

The default constructor doesn't set default field values, though, which 
is why my solution involved copying ClassInfo.init.


Re: Reset all Members of a Aggregate Instance

2015-12-05 Thread Jakob Ovrum via Digitalmars-d-learn

On Saturday, 5 December 2015 at 16:28:18 UTC, Chris Wright wrote:
The default constructor doesn't set default field values, 
though, which is why my solution involved copying 
ClassInfo.init.


Thanks, this is a handy factoid. Reminds me of the whole __dtor 
vs __xdtor debacle.




Re: Reset all Members of a Aggregate Instance

2015-12-05 Thread Observer via Digitalmars-d-learn

On Thursday, 3 December 2015 at 21:04:00 UTC, Nordlöw wrote:

Given

class C
{
// lots of members
}

and a function

f(C c)
{
}

is there a generic way, perhaps through reflection, to reset 
(inside f) all members of `c` to their default values? 
Something along


foreach(ref member; __traits(allMembers, c))
{
member = typeof(member).init;
}


Won't clear(c); do the trick?  ((pp187-188 of TDPL)


Re: Reset all Members of a Aggregate Instance

2015-12-04 Thread Enamex via Digitalmars-d-learn

On Thursday, 3 December 2015 at 21:04:00 UTC, Nordlöw wrote:

...


Unless I'm missing something very important: Isn't that 
essentially what the `out` attribute on a function parameter does?


Re: Reset all Members of a Aggregate Instance

2015-12-03 Thread Tofu Ninja via Digitalmars-d-learn

On Thursday, 3 December 2015 at 21:04:00 UTC, Nordlöw wrote:

...


I think reflection will be a bad choice for this because of 
private members and what not.

I think the correct way is:

void reset(C)(ref C c)
{
static if(is(C == class))
{
auto init = typeid(c).init();
auto objmem = ((cast(void*)c)[0 .. init.length]);
if(init.ptr == null) (cast(byte[])objmem)[] = 0;
else objmem[] = init[];
if(c.classinfo.defaultConstructor != null)
c.classinfo.defaultConstructor(c);
}
else c = C.init;
}

Seems to work for structs, classes, and basic types. It even 
calls the default constructor for classes, even if there is 
inheritance and the object passed in is not actually C but a sub 
class of C.


Re: Reset all Members of a Aggregate Instance

2015-12-03 Thread Tofu Ninja via Digitalmars-d-learn

On Friday, 4 December 2015 at 04:08:33 UTC, Tofu Ninja wrote:

On Thursday, 3 December 2015 at 21:04:00 UTC, Nordlöw wrote:

...


I think reflection will be a bad choice for this because of 
private members and what not.

I think the correct way is:

void reset(C)(ref C c)
{
static if(is(C == class))
{
auto init = typeid(c).init();
auto objmem = ((cast(void*)c)[0 .. init.length]);
if(init.ptr == null) (cast(byte[])objmem)[] = 0;
else objmem[] = init[];
if(c.classinfo.defaultConstructor != null)
c.classinfo.defaultConstructor(c);
}
else c = C.init;
}

Seems to work for structs, classes, and basic types. It even 
calls the default constructor for classes, even if there is 
inheritance and the object passed in is not actually C but a 
sub class of C.


Oh after reading chris's post, probably should change to...

void reset(C)(ref C c)
{
static if(is(C == class))
{
auto type_info = typeid(c);
auto class_info = c.classinfo;
destroy(c);
auto init = type_info.init();
auto objmem = ((cast(void*)c)[0 .. init.length]);
if(init.ptr == null) (cast(byte[])objmem)[] = 0;
else objmem[] = init[];
if(class_info.defaultConstructor != null)
class_info.defaultConstructor(c);
else throw new Exception("No default constructor");
}
else c = C.init;
}


Re: Reset all Members of a Aggregate Instance

2015-12-03 Thread Sebastiaan Koppe via Digitalmars-d-learn

On Thursday, 3 December 2015 at 21:13:59 UTC, Nordlöw wrote:
Need to assert that not a function and mutability 
(std.traits.isMutable)


Yeah you need to do that.


Re: Reset all Members of a Aggregate Instance

2015-12-03 Thread Jakob Ovrum via Digitalmars-d-learn

On Thursday, 3 December 2015 at 21:04:00 UTC, Nordlöw wrote:

Given

class C
{
// lots of members
}

and a function

f(C c)
{
}

is there a generic way, perhaps through reflection, to reset 
(inside f) all members of `c` to their default values? 
Something along


foreach(ref member; __traits(allMembers, c))
{
member = typeof(member).init;
}


import std.traits;

foreach(i, member; FieldNameTuple!C)
{
alias FieldType = Fields!C[i];
static if(isMutable!FieldType)
__traits(getMember, c, member) = FieldType.init;
}

However, it doesn't work in the presence of private fields. A 
better alternative is probably to `destroy` the instance then 
`emplace` to default-construct a new instance over it.


Re: Reset all Members of a Aggregate Instance

2015-12-03 Thread Nordlöw via Digitalmars-d-learn

On Thursday, 3 December 2015 at 21:04:00 UTC, Nordlöw wrote:

Something along

foreach(ref member; __traits(allMembers, c))
{
member = typeof(member).init;
}


This works for me:

void resetAllMembers(T)(T c)
if (is(T == class))
{
foreach (ref m; c.tupleof)
{
import std.traits : isMutable;
alias M = typeof(m);
static if (isMutable!M)
{
m = M.init;
}
}
}



Re: Reset all Members of a Aggregate Instance

2015-12-03 Thread Nordlöw via Digitalmars-d-learn

On Thursday, 3 December 2015 at 21:38:48 UTC, Chris Wright wrote:

The terrible way is something like:

void reset(Object o)
in {
  assert(!(o is null));
}
body {
  auto p = cast(ubyte*)*cast(void**)
  auto ci = o.classinfo;
  auto init = cast(ubyte)ci.init;
  p[0..init.length] = init[];
  if (ci.defaultConstructor) {
ci.defaultConstructor(o);
  } else {
throw new Exception("no default constructor; object is in 
invalid

state");
  }
}


In what way is this better than my solution?


Re: Reset all Members of a Aggregate Instance

2015-12-03 Thread Chris Wright via Digitalmars-d-learn
On Thu, 03 Dec 2015 21:55:04 +, Nordlöw wrote:

> On Thursday, 3 December 2015 at 21:38:48 UTC, Chris Wright wrote:
>> The terrible way is something like:
>>
>> void reset(Object o)
>> in {
>>   assert(!(o is null));
>> }
>> body {
>>   auto p = cast(ubyte*)*cast(void**)
>>   auto ci = o.classinfo;
>>   auto init = cast(ubyte)ci.init; p[0..init.length] = init[];
>>   if (ci.defaultConstructor) {
>> ci.defaultConstructor(o);
>>   } else {
>> throw new Exception("no default constructor; object is in
>> invalid state");
>>   }
>> }
> 
> In what way is this better than my solution?

I called my solution "terrible", which doesn't suggest that I think well 
of it compared to alternatives. I did this mainly to provide another 
implementation that someone else had alluded to. But since you asked...

You're ignoring the distinction between runtime and compiletime types, 
and you are resetting fields to the default values of their types, not 
the default values of the fields.

To wit:

class Foo {
  int i = 5;
}
class Bar : Foo {
  int j = 6;
  this() {}
  this(int a, int b) { i = a; j = b; }
}
Foo foo = new Bar(10, 10);
nordlow.resetAllFields(foo);

We expect foo to look like a default-initialized instance of whatever it 
began life as. Like if we implemented the equality operations, we'd 
expect to see this work: `assert(foo == new Bar);` But we don't. Your 
solution doesn't know what the default field value of Foo.i is, so it 
erroneously resets it to 0. We wanted 5.

Similarly, your solution ignores the fact that we've got an instance of 
Bar here, not an instance of Foo, and there are additional fields that 
need to be reset. You're using compile time reflection, so there's no way 
around that -- unless you create a virtual method with a mixin and 
require the user to mix it into each class in the hierarchy.

My solution still gets a ton wrong. I forgot to call the destructor, for 
instance. I can't call the constructor if it has parameters, and that 
means the object might well be in an invalid state. It's not safe in the 
face of potential runtime changes.

Assuming I encountered a case that both our solutions could handle 
correctly, I'd prefer yours. It's safer.


Re: Reset all Members of a Aggregate Instance

2015-12-03 Thread Adam D. Ruppe via Digitalmars-d-learn

On Thursday, 3 December 2015 at 21:04:00 UTC, Nordlöw wrote:
is there a generic way, perhaps through reflection, to reset 
(inside f) all members of `c` to their default values?


You could always copy the init back over it. For a struct:

s = Struct.init;

for a class... well, the code is a lot uglier and liable to break 
if you don't have the static type right. Maybe better off 
wrapping the class members in a struct and doing it that way.


Re: Reset all Members of a Aggregate Instance

2015-12-03 Thread Nordlöw via Digitalmars-d-learn

On Thursday, 3 December 2015 at 21:04:00 UTC, Nordlöw wrote:

Given

class C
{
// lots of members
}

and a function

f(C c)
{
}

is there a generic way, perhaps through reflection, to reset 
(inside f) all members of `c` to their default values? 
Something along


foreach(ref member; __traits(allMembers, c))
{
member = typeof(member).init;
}


Back in 2007 it didn't seem to exist a standard way of doing this:

http://forum.dlang.org/post/fbs9eg$721$1...@digitalmars.com

Is tupleof the best contender?


Re: Reset all Members of a Aggregate Instance

2015-12-03 Thread Sebastiaan Koppe via Digitalmars-d-learn

Haven't compiled but it should look something like this:

foreach(member; __traits(allMembers, typeof(c)))
__traits(getMember, c, member) = typeof(__traits(getMember, 
c, member)).init;




Re: Reset all Members of a Aggregate Instance

2015-12-03 Thread Nordlöw via Digitalmars-d-learn
On Thursday, 3 December 2015 at 21:08:30 UTC, Sebastiaan Koppe 
wrote:

Haven't compiled but it should look something like this:

foreach(member; __traits(allMembers, typeof(c)))
__traits(getMember, c, member) = typeof(__traits(getMember, 
c, member)).init;


Need to assert that not a function and mutability 
(std.traits.isMutable)


Re: Reset all Members of a Aggregate Instance

2015-12-03 Thread Chris Wright via Digitalmars-d-learn
The terrible way is something like:

void reset(Object o)
in {
  assert(!(o is null));
}
body {
  auto p = cast(ubyte*)*cast(void**)
  auto ci = o.classinfo;
  auto init = cast(ubyte)ci.init;
  p[0..init.length] = init[];
  if (ci.defaultConstructor) {
ci.defaultConstructor(o);
  } else {
throw new Exception("no default constructor; object is in invalid 
state");
  }
}

An object reference is just a pointer, but we can't directly cast it. So 
we make a pointer to it and cast that; the type system allows it. Now we 
can access the data that the object reference refers to directly.

`o.classinfo` gets the runtime type information object for the actual 
class that `o` belongs to. The `init` property gives you a single blob of 
bytes representing the default field values of that class. So we copy 
that over into the object.

To finish up, if the type has a default constructor, we invoke it. We 
don't have a way to identify any other constructors that we could invoke.

This is usually not a good thing to do, but if you really need to, you 
can.