www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Ambiguity issue with expanding and evaluating single template type

reply data pulverizer <data.pulverizer gmail.com> writes:
Hello,

I'm generating code using mixins and one of my mixins expands to 
something like this:

```
adder(MyType!MyEnum.INTEGER(), MyType!MyEnum.STRING());
```

`MyType!MyEnum.STRING` is generated with `T.stringof `. I get the 
error:

```
Error: template instance `MyType!(MyEnum)` does not match 
template declaration `MyType(MyEnum type)
```

and if I manually amend the code to this:

```
adder(MyType!(MyEnum.INTEGER)(), MyType!(MyEnum.STRING)());
```

It runs fine. It looks like the ambiguity of UFCS and type is 
messing things up. This is a simplified example. Since the code 
is being generated automatically in many places I can't go round 
adding the brackets.

A simplified functional example is given below:

```
import std.stdio: writeln;

enum MyEnum
{
   DOUBLE = 0,
   STRING = 1,
   INTEGER = 2
}

struct MyType(MyEnum type)
{}

auto getValue(T: MyType!U, alias U)(T x)
{
   return U;
}

auto adder(T, U)(T x, U y)
{
   return getValue(x) + getValue(y);
}

void main()
{
   writeln("instance: ", adder(MyType!MyEnum.INTEGER(), 
MyType!MyEnum.STRING()));
}

```
Dec 27 2021
next sibling parent reply data pulverizer <data.pulverizer gmail.com> writes:
On Monday, 27 December 2021 at 21:05:51 UTC, data pulverizer 
wrote:
 Hello, ...
 
... an equivalent mixin error would be ``` //... alias DOUBLE = MyEnum.DOUBLE; alias STRING = MyEnum.STRING; alias INTEGER = MyEnum.INTEGER; void main() { alias T = MyType!(INTEGER); alias U = MyType!(STRING); enum code = "writeln(\"instance: \", adder(" ~ T.stringof ~ "(), " ~ U.stringof ~ "()" ~ "));"; mixin(code); } ```
Dec 27 2021
parent reply Adam Ruppe <destructionator gmail.com> writes:
On Monday, 27 December 2021 at 21:21:30 UTC, data pulverizer 
wrote:
   alias T = MyType!(INTEGER);
What is MyType?
   enum code = "writeln(\"instance: \", adder(" ~
               T.stringof ~ "(), " ~ U.stringof ~ "()" ~ "));";
And why is this a string mixin instead of a plain simple function? prolly need more context....
Dec 27 2021
parent reply data pulverizer <data.pulverizer gmail.com> writes:
On Monday, 27 December 2021 at 23:04:40 UTC, Adam Ruppe wrote:
 On Monday, 27 December 2021 at 21:21:30 UTC, data pulverizer 
 wrote:
   alias T = MyType!(INTEGER);
What is MyType?
   enum code = "writeln(\"instance: \", adder(" ~
               T.stringof ~ "(), " ~ U.stringof ~ "()" ~ "));";
And why is this a string mixin instead of a plain simple function? prolly need more context....
Sorry the example is a bit contrived but basically I'm generating a whole bunch of code using string mixins. The types I'm generating are a template type I've constructed for R's SEXP, so that my wrapped numeric vector (struct) type is denoted `RVector!(REALSXP)`. But `alias REALSXP = SEXPTYPE.REALSXP` where `SEXPTYPE` is an `enum`. So if I start using `T.stringof` where `T = RVector!(SEXPTYPE.REALSXP)` to generate code it starts to create chaos because `T.stringof = "RVector!SEXPTYPE.REALSXP"`, so if I'm trying to convert or instantiate a type using `T.stringof ~ "(x)"`, I'll get `RVector!SEXPTYPE.REALSXP(x)` which gives an error, and various types like this can occur many times in a script. The new template allows me to safely paste the type and get what I want `RVector!(SEXPTYPE.REALSXP)(x)`. There are various requirements, sometimes I have to cast or type convert, so I **need** the type to paste correctly and explicitly. Which is what the `safe_stringof` template does for my baby example - the same methodology will work just as well for my `RVector` code.
Dec 27 2021
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 28 December 2021 at 00:13:13 UTC, data pulverizer 
wrote:
 The types I'm generating are a template type I've constructed 
 for R's SEXP, so that my wrapped numeric vector (struct) type 
 is denoted `RVector!(REALSXP)`. But `alias REALSXP = 
 SEXPTYPE.REALSXP` where `SEXPTYPE` is an `enum`.

 So if I start using `T.stringof` where `T = 
 RVector!(SEXPTYPE.REALSXP)` to generate code it starts to 
 create chaos because `T.stringof = "RVector!SEXPTYPE.REALSXP"`, 
 so if I'm trying to convert or instantiate a type using 
 `T.stringof ~ "(x)"`, I'll get `RVector!SEXPTYPE.REALSXP(x)` 
 which gives an error, and various types like this can occur 
 many times in a script. The new template allows me to safely 
 paste the type and get what I want 
 `RVector!(SEXPTYPE.REALSXP)(x)`.
The correct answer here is, "don't use `T.stringof` to generate code." The result of `.stringof` is completely implementation-defined, may change arbitrarily between compiler releases, and is not even guaranteed to be valid D code in the first place. You should not rely on it unless you have literally no other choice. In this case, the simplest solution is to have your code generator accept a string as its input, rather than a type. For example: ```d enum instantiate(string type, string expr) = type ~ "(" ~ expr ~ ")"; pragma(msg, instantiate!("RVector!(SEXPTYPE.REALSXP)", "x")); ```
Dec 27 2021
parent reply data pulverizer <data.pulverizer gmail.com> writes:
On Tuesday, 28 December 2021 at 00:32:03 UTC, Paul Backus wrote:
 The result of `.stringof` is completely implementation-defined, 
 may change arbitrarily between compiler releases, and is not 
 even guaranteed to be valid D code in the first place.
