Using the functions "map" and "any" on tuples in compile time.

Harry Gillanders ayjsy47yyz8 at temp.mailbox.org
Sun Apr 12 12:42:40 UTC 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.


More information about the Digitalmars-d-learn mailing list