digitalmars.D.learn - Ambiguity issue with expanding and evaluating single template type
- data pulverizer (45/45) Dec 27 2021 Hello,
- data pulverizer (17/19) Dec 27 2021 ... an equivalent mixin error would be
- Adam Ruppe (5/8) Dec 27 2021 What is MyType?
- data pulverizer (20/29) Dec 27 2021 Sorry the example is a bit contrived but basically I'm generating
- Paul Backus (16/29) Dec 27 2021 The correct answer here is, "don't use `T.stringof` to generate
- data pulverizer (8/19) Dec 27 2021 Well the code needs to be responsive from parameter types `T`
- Paul Backus (8/24) Dec 27 2021 I see. So, you need access to the type as a type in order to
- data pulverizer (7/14) Dec 27 2021 One possibility is to generate a collection of compile time
- Adam Ruppe (7/10) Dec 27 2021 You almost never actually need types as strings. I'm almost
- Adam Ruppe (12/15) Dec 27 2021 The rule for !(args) is of you leave the parenthesis off, it only
- data pulverizer (3/5) Dec 27 2021 Will the above `mixin` example suffice? It expands to the code
- data pulverizer (3/5) Dec 27 2021 I think the only thing to do for now is probably for me to
- data pulverizer (26/28) Dec 27 2021 It would look something like this:
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
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
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
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: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.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
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
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
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: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.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
On Tuesday, 28 December 2021 at 00:57:27 UTC, Paul Backus wrote: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.```d enum instantiate(string type, string expr) = type ~ "(" ~ expr ~ ")"; pragma(msg, instantiate!("RVector!(SEXPTYPE.REALSXP)", "x")); ```
Dec 27 2021
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
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
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
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
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