Jump to page: 1 2
Thread overview
Ambiguity issue with expanding and evaluating single template type parameter enums
Dec 27, 2021
data pulverizer
Dec 27, 2021
data pulverizer
Dec 27, 2021
Adam Ruppe
Dec 28, 2021
data pulverizer
Dec 28, 2021
Paul Backus
Dec 28, 2021
data pulverizer
Dec 28, 2021
Paul Backus
Dec 28, 2021
data pulverizer
Dec 28, 2021
Adam Ruppe
Dec 27, 2021
Adam Ruppe
Dec 27, 2021
data pulverizer
Dec 27, 2021
data pulverizer
Dec 27, 2021
data pulverizer
December 27, 2021

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

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

December 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.

December 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.

December 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.

December 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.

December 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....

December 28, 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:

>

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.

December 28, 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:

enum instantiate(string type, string expr) = type ~ "(" ~ expr ~ ")";
pragma(msg, instantiate!("RVector!(SEXPTYPE.REALSXP)", "x"));
December 28, 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.

« First   ‹ Prev
1 2