Read conditional function parameters during compile time using __traits

ag0aep6g via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Wed Jun 21 13:48:52 PDT 2017


On 06/21/2017 09:39 PM, timvol wrote:
>      size_t calcLength(ubyte ubFuncCode)() if ( ubFuncCode == 1 )
>      {
>          return 10; // More complex calculated value
>      }
> 
>      size_t calcLength(ubyte ubFuncCode)() if ( ubFuncCode == 2 )
>      {
>          return 20; // More complex calculated value
>      }
> 
>      size_t calcLength(ubyte ubFuncCode)() if ( ubFuncCode == 3 )
>      {
>          return 30; // More complex calculated value
>      }
[...]
> But... how can I execute these functions? I mean, calling 
> doCalcLength(1) function says "Variable ubFuncCode cannot be read at 
> compile time". So my idea is to create an array during compile time 
> using traits (e.g. __traits(allMembers)) and to check this later during 
> runtime. For illustration purposes something like this:
> 
> --> During compile time:
> 
> void function()[ubyte] calcLengthArray;
> 
> auto tr = __traits(allMembers, example);
> foreach ( string s; tr )
> {
>      calcLengthArray[__trait(get<ubFuncCode>, s)] = s;
> }

As far as I know, there's no way to get the ubFuncCode from the 
constraints. In order to figure out which values are valid, you have to 
try them all. Which is actually doable for a ubyte:

----
size_t function()[ubyte] calcLengthArray;
static this()
{
     import std.meta: aliasSeqOf;
     import std.range: iota;
     foreach (ubFuncCode; aliasSeqOf!(iota(ubyte.max + 1)))
     {
         static if (is(typeof(&calcLength!ubFuncCode)))
         {
             calcLengthArray[ubFuncCode] = &calcLength!ubFuncCode;
         }
     }
}
----

Using a static constructor instead of direct initialization, because you 
can't initialize a static associative array directly.

> --> During runtime:
> 
> size_t doCalcLength(ubyte ubFuncCode)
> {
>      auto length = 0;
> 
>      if ( ubFuncCode in calcLengthArray )
>      {
>          length = calcLengthArray[ubFuncCode]!(ubFuncCode)();
>      }
> 
>      return length;
> }

If you can accept hard-coding the range of ubFuncCode values here (and 
if there are no holes), then you can generate a switch that calls the 
correct calcLength version:

----
     import std.meta: aliasSeqOf;
     import std.range: iota;
     enum min = 1;
     enum max = 3;
     sw: switch (ubFuncCode)
     {
         foreach (code; aliasSeqOf!(iota(min, max + 1)))
         {
             case code:
             length = calcLength!code();
             break sw;
         }
         default: throw new Exception("unexpected ubFuncCode");
     }
----

Instead of hard-coding the range, you could also do the same here as 
above when filling calcLengthArray: loop over all ubyte values and 
figure out which ones are valid with a `static if`.

> I hope everyone knows what I want to do :). But... does anyone know how 
> I can realize that? I don't want to use a switch/case structure because 
> the calcLength() functions can be very complex and I've over 40 
> different function codes. So, I think the best approach is to use 
> something similar to the one I described.

I don't see how you're reducing the complexity here. You have the same 
code, just spread over 40 functions, plus the extra code to make it 
work. From what I see, I'd prefer the hand-written switch.


More information about the Digitalmars-d-learn mailing list