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