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