Thread overview
Using the functions "map" and "any" on tuples in compile time.
Apr 12, 2020
realhet
Apr 12, 2020
Harry Gillanders
Apr 12, 2020
realhet
April 12, 2020
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)
April 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.
April 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!