www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Template to retrieve compile-time enum member from run-time enum

reply Timoses <timosesu gmail.com> writes:
The following should depict what I'm trying to achieve:


```
import std.stdio;

enum menum { A, B, C }

void main()
{
    foo(menum.B);
}

void foo(menum e)
{
     // Not possible!!!!
     // run time variable 'e' in conjunction with template 'Temp'
     writeln(Temp!(GetMenum(e)));
}

static int i = 0;

template Temp(menum e)
{
     // ... do stuff
     shared static this()
     {
         static if (e == menum.A)
             i = 1;
     }

     import std.meta : Alias;
     alias Temp = Alias!i;
}

// Trying to return a compile-time variable with a function... 
Not like this...
// I don't see a way to pass in the run-time variable without a 
function..
template GetMenum()
{
     menum GetMenum(menum e)
     {
         import std.traits : EnumMembers;
         static foreach(mem; EnumMembers!menum)
             if (mem == e)
                 return mem;

         return menum.A;
     }
}
```

yields: Error: variable e cannot be read at compilte time

However, if I replace foo with
```
void foo(menum e)
{
     import std.traits : EnumMembers;
     static foreach(mem; EnumMembers!menum)
         if (mem == e)
             writeln(Temp!(GetMenum(mem)));
}
```
it works..

Is it possible to use a template to place the "static foreach" 
looping to find the correct enum value into? Like I am trying in 
the initial "draft" GetMenum?
Apr 26 2018
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Thursday, 26 April 2018 at 16:10:16 UTC, Timoses wrote:
 Is it possible to use a template to place the "static foreach" 
 looping to find the correct enum value into? Like I am trying 
 in the initial "draft" GetMenum?
As the compiler says, the value of `e` is not known at compile-time. In order to correctly instantiate the template with that value, all possible instantiations must be instantiated, and the correct one chosen by a static foreach, just like you do. The only step you're missing is the template needs to be instantiated inside the static foreach, like this: auto instantiateWith(alias Fn, T)(T x) if (is(T == enum)) { import std.traits : EnumMembers; switch (x) { static foreach (e; EnumMembers!T) case e: return Fn!e; default: assert(false); } } enum menum { A, B, C } template Temp(menum m) { enum Temp = m.stringof; } unittest { menum m = menum.A; import std.stdio; assert(instantiateWith!Temp(m) == Temp!(menum.A)); m = menum.B; assert(instantiateWith!Temp(m) == Temp!(menum.B)); m = menum.C; assert(instantiateWith!Temp(m) == Temp!(menum.C)); } -- Simen
Apr 26 2018
next sibling parent Timoses <timosesu gmail.com> writes:
On Thursday, 26 April 2018 at 16:46:11 UTC, Simen Kjærås wrote:
 The only step you're missing is the template needs to be 
 instantiated inside the static foreach, like this:

 auto instantiateWith(alias Fn, T)(T x)
 if (is(T == enum))
 {
     import std.traits : EnumMembers;
     switch (x)
     {
         static foreach (e; EnumMembers!T)
             case e:
                 return Fn!e;
         default:
             assert(false);
     }
 }

 enum menum { A, B, C }

 template Temp(menum m)
 {
     enum Temp = m.stringof;
 }
Ah thanks!! I struggled a bit with finding out how to actually it in my use case. I just didn't see the pattern: ``` instantiateWith!<ANY-Template-That-should-be-instantiated-with-run-time-Var>(<run-time-var>) ``` Thank you!
Apr 27 2018
prev sibling parent reply Timoses <timosesu gmail.com> writes:
Bumped across another problem : /

```
import std.stdio;

enum menum { A, B, C }

void main()
{
    foo(menum.A);
}

void foo(menum e)
{
     writeln(instantiateWith!Temp(e));
}

auto instantiateWith(alias Fn, T)(T x)
     if (is(T == enum))
{
     switch (x)
     {
         import std.traits : EnumMembers;
         static foreach (e; EnumMembers!T)
             case e:
                 return Fn!e;
         default:
             assert(false);
     }
}

template Temp(menum e)
{
     struct TempStruct { uint i; };
     TempStruct Temp;

     shared static this()
     {
         static if (e == menum.A)
             Temp.i = 3;
     }

}
```

