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.

Reply via email to