Thread overview
Template to retrieve compile-time enum member from run-time enum member?
Apr 26, 2018
Timoses
Apr 26, 2018
Simen Kjærås
Apr 27, 2018
Timoses
Apr 27, 2018
Timoses
Apr 27, 2018
Simen Kjærås
Apr 27, 2018
Timoses
Apr 27, 2018
Alex
Apr 28, 2018
Timoses
April 26, 2018
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?
April 26, 2018
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
April 27, 2018
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!
April 27, 2018
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?
April 27, 2018
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
April 27, 2018
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?!
April 27, 2018
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.
April 28, 2018
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.