www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - mixin does not work as expected

reply rempas <rempas tutanota.com> writes:
I'm trying to use mixins and enums to "expand" code in place but 
the results are not what I expected and I'm getting an weird 
error. I have created the smallest possible example to reproduce 
the error and it is the following:

```
enum base_digit(string ten, string sixteen, string two, string 
eight) = `
   if (base == 10)      i = ` ~ ten ~ `;
   else if (base == 16) i = ` ~ sixteen ~ `;
   else if (base == 2)  i = ` ~ two ~ `;
   else if (base == 8)  i = ` ~ eight ~ `;
`;

enum overflow_check(string type) = `
   if (num == ` ~ type ~ `) {
     min_num = true;
     ++num;
   }
`;

enum type_check(string which_if, string type, string signed,
     string ten, string sixteen, string two, string eight) =
   which_if ~ `(is_same!(num, ` ~ type ~ `)) {
     mixin(base_digit!("` ~ ten ~ `", "` ~ sixteen
           ~ `", "` ~ two ~ `", "` ~ eight ~ `"));
     static if (` ~ signed ~ `) {
       mixin(overflow_check!"` ~ type ~ `.min"); }
   }
`;

void test(bool signed)(int num, int base) {
   static if (signed) {
     mixin(type_check!("static if", "i8", "true", "5", "4", "10", 
"5"));
     mixin(type_check!("else static if", "i16", "true", "7", "6", 
"18", "8"));
     mixin(type_check!("else static if", "i32", "true", "12", 
"10", "34", "13"));
     mixin(type_check!("else", "i64", "true", "21", "18", "66", 
"24"));
   }

   else {
     mixin(type_check!("static if", "u8", "false", "4", "3", "9", 
"4"));
     mixin(type_check!("else static if", "u16", "false", "6", "5", 
"17", "7"));
     mixin(type_check!("else static if", "u32", "false", "11", 
"9", "33", "12"));
     mixin(type_check!("else", "u64", "false", "21", "17", "65", 
"23"));
   }
}

extern (C) void main() {
   test!true(10, 16);
}
```

In my normal code, the "test" function is actually part of an 
emum that is expanded from another function (told you, the whole 
code will just be to complicated for anyone to read). In the 
example, trying to compile will give the following error:

```
test.d-mixin-27(27): Error: template instance `is_same!(num, i8)` 
template `is_same` is not defined
test.d-mixin-28(28): Error: found `else` without a corresponding 
`if`, `version` or `debug` statement
test.d-mixin-29(29): Error: found `else` without a corresponding 
`if`, `version` or `debug` statement
test.d-mixin-30(30): Error: found `else` without a corresponding 
`if`, `version` or `debug` statement
test.d(42): Error: template instance `test.test!true` error 
instantiating
```

Now as you can see, there are three times this error occurs in 
the expansion of the mixin. The error will go away if we comment 
the three mixins in the first "static if" branch (as in our 
example, "signed" is set to true so the first branch will get 
compiled). So from me understanding, when a mixin is expanded, 
the compiler checks the code from the mixin SEPARATELY and sees 
if it makes sense. So for that reason, it sees as there is only 
an "else static if" without a "static if" before and thus it 
gives us the error. Tho this doesn't seem right because how can 
it see the other variables like "num" or "base" that were not 
defined INSIDE the enum as its parameters? So it may be another 
sneaky bug that I can't understand. Or like it has happened to 
the past, it may be something obvious that I'm so blind to see 
and someone will tell me and I'll get embarrassed again (this is 
why I don't like asking only for help fixing bugs)...
Jan 05 2022
next sibling parent reply vit <vit vit.vit> writes:
On Wednesday, 5 January 2022 at 08:40:15 UTC, rempas wrote:
 I'm trying to use mixins and enums to "expand" code in place 
 but the results are not what I expected and I'm getting an 
 weird error. I have created the smallest possible example to 
 reproduce the error and it is the following:

 [...]
Try this: ```d pragma(msg, type_check!("static if", "i8", "true", "5", "4", "10", "5")); ``` Result: ```d static if(is_same!(num, i8)) { mixin(base_digit!("5", "4", "10", "5")); static if (true) { mixin(overflow_check!"i8.min"); } } ``` is_same - is undefined i8 - is undefined overflow_check!"i8.min" - ? This code with mixins is horrible :)
Jan 05 2022
parent reply rempas <rempas tutanota.com> writes:
On Wednesday, 5 January 2022 at 09:02:53 UTC, vit wrote:
 Try this:
 ```d
 pragma(msg, type_check!("static if", "i8", "true", "5", "4", 
 "10", "5"));
 ```

 Result:

 ```d
 static if(is_same!(num, i8)) {
     mixin(base_digit!("5", "4", "10", "5"));
     static if (true) {
       mixin(overflow_check!"i8.min"); }
   }
 ```
 is_same - is undefined
 i8 - is undefined
 overflow_check!"i8.min" - ?


 This code with mixins is horrible :)
Hi and thanks for your time. "pragma(msg, ...)" will indeed work but it will print the code in the standard output. Is there something that I'm missing?
Jan 05 2022
parent reply vit <vit vit.vit> writes:
On Wednesday, 5 January 2022 at 09:17:54 UTC, rempas wrote:
 On Wednesday, 5 January 2022 at 09:02:53 UTC, vit wrote:
 Try this:
 ```d
 pragma(msg, type_check!("static if", "i8", "true", "5", "4", 
 "10", "5"));
 ```

 Result:

 ```d
 static if(is_same!(num, i8)) {
     mixin(base_digit!("5", "4", "10", "5"));
     static if (true) {
       mixin(overflow_check!"i8.min"); }
   }
 ```
 is_same - is undefined
 i8 - is undefined
 overflow_check!"i8.min" - ?


 This code with mixins is horrible :)
Hi and thanks for your time. "pragma(msg, ...)" will indeed work but it will print the code in the standard output. Is there something that I'm missing?
1) That printed code on standard output is wrong, missing declaration for is_same and i8. 2) if-else must be in same mixin: ```d mixin("" + type_check!("static if", "i8", "true", "5", "4", "10", "5") + type_check!("else static if", "i16", "true", "7", "6", "18", "8") + type_check!("else static if", "i32", "true", "12", "10", "34", "13") + type_check!("else", "i64", "true", "21", "18", "66", "24") ); ``` 3) last type_checkin signed and unsigned part of function test has else with condition. ```d else(is_same!(num, i64)) { mixin(base_digit!("21", "18", "66", "24")); static if (true) { mixin(overflow_check!"i64.min"); } } ```
Jan 05 2022
next sibling parent reply rempas <rempas tutanota.com> writes:
On Wednesday, 5 January 2022 at 09:33:07 UTC, vit wrote:
 2) if-else must be in same mixin:
 ```d
     mixin(""
         + type_check!("static if", "i8", "true", "5", "4", 
 "10", "5")
         + type_check!("else static if", "i16", "true", "7", 
 "6", "18", "8")
     	+ type_check!("else static if", "i32", "true", "12", "10", 
 "34", "13")
     	+ type_check!("else", "i64", "true", "21", "18", "66", 
 "24")
     );
 ```
Yeah, sorry, you added the last comment (before this one) after I posted mine. Also, the fact that they must be in the same mixin is what I said that the problem is but thankfully there is a way around it. Tho this code will still not work. First, we must change "+" to "~" to concatenate strings but still, I'm getting the following error: ``` test.d-mixin-27(47): Error: found `End of File` when expecting `;` following statement test.d(45): Error: template instance `test.test!true` error instantiating ``` There may be a problem in the "type_check" enum but I wanted to post this reply before I search any any case there is something else happening and you happen to know. I will update if I found it
 3) last type_checkin signed and unsigned part of function test 
 has else with condition.

 ```d
 else(is_same!(num, i64)) {
     mixin(base_digit!("21", "18", "66", "24"));
     static if (true) {
       mixin(overflow_check!"i64.min"); }
   }
 ```
What does this mean? Yes they do, the else is used to mach the 64-bit type.
Jan 05 2022
parent rempas <rempas tutanota.com> writes:
On Wednesday, 5 January 2022 at 10:47:44 UTC, rempas wrote:
 There may be a problem in the "type_check" enum but I wanted to 
 post this reply before I search any any case there is something 
 else happening and you happen to know. I will update if I found 
 it
Well, it is specifically the "static if" part that cases the problem. I can't seem to be able to find anything tho
Jan 05 2022
prev sibling parent donalexinder24 <donalexinder24 gmail.com> writes:
On Wednesday, 5 January 2022 at 09:33:07 UTC, vit wrote:
 On Wednesday, 5 January 2022 at 09:17:54 UTC, rempas wrote:
 [...]
1) That printed code on standard output is wrong, missing declaration for is_same and i8. 2) if-else must be in same mixin: ```d mixin("" + type_check!("static if", "i8", "true", "5", "4", "10", "5") + type_check!("else static if", "i16", "true", "7", "6", "18", "8") + type_check!("else static if", "i32", "true", "12", "10", "34", "13") + type_check!("else", "i64", "true", "21", "18", "66", "24") ); ``` 3) last type_checkin signed and unsigned part of function test has else with condition. ```d else(is_same!(num, i64)) { mixin(base_digit!("21", "18", "66", "24")); static if (true) { mixin(overflow_check!"i64.min"); } } ```
hi Thank you sincerely! What exactly does this imply? They do, because the other is utilized to create the 64-bit type.
Jan 10 2022
prev sibling next sibling parent vit <vit vit.vit> writes:
On Wednesday, 5 January 2022 at 08:40:15 UTC, rempas wrote:
 I'm trying to use mixins and enums to "expand" code in place 
 but the results are not what I expected and I'm getting an 
 weird error. I have created the smallest possible example to 
 reproduce the error and it is the following:

 [...]
And you cannot have else statement in different mixin: ```d extern (C) void main() { ///OK: mixin("static if(true){}else{}"); ///ERROR: mixin("static if(true){}"); mixin("else{}"); } ```
Jan 05 2022
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jan 05, 2022 at 08:40:15AM +0000, rempas via Digitalmars-d-learn wrote:
[...]
 void test(bool signed)(int num, int base) {
   static if (signed) {
     mixin(type_check!("static if", "i8", "true", "5", "4", "10", "5"));
     mixin(type_check!("else static if", "i16", "true", "7", "6", "18",
 "8"));
     mixin(type_check!("else static if", "i32", "true", "12", "10", "34",
 "13"));
     mixin(type_check!("else", "i64", "true", "21", "18", "66", "24"));
   }
[...] Yeah, this won't fly. Whatever you pass to mixin must be one or more *complete* declaration or (possibly compound) statements. It's illegal to pass the `static if` and its else-clause to two different mixin() invocations (they are considered part of the same compound statement). T -- "No, John. I want formats that are actually useful, rather than over-featured megaliths that address all questions by piling on ridiculous internal links in forms which are hideously over-complex." -- Simon St. Laurent on xml-dev
Jan 05 2022
parent rempas <rempas tutanota.com> writes:
On Wednesday, 5 January 2022 at 17:48:03 UTC, H. S. Teoh wrote:
 Yeah, this won't fly. Whatever you pass to mixin must be one or 
 more
 *complete* declaration or (possibly compound) statements. It's 
 illegal
 to pass the `static if` and its else-clause to two different 
 mixin()
 invocations (they are considered part of the same compound 
 statement).


 T
I got it, thanks a lot!
Jan 05 2022
prev sibling parent reply Era Scarecrow <rtcvb32 yahoo.com> writes:
On Wednesday, 5 January 2022 at 08:40:15 UTC, rempas wrote:
 I'm trying to use mixins and enums to "expand" code in place 
 but the results are not what I expected and I'm getting an 
 weird error. I have created the smallest possible example to 
 reproduce the error and it is the following:
Back when i was working on bitmanip with the bitfield mixin, i had to re-write it with newlines and tabs, and then have it expand it as text and output it and see what the actual output was before i could debug it. The output was... informative. That said, rolling your own mixins should really be the last resort. You're dumping a lot into a single line of code you can't trace, follow, debug, or look at.
Jan 05 2022
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jan 05, 2022 at 11:29:08PM +0000, Era Scarecrow via Digitalmars-d-learn
wrote:
[...]
  That said, rolling your own mixins should really be the last resort.
  You're dumping a lot into a single line of code you can't trace,
  follow, debug, or look at.
Mixins are kinda like the nuclear option when nothing else would work. Generally, they should be avoided if there are less drastic ways of achieving what you want. Don't kill an ant with a nuclear warhead. Having said that, though, the usual way I use string mixins is to separately construct the string (usually in a CTFE function that returns the code string), then hand that to mixin() separately. The reason: if something goes wrong, then I can easily write `pragma(msg, myCtfeCodeGenFunc());` to see exactly what the compiler is choking on, rather than having to debug code by mentally piecing together a complicated inline mixin expression. Also, once my mixin expressions reaches a certain threshold of complexity, I prefer rather the build-time option of writing a helper program that generates the code into a .d file, that then gets imported by the subsequent main compilation step. That way I can actually see (and edit!) the generated code to figure out the problem without having to deal with CTFE-generated mixin expressions. (Certain forms of codegen are also faster to generate as a separate build step than trying to shoehorn it into CTFE -- generating code from large data files that require non-trivial data processing, for example.) (Off-topic rant: dub doesn't really support this usage very well, which is one of the reasons I haven't embraced it yet. It's technically *possible* to do this, but requires jumping through a lot of hoops. It really should be a LOT more integrated, considering that many D coders are here because they're into meta-programming, and codegen via a preliminary step is one of the staples of metaprogramming.) T -- In order to understand recursion you must first understand recursion.
Jan 05 2022