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