now returns:
source\app.d(25,17): Error: mismatched function return type 
inference of `TempStruct` and `TempStruct`
source\app.d(12,33): Error: template instance 
`app.instantiateWith!(Temp, menum)` error instantiating
dmd failed with exit code 1.

It's the same return type... so why the error?
Apr 27 2018
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Friday, 27 April 2018 at 13:27:45 UTC, Timoses wrote:
 Bumped across another problem : /

 ```
 import std.stdio;

 enum menum { A, B, C }

 void main()
 {
    foo(menum.A);
 }

 void foo(menum e)
 {
     writeln(instantiateWith!Temp(e));
 }

 auto instantiateWith(alias Fn, T)(T x)
     if (is(T == enum))
 {
     switch (x)
     {
         import std.traits : EnumMembers;
         static foreach (e; EnumMembers!T)
             case e:
                 return Fn!e;
         default:
             assert(false);
     }
 }

 template Temp(menum e)
 {
     struct TempStruct { uint i; };
     TempStruct Temp;

     shared static this()
     {
         static if (e == menum.A)
             Temp.i = 3;
     }

 }
 ```

 now returns:
 source\app.d(25,17): Error: mismatched function return type 
 inference of `TempStruct` and `TempStruct`
 source\app.d(12,33): Error: template instance 
 `app.instantiateWith!(Temp, menum)` error instantiating
 dmd failed with exit code 1.

 It's the same return type... so why the error?
That's an unfortunate error message. The problem is TempStruct is defined inside the Temp template. In the same way that struct Foo(T) {} is different for Foo!int and Foo!string, TempStruct is a different type for Temp!(menum.A) and Temp!(menum.B). The solution is to move TempStruct outside the template: struct TempStruct { uint i; } template Temp(menum e) { TempStruct Temp; shared static this() { static if (e == menum.A) Temp.i = 3; } } -- Simen
Apr 27 2018
parent reply Timoses <timosesu gmail.com> writes:
On Friday, 27 April 2018 at 13:39:22 UTC, Simen Kjærås wrote:
 That's an unfortunate error message. The problem is TempStruct 
 is defined inside the Temp template. In the same way that 
 struct Foo(T) {} is different for Foo!int and Foo!string, 
 TempStruct is a different type for Temp!(menum.A) and 
 Temp!(menum.B).

 The solution is to move TempStruct outside the template:

 struct TempStruct { uint i; }

 template Temp(menum e)
 {
     TempStruct Temp;
     shared static this()
     {
         static if (e == menum.A)
             Temp.i = 3;
     }
 }

 --
   Simen
Ty. I figured that's the reason. I still can't quite get my head around "Why?" though. `instantiateWith` gets called in three variations (menum.A, menum.B and menum.C). This causes instantiateWith to return TempStruct for each case of Temp... However, I was under the impression that a templated function will exist multiple (in this case 3) times, so the return type should be allowed to be different?!
Apr 27 2018
parent reply Alex <sascha.orlov gmail.com> writes:
On Friday, 27 April 2018 at 13:43:47 UTC, Timoses wrote:
 `instantiateWith` gets called in three variations (menum.A, 
 menum.B and menum.C). This causes instantiateWith to return 
 TempStruct for each case of Temp...

 However, I was under the impression that a templated function 
 will exist multiple (in this case 3) times, so the return type 
 should be allowed to be different?!
I think, because the enum value is a runtime parameter for instantiateWith, only a single variation of it exists. And this cannot have different return types. So... "alias Fn" and "T" stay the same. The value of x varies. https://run.dlang.io/is/jX4Ybh states the same.
Apr 27 2018
parent Timoses <timosesu gmail.com> writes:
On Friday, 27 April 2018 at 14:33:36 UTC, Alex wrote:
 On Friday, 27 April 2018 at 13:43:47 UTC, Timoses wrote:
 `instantiateWith` gets called in three variations (menum.A, 
 menum.B and menum.C). This causes instantiateWith to return 
 TempStruct for each case of Temp...

 However, I was under the impression that a templated function 
 will exist multiple (in this case 3) times, so the return type 
 should be allowed to be different?!
I think, because the enum value is a runtime parameter for instantiateWith, only a single variation of it exists. And this cannot have different return types. So... "alias Fn" and "T" stay the same. The value of x varies. https://run.dlang.io/is/jX4Ybh states the same.
Ah yes, thanks. I see it now.
Apr 28 2018