Search elemnt in Compile-time Argument List of strings

Steven Schveighoffer via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Tue Jul 26 13:18:48 PDT 2016


On 7/26/16 3:30 PM, ParticlePeter wrote:
> I want to generate one function for any struct data member, but also
> want to be able to skip few of the members. The first part works, but I
> have some trouble with the skipping.
>
> I pass the struct type and a Compile-time Argument List of strings as
> template arguments to a template function, list all members of the
> struct and compare each member to each element of the List. If the
> member is in the List skip processing of that member. Pretty straight
> forward ... should be.
>
> // First approach doesn't work:
> // Error: variable skip cannot be read at compile time
> void processMember( T, ignore... )() {
>   foreach( member; __traits( allMembers, T )) {
>     bool skip = false;
>     foreach( arg; ignore )
>       skip = skip || ( arg == member );
>
>     static if( !skip ) {
>       // process member here, generate e.g. setter function as string mixin
>     }
>   }
> }
>
> // Second approach, get warnings for every skipped member
> // and every line after the return statement:
> // Warning: statement is not reachable
> void processMember( T, ignore... )() {
>   foreach( member; __traits( allMembers, T )) {
>     foreach( arg; ignore )
>       static if( arg == member )
>         return;
>     // process member here, generate e.g. setter function as string mixin
>   }
> }
>
> So how can I achieve my goal the right way?

You are doing it *almost* right.

What you need to remember is what is compile time, and what is runtime. 
In order to declare something is readable at compile-time, you need to 
have an expression that only involves compile-time constants. This means 
you need to process *all* the ignore's at once, or process the "end of 
the loop" after you haven't found it. Here is one way to do it:


void processMember( T, ignore... )() {
   foreach( member; __traits( allMembers, T )) { // this is a 
compile-time list, so it's a static foreach.
     foreach(i, arg; ignore ){ // i is the index into the ignore tuple
       static if( arg == member ) break; // break out of the foreach 
loop, need to ignore it.
       else static if(i + 1 == arg.length) // this is the last element!
       {
       // process member here, generate e.g. setter function as string mixin
       }
     }
   }
}

Another way is to use std.meta.anySatisfy 
(http://dlang.org/phobos/std_meta.html#.anySatisfy), which can apply a 
template to each element in a compile-time list to see if any match:

template skipper(string target)
{
    enum shouldSkip(string s) = (s == target);
}

// replace your bool skip = ... with this:
enum skip = anySatisfy!(skipper!(member).shouldSkip, ignore);

It's a bit weird to work on these compile-time things, but they are so 
cool when you look at what is available in std.meta and std.traits :)

-Steve


More information about the Digitalmars-d-learn mailing list