www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Read conditional function parameters during compile time using

reply timvol <timvol unknownmailaddress.com> writes:
Hi! I've a simple array of bytes I received using sockets. What I 
want to do is to calculate the target length of the message. So, 
I defined a calcLength() function for each function code (it's 
the first byte in my array). My problem is that I defined the 
calcLength() function using conditions so that each calcLength 
should be called depending on the respective function code, see 
below:

module example;

private
{
     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
     }
}

size_t doCalcLength(ubyte ubFuncCode)
{
     return calcLength!(ubFuncCode)();
}

int main()
{
     doCalcLength(1);
     return 0;
}

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;
}

--> During runtime:

size_t doCalcLength(ubyte ubFuncCode)
{
     auto length = 0;

     if ( ubFuncCode in calcLengthArray )
     {
         length = calcLengthArray[ubFuncCode]!(ubFuncCode)();
     }

     return length;
}

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.
Jun 21
next sibling parent Eugene Wissner <belka caraus.de> writes:
On Wednesday, 21 June 2017 at 19:39:14 UTC, timvol wrote:
 Hi! I've a simple array of bytes I received using sockets. What 
 I want to do is to calculate the target length of the message. 
 So, I defined a calcLength() function for each function code 
 (it's the first byte in my array). My problem is that I defined 
 the calcLength() function using conditions so that each 
 calcLength should be called depending on the respective 
 function code, see below:

 module example;

 private
 {
     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
     }
 }

 size_t doCalcLength(ubyte ubFuncCode)
 {
     return calcLength!(ubFuncCode)();
 }

 int main()
 {
     doCalcLength(1);
     return 0;
 }

 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;
 }

 --> During runtime:

 size_t doCalcLength(ubyte ubFuncCode)
 {
     auto length = 0;

     if ( ubFuncCode in calcLengthArray )
     {
         length = calcLengthArray[ubFuncCode]!(ubFuncCode)();
     }

     return length;
 }

 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.
Let us to look at your function: size_t calcLength(ubyte ubFuncCode)() if ( ubFuncCode == 1 ) { return 10; // More complex calculated value } This function accepts only one template parameter and no other parameters. Template parameter should be known at compile time. You can't pass a value read from socket, because you can read from socket only at runtime. It is what the error message says. You calls such function as follows: calcLength!1() calcLength!2() and so on. Your doCalcLength won't work for the same reason. You try to pass "ubFuncCode" known at runtime as a template parameter, you will get the same error. You can try to instantiate all calcLength overloads and save them in calcLengthArray at some index just like you already do, but without any template parameters. The call would look something like: length = calcLengthArray[ubFuncCode](); But it is simplier and shorter just to use a switch statement: switch (ubFuncCode) { case 1: Do what calcLength!1() would do break; case 2: Do what calcLength!2() would do break; default: break; }
Jun 21
prev sibling parent reply ag0aep6g <anonymous example.com> writes:
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.
Jun 21
parent timvol <timvol unknownmailaddress.com> writes:
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?
Jun 23