Read conditional function parameters during compile time using __traits

timvol via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Fri Jun 23 12:08:03 PDT 2017


On Wednesday, 21 June 2017 at 20:48:52 UTC, ag0aep6g wrote:
> 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.

Thanks in advance! I finally solved my problem by adjust my 
message structure. So I'm now sending the length of the message, 
followed by the function code and the data.

I finally created different functions and annotated them with 
@FunctionCode(1), e.g.:

@FunctionCode(1)
void func1() { ... }

@FunctionCode(2)
void func2() { ... }

But I now ran into the next problem.
I'm creating an array containing these function using traits:

#1: foreach ( cb; __traits(allMembers, test))
#2: {
#3:     static if ( __traits(isStaticFunction, 
__traits(getMember, test, cb)) )
#4: {
#5: // do something
#6: }
#7: }

But I'm getting the following error on line #3: error: undefined 
identifier '_D11TypeInfo_ya6__initZ'

I figured out that some other users are also had this error. 
Unfortunately, the error wasn't solved perfectly as far as I 
know. So, how can I resolve the eror?


More information about the Digitalmars-d-learn mailing list