digitalmars.D.learn - hasDataMember and mixin inconsistency
- Olivier Grant (61/61) Jan 26 2013 Hi,
- Philippe Sigaud (32/38) Jan 27 2013 Hi,
- Olivier Grant (24/73) Jan 27 2013 But what I'm surprised by is that the behavior of hasDataMember
- Artur Skawina (6/18) Jan 27 2013 Yes, it's a bug. Assignments to .init do not make sense and shouldn't be
- Philippe Sigaud (36/73) Jan 27 2013 Test is inside main() { ... }. I guess it's not visible from the
- Olivier Grant (5/91) Jan 27 2013 Using dmd 2.060 and command line "rdmd test.d", I get an
- Philippe Sigaud (4/15) Jan 27 2013 Just testing whether t.M can be assigned to something (ie, is it a value...
- Olivier Grant (10/31) Jan 28 2013 Ok, I must be missing something, why "t.D" or is this just a
- Philippe Sigaud (5/16) Jan 28 2013 Yes, but if a type is not default-constructible, I don't know how to tes...
- Olivier Grant (16/44) Jan 29 2013 My solution to that was to try and compile the definition of a
Hi, First of all, I am very new to D as I've just been playing around with it for the last week, so I might be missing something very obvious. I'm trying to write a template that would allow me to determine if a struct or class has a data member with a specific name (similar to what the hasMember template does, but only for data members). I originally wrote the following template : [code] template hasDataMember( T, string M ) { enum hasDataMember = __traits( compiles, mixin("( ref T x, ref T y ){ x." ~ M ~ " = y." ~ M ~ "; }") ); } unittest { struct Test { struct A { } void B( ) { } property A C( ) const { return F; } property long D( ) const { return E; } property void D( long e ) { E = e; } long E; A F; } assert(!hasDataMember!(int,"A")); assert(!hasDataMember!(int,"init")); //assert(!hasDataMember!(Test,"init")); // This fails, why? assert(!hasDataMember!(Test,"A")); assert(!hasDataMember!(Test,"B")); assert(!hasDataMember!(Test,"C")); assert( hasDataMember!(Test,"D")); assert( hasDataMember!(Test,"E")); assert( hasDataMember!(Test,"F")); } [/code] And it works pretty well, but it gets the wrong result for the following test case which is commented out. Originally, I thought maybe you were allowed to write to the .init member of structures, but writing the same code directly without relying on a mixin actually yields the right result (all the asserts pass): [code] struct A { int b_; } assert(!__traits(compiles,( ref int x, ref int y ){ x.init = y.init; })); assert(!__traits(compiles,( ref Test0 x, ref Test0 y ){ x.init = y.init; })); assert( __traits(compiles,( ref Test0 x, ref Test0 y ){ x.b_ = y.b_; })); [/code] So I have two questions : 1) Why is there this inconsistency between the mixin and direct version ? 2) Is there a better way to check for the existence of a data member ? Best regards, Olivier.
Jan 26 2013
Hi, If think in your code, your testing whether or a not a mixin("...") statement is valid D. Which it is. I'd put the mixin externally: template hasDataMember( T, string M ) { mixin(" enum hasDataMember = __traits( compiles, ( ref T x, ref T y ){ x." ~ M ~ " = y." ~ M ~ "; } );"); } Also, I suppose the Test inner struct is not visible from the hasDataMember template. The template is instantiated where it's declared, not where it's called. You could use a mixin template, I guess. I'd use a string mixin, but then I was converted to string mixins a few years ago :) string hasDataMember( T )(string M ) { return " __traits(compiles, { Test t; auto _ = t.D; // reading t.M t." ~ M ~ " = t." ~ M ~ "; // assign to t.M })"; } using is a bit more noisy than your solution: mixin(hadDataMember!(Test)("M"))And it works pretty well, but it gets the wrong result for the following test case which is commented out. Originally, I thought maybe you were allowed to write to the .init member of structures, but writing the same code directly without relying on a mixin actually yields the right result (all the asserts pass)You cannot write to .init, it's not a member. It's a built-in property, like .sizeof or .offsetof.2) Is there a better way to check for the existence of a data member ?If by data member, you mean some symbol that can be read and written to, then I'd test just that. See the string mixin before: it tests for existence, reading and writing.
Jan 27 2013
On Sunday, 27 January 2013 at 09:49:33 UTC, Philippe Sigaud wrote:Hi, If think in your code, your testing whether or a not a mixin("...") statement is valid D. Which it is.But what I'm surprised by is that the behavior of hasDataMember with my implementation works fine for all test cases except hasDataMember!(Test, "init"). If it was only testing whether the mixin statement was well formed or not, shouldn't my implementation of hasDataMember always return true ? What's more confusing is that it seems to work properly for all test cases except a built-in property of a structure : assert(!hasDataMember!(long, "init")); // This succeeds. assert(!hasDataMember!(Test, "init")); // This fails.I'd put the mixin externally: template hasDataMember( T, string M ) { mixin(" enum hasDataMember = __traits( compiles, ( ref T x, ref T y ){ x." ~ M ~ " = y." ~ M ~ "; } );"); }I've just tried that and it unfortunately does not work, the same test case still fails.Also, I suppose the Test inner struct is not visible from the hasDataMember template. The template is instantiated where it's declared, not where it's called. You could use a mixin template, I guess.I'm not sure at all what you mean by that. I thought all symbols within a source file were visible irrespective of their order or scope? Also, the test failure is on the Test structure directly, not its inner structure A.I'd use a string mixin, but then I was converted to string mixins a few years ago :) string hasDataMember( T )(string M ) { return " __traits(compiles, { Test t; auto _ = t.D; // reading t.M t." ~ M ~ " = t." ~ M ~ "; // assign to t.M })"; } using is a bit more noisy than your solution: mixin(hadDataMember!(Test)("M"))I've tried that as well and it still fails on the same test case again.Makes sense.And it works pretty well, but it gets the wrong result for the following test case which is commented out. Originally, I thought maybe you were allowed to write to the .init member of structures, but writing the same code directly without relying on a mixin actually yields the right result (all the asserts pass)You cannot write to .init, it's not a member. It's a built-in property, like .sizeof or .offsetof.Could this be a compiler bug by any chance? It seems really weird that the template would work for intrinsic types but not for structure, especially when the exact same template without the use of mixin works fine. Thanks again for your help.2) Is there a better way to check for the existence of a data member ?If by data member, you mean some symbol that can be read and written to, then I'd test just that. See the string mixin before: it tests for existence, reading and writing.
Jan 27 2013
On 01/27/13 12:47, Olivier Grant wrote:On Sunday, 27 January 2013 at 09:49:33 UTC, Philippe Sigaud wrote:It does, but apparently the compiler disagrees.You cannot write to .init, it's not a member. It's a built-in property, like .sizeof or .offsetof.Makes sense.Yes, it's a bug. Assignments to .init do not make sense and shouldn't be allowed. I just tried, and the old gdc version i have here doesn't flag them as errors (but did segfault after processing one ;) ). arturCould this be a compiler bug by any chance? It seems really weird2) Is there a better way to check for the existence of a data member ?If by data member, you mean some symbol that can be read and written to, then I'd test just that. See the string mixin before: it tests for existence, reading and writing.
Jan 27 2013
? I'll find the files again, for I tested before posting.I'd put the mixin externally: template hasDataMember( T, string M ) { mixin(" enum hasDataMember = __traits( compiles, ( ref T x, ref T y ){ x." ~ M ~ " = y." ~ M ~ "; } );"); }I've just tried that and it unfortunately does not work, the same test case still fails.Test is inside main() { ... }. I guess it's not visible from the module inner scope.Also, I suppose the Test inner struct is not visible from the hasDataMember template. The template is instantiated where it's declared, not where it's called. You could use a mixin template, I guess.I'm not sure at all what you mean by that. I thought all symbols within a source file were visible irrespective of their order or scope? Also, the test failure is on the Test structure directly, not its inner structure A.? Here is what I used before posting: string hasDataMember( T )(string M ) { return " __traits(compiles, { Test t; auto _ = t.D; // reading t.M t." ~ M ~ " = t." ~ M ~ "; // assign to t.M })"; } void main() { struct Test { struct A { } void B( ) { } property A C( ) const { return F; } property long D( ) const { return E; } property void D( long e ) { E = e; } long E; A F; } assert(!mixin(hasDataMember!(Test)("init"))); assert(!mixin(hasDataMember!(int)("init"))); assert(!mixin(hasDataMember!(Test)("init"))); // Passes assert(!mixin(hasDataMember!(Test)("A"))); assert(!mixin(hasDataMember!(Test)("B"))); assert(!mixin(hasDataMember!(Test)("C"))); assert(mixin(hasDataMember!(Test)("D"))); assert(mixin(hasDataMember!(Test)("E"))); assert(mixin(hasDataMember!(Test)("F"))); } It seems the right behaviour. Am I mistaken?I'd use a string mixin, but then I was converted to string mixins a few years ago :) string hasDataMember( T )(string M ) { return " __traits(compiles, { Test t; auto _ = t.D; // reading t.M t." ~ M ~ " = t." ~ M ~ "; // assign to t.M })"; } using is a bit more noisy than your solution: mixin(hadDataMember!(Test)("M"))I've tried that as well and it still fails on the same test case again.
Jan 27 2013
On Sunday, 27 January 2013 at 12:58:39 UTC, Philippe Sigaud wrote:What is the purpose of "auto _ = t.D;" ?? I'll find the files again, for I tested before posting.I'd put the mixin externally: template hasDataMember( T, string M ) { mixin(" enum hasDataMember = __traits( compiles, ( ref T x, ref T y ){ x." ~ M ~ " = y." ~ M ~ "; } );"); }I've just tried that and it unfortunately does not work, the same test case still fails.Test is inside main() { ... }. I guess it's not visible from the module inner scope.Also, I suppose the Test inner struct is not visible from the hasDataMember template. The template is instantiated where it's declared, not where it's called. You could use a mixin template, I guess.I'm not sure at all what you mean by that. I thought all symbols within a source file were visible irrespective of their order or scope? Also, the test failure is on the Test structure directly, not its inner structure A.? Here is what I used before posting: string hasDataMember( T )(string M ) { return " __traits(compiles, { Test t; auto _ = t.D; // reading t.M t." ~ M ~ " = t." ~ M ~ "; // assign to t.M })"; }I'd use a string mixin, but then I was converted to string mixins a few years ago :) string hasDataMember( T )(string M ) { return " __traits(compiles, { Test t; auto _ = t.D; // reading t.M t." ~ M ~ " = t." ~ M ~ "; // assign to t.M })"; } using is a bit more noisy than your solution: mixin(hadDataMember!(Test)("M"))I've tried that as well and it still fails on the same test case again.void main() { struct Test { struct A { } void B( ) { } property A C( ) const { return F; } property long D( ) const { return E; } property void D( long e ) { E = e; } long E; A F; } assert(!mixin(hasDataMember!(Test)("init"))); // (1) - Fails assert(!mixin(hasDataMember!(int)("init"))); // (2) - Fails assert(!mixin(hasDataMember!(Test)("A"))); assert(!mixin(hasDataMember!(Test)("B"))); assert(!mixin(hasDataMember!(Test)("C"))); assert(mixin(hasDataMember!(Test)("D"))); assert(mixin(hasDataMember!(Test)("E"))); assert(mixin(hasDataMember!(Test)("F"))); } It seems the right behaviour. Am I mistaken?Using dmd 2.060 and command line "rdmd test.d", I get an assertion fail on both calls that check for a member "init" whether for int or Test.
Jan 27 2013
Just testing whether t.M can be assigned to something (ie, is it a value?) I use '_' as a variable name to indicate I don't care for it's precise name/value. It's just a placeholder.string hasDataMember( T )(string M ) { return " __traits(compiles, { Test t; auto _ = t.D; // reading t.M t." ~ M ~ " = t." ~ M ~ "; // assign to t.M })"; }What is the purpose of "auto _ = t.D;" ?Using dmd 2.060 and command line "rdmd test.d", I get an assertion fail on both calls that check for a member "init" whether for int or Test.Using 2.061 here.
Jan 27 2013
On Sunday, 27 January 2013 at 16:51:26 UTC, Philippe Sigaud wrote:Ok, I must be missing something, why "t.D" or is this just a typo? And secondly wouldn't "x.M = y.M" take care of checking that the member can be read and written to ? Also, doesn't your solution require T to be default constructible ?Just testing whether t.M can be assigned to something (ie, is it a value?) I use '_' as a variable name to indicate I don't care for it's precise name/value. It's just a placeholder.string hasDataMember( T )(string M ) { return " __traits(compiles, { Test t; auto _ = t.D; // reading t.M t." ~ M ~ " = t." ~ M ~ "; // assign to t.M })"; }What is the purpose of "auto _ = t.D;" ?Well that was the problem! I was still using 2.060 which seems to have the bug. Now that I've moved to 2.061 everything works as expected (your implementation and mine). Thanks, Olivier.Using dmd 2.060 and command line "rdmd test.d", I get an assertion fail on both calls that check for a member "init" whether for int or Test.Using 2.061 here.
Jan 28 2013
Ark, typo, sorry about that.Just testing whether t.M can be assigned to something (ie, is it a value?) I use '_' as a variable name to indicate I don't care for it's precise name/value. It's just a placeholder.Ok, I must be missing something, why "t.D" or is this just a typo?And secondly wouldn't "x.M = y.M" take care of checking that the member can be read and written to ?t.M = t.M? Yes, you're right.Also, doesn't your solution require T to be default constructible ?Yes, but if a type is not default-constructible, I don't know how to test it. Maybe testing on typeof(T.M)?Great!Using 2.061 here.Well that was the problem! I was still using 2.060 which seems to have the bug. Now that I've moved to 2.061 everything works as expected (your implementation and mine).
Jan 28 2013
On Monday, 28 January 2013 at 20:21:19 UTC, Philippe Sigaud wrote:My solution to that was to try and compile the definition of a delegate : __traits( compiles, "( ref T x ){ x." ~ M ~ " = x." ~ M ~ "; }" ); I had originally provided two arguments to the delegate (using "( ref T x, ref T y){ x.M = y.M; }") to avoid self assignment but It's not necessary. This seems to work as it returns the right result and does not require T to be default constructible. Just thought of the fact that I didn't try this with a class (I don't know if you're allowed to write "ref T x" if T == class), but you can always specialize hasDataMember with constraints or use static if.Ark, typo, sorry about that.Just testing whether t.M can be assigned to something (ie, is it a value?) I use '_' as a variable name to indicate I don't care for it's precise name/value. It's just a placeholder.Ok, I must be missing something, why "t.D" or is this just a typo?And secondly wouldn't "x.M = y.M" take care of checking that the member can be read and written to ?t.M = t.M? Yes, you're right.Also, doesn't your solution require T to be default constructible ?Yes, but if a type is not default-constructible, I don't know how to test it. Maybe testing on typeof(T.M)?Many thanks for your help on this again.Great!Using 2.061 here.Well that was the problem! I was still using 2.060 which seems to have the bug. Now that I've moved to 2.061 everything works as expected (your implementation and mine).
Jan 29 2013