digitalmars.D.learn - Using the functions "map" and "any" on tuples in compile time.
- realhet (20/20) Apr 12 2020 Hello, anyone can help me make this better?
- Harry Gillanders (60/81) Apr 12 2020 Using a compile-time tuple as a range is easy, turn it into an
- realhet (6/7) Apr 12 2020 I only remembered the __traits(identifier...), but completely
Hello, anyone can help me make this better? The functionality I want to achieve is: While serializing the fields of a struct, I want it to check the STORED UDA on every field. If there is no fields are marked with STORED, that means every field must be serialized. Otherwise only the marked one. I had problems using map and any, so I come up with this lame foreach version: It works, but I think it does it in runtime. bool anySTORED = false; static foreach(fieldName; FieldNameTuple!T) mixin("anySTORED |= hasUDA!(data.*, STORED);".replace("*", fieldName)); static foreach(fieldName; FieldNameTuple!T){{ mixin("const thisSTORED = hasUDA!(data.*, STORED);".replace("*", fieldName)); if(thisSTORED || !anySTORED) mixin("streamAppend_json!(dense, fieldName)(st, data.*, nextIndent);".replace("*", fieldName)); }} Thanks in advance! (ps: I just love string mixins, I know :D)
Apr 12 2020
On Sunday, 12 April 2020 at 11:17:39 UTC, realhet wrote:Hello, anyone can help me make this better? The functionality I want to achieve is: While serializing the fields of a struct, I want it to check the STORED UDA on every field. If there is no fields are marked with STORED, that means every field must be serialized. Otherwise only the marked one. I had problems using map and any, so I come up with this lame foreach version: It works, but I think it does it in runtime. bool anySTORED = false; static foreach(fieldName; FieldNameTuple!T) mixin("anySTORED |= hasUDA!(data.*, STORED);".replace("*", fieldName)); static foreach(fieldName; FieldNameTuple!T){{ mixin("const thisSTORED = hasUDA!(data.*, STORED);".replace("*", fieldName)); if(thisSTORED || !anySTORED) mixin("streamAppend_json!(dense, fieldName)(st, data.*, nextIndent);".replace("*", fieldName)); }} Thanks in advance! (ps: I just love string mixins, I know :D)Using a compile-time tuple as a range is easy, turn it into an array via an array literal (surround it in square bracket), e.g. struct Foo { int a; int b; } pragma(msg, [FieldNameTuple!Foo].map!(f => f ~ "_").array()); However, if you were to try that with `any` for `hasUDA`, wherein the arguments for `any`'s predicate are used for `hasUDA`'s template parameters, you'll find that it won't compile. That's because `any`'s predicate is a runtime function, executed at compile-time via CTFE, so the argument technically isn't known at compile-time for the `hasUDA` template, e.g. struct Foo { int a; int b; } enum STORED; enum bool anyStored = [FieldNameTuple!Foo].any!( f => hasUDA!(__traits(getMember, Foo, f), STORED) ); The solution to that is to define a template predicate, and use std.meta.anySatisfy, instead of `any`. Which would accomplish what you want to do, with something like so: string serialiseFields (T) (auto ref T instance) { enum bool hasStored (string fieldName) = hasUDA!(__traits(getMember, T, fieldName), STORED); enum fields = FieldNameTuple!T; static if (anySatisfy!(hasStored, fields)) { enum fieldsToSerialise = Filter!(hasStored, fields); } else { enum fieldsToSerialise = fields; } string serialise (string name, T) (auto ref T value) { return format!(name ~ " = %s")(value); } string serialised; static foreach (field; fieldsToSerialise) { serialised ~= serialise!field(__traits(getMember, instance, field)) ~ "\n"; } return serialised; } --- This source code in this reply is licensed under the terms of Creative Commons CC0 1.0.
Apr 12 2020
On Sunday, 12 April 2020 at 12:42:40 UTC, Harry Gillanders wrote:On Sunday, 12 April 2020 at 11:17:39 UTC, realhet wrote:I only remembered the __traits(identifier...), but completely forgot about the getMember. And I never heard of anySatisfy. My JSON serializer is beautiful now. Thank You very much!
Apr 12 2020