December 03, 2015 Re: Reset all Members of a Aggregate Instance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Nordlöw | On Thu, 03 Dec 2015 21:55:04 +0000, 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**)&o;
>> 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.
|
December 04, 2015 Re: Reset all Members of a Aggregate Instance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Nordlöw | 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. |
December 04, 2015 Re: Reset all Members of a Aggregate Instance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Tofu Ninja | 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; } |
December 04, 2015 Re: Reset all Members of a Aggregate Instance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Nordlöw | 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?
|
December 05, 2015 Re: Reset all Members of a Aggregate Instance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Nordlöw | 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)
|
December 05, 2015 Re: Reset all Members of a Aggregate Instance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Observer | 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 |
December 05, 2015 Re: Reset all Members of a Aggregate Instance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | 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.
|
December 05, 2015 Re: Reset all Members of a Aggregate Instance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Chris Wright | 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.
|
December 08, 2015 Re: Reset all Members of a Aggregate Instance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Chris Wright | 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
|
December 08, 2015 Re: Reset all Members of a Aggregate Instance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Daniel Murphy | 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.
|
Copyright © 1999-2021 by the D Language Foundation