Wow, I didn't know this.
 In this case, the simplest solution is to have your code 
 generator accept a string as its input, rather than a type. For 
 example:

 ```d
 enum instantiate(string type, string expr) = type ~ "(" ~ expr 
 ~ ")";
 pragma(msg, instantiate!("RVector!(SEXPTYPE.REALSXP)", "x"));
 ```
Well the code needs to be responsive from parameter types `T` generated from other code. I'm allowing the user to create functions select those they wish to access in R by UDA decorators in the D script which I then filter for and wrap the necessary functions generating any type conversion code I need at compile time to create functions callable in R.
Dec 27 2021
parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 28 December 2021 at 00:42:18 UTC, data pulverizer 
wrote:
 On Tuesday, 28 December 2021 at 00:32:03 UTC, Paul Backus wrote:
 In this case, the simplest solution is to have your code 
 generator accept a string as its input, rather than a type. 
 For example:

 ```d
 enum instantiate(string type, string expr) = type ~ "(" ~ expr 
 ~ ")";
 pragma(msg, instantiate!("RVector!(SEXPTYPE.REALSXP)", "x"));
 ```
Well the code needs to be responsive from parameter types `T` generated from other code. I'm allowing the user to create functions select those they wish to access in R by UDA decorators in the D script which I then filter for and wrap the necessary functions generating any type conversion code I need at compile time to create functions callable in R.
I see. So, you need access to the type as a type in order to reflect on it, but you also want it as a string in order to generate code. My guess is that you don't actually *need* to use string mixins for most of this, but I can't say for sure without seeing a more complete example.
Dec 27 2021
parent data pulverizer <data.pulverizer gmail.com> writes:
On Tuesday, 28 December 2021 at 00:57:27 UTC, Paul Backus wrote:
 ```d
 enum instantiate(string type, string expr) = type ~ "(" ~ 
 expr ~ ")";
 pragma(msg, instantiate!("RVector!(SEXPTYPE.REALSXP)", "x"));
 ```
One possibility is to generate a collection of compile time strings that denote the types and then to a comparison with the type something like `is(T == mixin(CString)`, where `CString = "RVector!(SEXPTYPE.REALSXP)"` to discover the correct string which I can then use to generate the code without having to use `T.stringof` anywhere in the code at all.
Dec 27 2021
prev sibling parent Adam Ruppe <destructionator gmail.com> writes:
On Tuesday, 28 December 2021 at 00:13:13 UTC, data pulverizer 
wrote:
 There are various requirements, sometimes I have to cast or 
 type convert, so I **need** the type to paste correctly and 
 explicitly.
You almost never actually need types as strings. I'm almost certain there's a better way for you to get the same work done. Have you tried just using T directly in your mixin? You can frequently just use the local name and skip the string getting entirely.
Dec 27 2021
prev sibling parent reply Adam Ruppe <destructionator gmail.com> writes:
On Monday, 27 December 2021 at 21:05:51 UTC, data pulverizer 
wrote:
 adder(MyType!MyEnum.INTEGER(), MyType!MyEnum.STRING());
The rule for !(args) is of you leave the parenthesis off, it only uses the next single token as the argument. So it will never include a dot; it is like you wrote `MyType!(MyEnum).INTEGER`. You might just always use the () in your generated code.... when you create that mixin string can't just just change the generator to put the () around it? Or is the stringof generating this? (Another reason why stringof is terrible and should never be used ever for anything.)
 `MyType!MyEnum.STRING` is generated with `T.stringof `. I get 
 the error:
if you can paste teh code where you generate this I can prolly show you a much easier way to do it. stringof sucks really hard.
Dec 27 2021
next sibling parent data pulverizer <data.pulverizer gmail.com> writes:
On Monday, 27 December 2021 at 21:31:03 UTC, Adam Ruppe wrote:
 if you can paste teh code where you generate this I can prolly 
 show you a much easier way to do it. stringof sucks really hard.
Will the above `mixin` example suffice? It expands to the code that I described.
Dec 27 2021
prev sibling parent reply data pulverizer <data.pulverizer gmail.com> writes:
On Monday, 27 December 2021 at 21:31:03 UTC, Adam Ruppe wrote:
 if you can paste teh code where you generate this I can prolly 
 show you a much easier way to do it. stringof sucks really hard.
I think the only thing to do for now is probably for me to construct a template that creates a proper string for this type.
Dec 27 2021
parent data pulverizer <data.pulverizer gmail.com> writes:
On Monday, 27 December 2021 at 22:52:58 UTC, data pulverizer 
wrote:
 I think the only thing to do for now is probably for me to 
 construct a template that creates a proper string for this type.
It would look something like this: ``` enum safe_stringof(T) = T.stringof; template safe_stringof(T: MyType!U, alias U) { enum string safe_stringof = "MyType!(" ~ U.stringof ~ ")"; } ``` So this ``` alias DOUBLE = MyEnum.DOUBLE; alias STRING = MyEnum.STRING; alias INTEGER = MyEnum.INTEGER; void main() { alias T = MyType!(INTEGER); alias U = MyType!(STRING); enum code = "writeln(\"instance: \", adder(" ~ safe_stringof!(T) ~ "(), " ~ safe_stringof!(U) ~ "()" ~ "));"; pragma(msg, code); } ``` Which works. Now back to my very late dinner.
Dec 27 2021