www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to insert code in place with templates/mixins?

reply rempas <rempas tutanota.com> writes:
Here I am having a problem with templates again. No matter how 
much I read, I can't seem to understand how templates/mixins 
work. So I'm having the following code (just a snippet of the 
real code):

```
     if (c != '%') {
       if (stdout_index < STDOUT_BUF_LEN) {
         stdout_buffer[stdout_index++] = c;
         continue;
       } else {
         sys_write(1, stdout_buffer.ptr, cast(i32)stdout_index);
         stdout_index = 0;
         stdout_buffer[stdout_index++] = c;
         continue;
       }
     }
```

And I want to create a macro (using the C terms) to make the code 
inside the first if statement (`if (c != '%')`) into a template 
that will be able to used and added in place (not as a function 
as I don't want to function call). I tried to make it both a 
template and a mixin template and It will not compile, rather it 
will give my the following error:

```
Error: declaration expected, not `if`
Error: declaration expected, not `continue`
Error: declaration expected, not `else`
Error: basic type expected, not `0`
Error: found `0` when expecting `;`
Error: no identifier for declarator 
`stdout_buffer[stdout_index++]`
Error: declaration expected, not `=`
Error: declaration expected, not `continue`
Error: unrecognized declaration
```

It should be clear what I tried to still I will post what I tried 
in case someone is curious to see:

```
mixin template add_char() {
   if (stdout_index < STDOUT_BUF_LEN) {
     stdout_buffer[stdout_index++] = c;
     continue;
   } else {
     sys_write(1, stdout_buffer.ptr, cast(i32)stdout_index);
     stdout_index = 0;
     stdout_buffer[stdout_index++] = c;
     continue;
   }
}
```

So any ideas why this doesn't work?
Dec 20 2021
parent reply rumbu <rumbu rumbu.ro> writes:
On Monday, 20 December 2021 at 08:45:50 UTC, rempas wrote:
 Here I am having a problem with templates again. No matter how 
 much I read, I can't seem to understand how templates/mixins 
 work.

 So any ideas why this doesn't work?
because you cannot have statements directly in a template (the fact that is a mixin template is irelevant), only declarations. If you want to just insert some random code, use strings. You can create a templated enum to store your parametrized string. Please note how your parameter (c) becomes part of the resulting string through concatenation (~): ``` enum add_char(char c) = `if (stdout_index < STDOUT_BUF_LEN) { stdout_buffer[stdout_index++] =` ~ c ~ `; continue; } else { sys_write(1, stdout_buffer.ptr, cast(i32)stdout_index); stdout_index = 0; stdout_buffer[stdout_index++] =` ~ c ~ `; continue; }`; ``` and when you want the code inserted: ``` mixin(add_char!'%'); ``` If you want to be sure that your string is syntactically correct, use token strings (https://dlang.org/spec/lex.html#token_strings)
Dec 20 2021
parent reply rempas <rempas tutanota.com> writes:
On Monday, 20 December 2021 at 09:30:30 UTC, rumbu wrote:
 because you cannot have statements directly in a template (the 
 fact that is a mixin template is irelevant), only declarations.

 If you want to just insert some random code, use strings. You 
 can create a templated enum to store your parametrized string. 
 Please note how your parameter (c) becomes part of the 
 resulting string through concatenation (~):

 ```
 enum add_char(char c) =

   `if (stdout_index < STDOUT_BUF_LEN) {
     stdout_buffer[stdout_index++] =` ~ c ~ `;
     continue;
   } else {
     sys_write(1, stdout_buffer.ptr, cast(i32)stdout_index);
     stdout_index = 0;
     stdout_buffer[stdout_index++] =` ~ c ~ `;
     continue;
   }`;

 ```

 and when you want the code inserted:

 ```
 mixin(add_char!'%');
 ```

 If you want to be sure that your string is syntactically 
 correct, use token strings 
 (https://dlang.org/spec/lex.html#token_strings)
Thanks a lot for the info. When I try to use this code, I'm getting the following error: ``` Error: expression expected, not `%` Error: expression expected, not `%` ``` So I suppose there is a problem with string concatenation. I couldn't use it anyway because it is inefficient and because I'm using betterC. Do you know any other way that I can concatenate strings that does not depend an the Garbage Collector or the standard library?
Dec 20 2021
parent reply rumbu <rumbu rumbu.ro> writes:
On Monday, 20 December 2021 at 10:49:20 UTC, rempas wrote:
 On Monday, 20 December 2021 at 09:30:30 UTC, rumbu wrote:

 Thanks a lot for the info. When I try to use this code, I'm 
 getting the following error:

 ```
 Error: expression expected, not `%`
 Error: expression expected, not `%`
 ```
My fault, I forgot to put some char delimiters. You can find tested code here: https://run.dlang.io/is/KfdED0
 So I suppose there is a problem with string concatenation. I 
 couldn't use it anyway because it is inefficient and because 
 I'm using betterC. Do you know any other way that I can 
 concatenate strings that does not depend an the Garbage 
 Collector or the standard library?
Enums (that's why the string is declarated as enum) are evaluated at compile time, the concatenation op will not end in your code as instruction, so you can do anything outside betterC rules as long you do it at compile time. You are just building some code to use later, the compiler does not generate any instruction for it. In the example above you can press the AST button to see exactly how your code is generated. Wnen you have doubts about a generated string you can always test it with ```pragma msg```. In this case, if you write: ``` pragma(msg, add_char!'%'); ``` you will have in the output exactly what the compiler will generate for your mixin.
Dec 20 2021
next sibling parent reply Tejas <notrealemail gmail.com> writes:
On Monday, 20 December 2021 at 11:30:09 UTC, rumbu wrote:
 On Monday, 20 December 2021 at 10:49:20 UTC, rempas wrote:
 On Monday, 20 December 2021 at 09:30:30 UTC, rumbu wrote:

 Thanks a lot for the info. When I try to use this code, I'm 
 getting the following error:

 ```
 Error: expression expected, not `%`
 Error: expression expected, not `%`
 ```
My fault, I forgot to put some char delimiters. You can find tested code here: https://run.dlang.io/is/KfdED0
 So I suppose there is a problem with string concatenation. I 
 couldn't use it anyway because it is inefficient and because 
 I'm using betterC. Do you know any other way that I can 
 concatenate strings that does not depend an the Garbage 
 Collector or the standard library?
Enums (that's why the string is declarated as enum) are evaluated at compile time, the concatenation op will not end in your code as instruction, so you can do anything outside betterC rules as long you do it at compile time. You are just building some code to use later, the compiler does not generate any instruction for it. In the example above you can press the AST button to see exactly how your code is generated. Wnen you have doubts about a generated string you can always test it with ```pragma msg```. In this case, if you write: ``` pragma(msg, add_char!'%'); ``` you will have in the output exactly what the compiler will generate for your mixin.
Ehh, it still fails; should've explicitly put the length of the array and the `extern (C)` in `main` ```d module demo; //i am just declaring these to have them. size_t stdout_index; enum STDOUT_BUF_LEN = 42; char[STDOUT_BUF_LEN] stdout_buffer; /+indexing an uninitialized dynamic array resulted in out of bounds error even for index == 0+/ alias i32 = int; void sys_write(int i, void* p, int index) {} // enum add_char(char c) = `if (stdout_index < STDOUT_BUF_LEN) { stdout_buffer[stdout_index++] ='` ~ c ~ `'; continue; } else { sys_write(1, stdout_buffer.ptr, cast(i32)stdout_index); stdout_index = 0; stdout_buffer[stdout_index++] ='` ~ c ~ `'; continue; }`; extern(C) /+added this because you used -betterC+/ void main() { while (true) { mixin(add_char!'%'); mixin(add_char!'$'); } } ```
Dec 20 2021
parent reply rempas <rempas tutanota.com> writes:
On Monday, 20 December 2021 at 11:58:58 UTC, Tejas wrote:
 Ehh, it still fails; should've explicitly put the length of the 
 array and the `extern (C)` in `main`

 ```d
 module demo;

 [ ... ]

 extern(C) /+added this because you used -betterC+/ void main() {

     while (true) {
 		mixin(add_char!'%');
 		mixin(add_char!'$');
     }
 }
 ```
Thanks! A mixin is not necessary, it will do the same thing without it.
Dec 20 2021
parent rempas <rempas tutanota.com> writes:
On Monday, 20 December 2021 at 18:06:32 UTC, rempas wrote:
 On Monday, 20 December 2021 at 11:58:58 UTC, Tejas wrote:
 Ehh, it still fails; should've explicitly put the length of 
 the array and the `extern (C)` in `main`

 ```d
 module demo;

 [ ... ]

 extern(C) /+added this because you used -betterC+/ void main() 
 {

     while (true) {
 		mixin(add_char!'%');
 		mixin(add_char!'$');
     }
 }
 ```
Thanks! A mixin is not necessary, it will do the same thing without it.
Well it seem that it actually needs it...
Dec 20 2021
prev sibling parent reply rempas <rempas tutanota.com> writes:
On Monday, 20 December 2021 at 11:30:09 UTC, rumbu wrote:
 Enums (that's why the string is declarated as enum) are 
 evaluated at compile time, the concatenation op will not end in 
 your code as instruction, so you can do anything outside 
 betterC rules as long you do it at compile time. You are just 
 building some code to use later, the compiler does not generate 
 any instruction for it.

 In the example above you can press the AST button to see 
 exactly how your code is generated.

 Wnen you have doubts about a generated string you can always 
 test it with ```pragma msg```. In this case, if you write:

 ```
 pragma(msg, add_char!'%');
 ```

 you will have in the output exactly what the compiler will 
 generate for your mixin.
That's cool! And I was wondering how I can make sting literal concatenation at compile time. Now the problem is that I want it to get the name of so symbol and add it to a string literal. Let's check this example: enum state(alias name) = `name` ~ ` = 10;`; I want this to add the token of that will be used as name in the string. For example, I want `state!val;` to get "expanded" as `val = 10;` rather than `10 = 10;`. So I don't want it to take the value of "val" but the word/token "val" itself. I tried using `alias` instead of `char` for the parameter but it didn't worked. Do you know how I can do that?
Dec 20 2021
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Monday, 20 December 2021 at 18:03:09 UTC, rempas wrote:

 Now the problem is that I want it to get the name of so 
 symbol and add it to a string literal.
Let's check this example: enum state(alias name) = `name` ~ ` = 10;`;
https://dlang.org/spec/traits.html#identifier
Dec 20 2021
parent reply rempas <rempas tutanota.com> writes:
On Monday, 20 December 2021 at 18:12:35 UTC, Stanislav Blinov 
wrote:
 https://dlang.org/spec/traits.html#identifier
Thanks!!! Finally I was able to do it! The code is the following (well not in my final project but it's a demonstration): ``` enum add_char(string c) = `if (stdout_index < STDOUT_BUF_LEN) { stdout_buffer[stdout_index++] =` ~ c ~ `; continue; } else { sys_write(1, stdout_buffer.ptr, cast(i32)stdout_index); stdout_index = 0; stdout_buffer[stdout_index++] =` ~ c ~ `; continue; }`; void main() { mixin(add_char!(__traits(identifier, c))); } ``` I don't know if there is another way to do it but this works for me. Also another thing that I want to ask is if the "mixin" is generated every time inside a loop and if there is a better way to do that?
Dec 20 2021
parent reply bachmeier <no spam.net> writes:
On Monday, 20 December 2021 at 18:23:44 UTC, rempas wrote:
 On Monday, 20 December 2021 at 18:12:35 UTC, Stanislav Blinov 
 wrote:
 https://dlang.org/spec/traits.html#identifier
Thanks!!! Finally I was able to do it! The code is the following (well not in my final project but it's a demonstration): ``` enum add_char(string c) = `if (stdout_index < STDOUT_BUF_LEN) { stdout_buffer[stdout_index++] =` ~ c ~ `; continue; } else { sys_write(1, stdout_buffer.ptr, cast(i32)stdout_index); stdout_index = 0; stdout_buffer[stdout_index++] =` ~ c ~ `; continue; }`; void main() { mixin(add_char!(__traits(identifier, c))); } ``` I don't know if there is another way to do it but this works for me. Also another thing that I want to ask is if the "mixin" is generated every time inside a loop and if there is a better way to do that?
You can see the ["String mixins" section here](http://ddili.org/ders/d.en/mixin.html) for more details. Mixins are generated at compile time, so if you're referring to a string mixin inside a runtime loop, the code will not be generated every time the loop runs.
Dec 20 2021
parent rempas <rempas tutanota.com> writes:
On Monday, 20 December 2021 at 18:58:39 UTC, bachmeier wrote:
 You can see the ["String mixins" section 
 here](http://ddili.org/ders/d.en/mixin.html) for more details. 
 Mixins are generated at compile time, so if you're referring to 
 a string mixin inside a runtime loop, the code will not be 
 generated every time the loop runs.
Thanks! Yeah after I commented, I thought a little bit about how they generate code at compile time like you said and understand that what I'm saying doesn't make much sense. Have a nice day my friend!
Dec 20 2021