Variadic template parameters, refactor a long chain of static if's to 'functions'.

Adam D. Ruppe destructionator at gmail.com
Tue Jun 11 13:22:26 UTC 2019


On Tuesday, 11 June 2019 at 09:26:56 UTC, realhet wrote:
>   static bool processId(bool captureIntId, alias r, alias a)(){
>     mixin("alias ta = typeof("~a.stringof~");");

As I have been saying a lot, mixin and stringof should almost 
never be used together. You could write this a lot easier:

alias ta = typeof(a);

no need for mixin at all.


> Error: template instance processId!(true, id, _param_1) 
> processId!(true, id, _param_1) is nested in both Args and btn

So Args is a runtime thing, but you are trying to use it in 
compile time. The compiler can handle one layer of this - it 
basically inlines a runtime function when you do that - but with 
more layers it gets lost.

What you'll need to do is to kinda embrace this and have a helper 
struct that returns the stuff by value. To avoid holding all the 
possible types, pass what you want to it as type arguments inside 
the function.

Check this out:


---

struct ID { int val; }
struct enabled { bool val; }
struct disabled { bool val; }

struct PossibleArguments(T...) {

         this(Args...)(Args args) {
                 static foreach(arg; args) {{
                         enum idx = TindexOf!(typeof(arg));
                         static if(idx >= 0)
                                 contents[idx] = arg;
                         else
                                 static assert(0, "Unusable arg " 
~ typeof(arg).stringof);
                 }}
         }

         private T contents;
         private static int TindexOf(What)() {
                 static foreach(idx, t; T) {
                         // you could use your isSame here
                         static if(is(t == What))
                                 return cast(int) idx;
                 }
                 return -1;
         }

         auto opDispatch(string n)() {
                 static foreach(idx, item; T)
                         if(item.stringof == n)
                                 return contents[idx].val;
                 assert(0);
         }
}

void btn(T...)(string params, T args) {
      // here's the usage: list args I want to support as CT,
      // then pass the other args as runtime
         auto r = PossibleArguments!(
                 ID,
                 enabled
         )(args);

         import std.stdio;
         writeln(r.opDispatch!"ID"); // or r.ID, but while 
debugging it helps to explicitly all r.opDispatch so you get the 
full error instead of "no such property"
         writeln(r.enabled);
}


void main() {
         // I enabled the first two, but not disabled, so this is 
an error
         //btn("ignored", ID(5), enabled(true), disabled(false));

         // but this works
         btn("ignored", ID(5), enabled(true));

         // and it can be reordered/ignored/etc
         btn("ignored", enabled(false));
}

---



So now you avoid the list of ifs thanks to that TindexOf helper 
thing while still getting the helpful user error message, and the 
opDispatch can make feel like you are accessing by name inside.

Extending it to other types is an exercise for you :) 
Particularly, the event ones will need a different approach of 
some sort.

I would suggest actually putting them in a wrapper struct so you 
can  still use this cool name trick.

But alternatively, you could special-case them in the two loops, 
like always knowing void delegate() is going to be named onClick. 
I can help you solve this later too if you wanna go that way, I 
just g2g right now lol.


More information about the Digitalmars-d-learn mailing list