www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - how to achieve C's Token Pasting (##) Operator to generate variable

reply mw <mingwu gmail.com> writes:
I want to generate a new symbol (new variable name) from existing 
one: e.g. in C:


$ cat t.c
--------------------------------------------
#define

int main() {
   int f(x) = 3;
   return _x;
}

$ make t
cc     t.c   -o t
$ ./t
$ echo $?
3
--------------------------------------------

I wonder how to do this in D? using template / mixin? traits?

Can you show me an example?

Thanks.
May 30 2020
next sibling parent kinke <noone nowhere.com> writes:
Using a mixin:

string f(string x) { return "_" ~ x; }

int main() {
   mixin("int "~f("x")~" = 3;");
   return _x;
}
May 30 2020
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Saturday, 30 May 2020 at 22:06:30 UTC, mw wrote:
 I want to generate a new symbol (new variable name) from 
 existing one: e.g. in C:


 $ cat t.c
 --------------------------------------------
 #define

 int main() {
   int f(x) = 3;
   return _x;
 }

 $ make t
 cc     t.c   -o t
 $ ./t
 $ echo $?
 3
 --------------------------------------------

 I wonder how to do this in D? using template / mixin? traits?

 Can you show me an example?

 Thanks.
enum f(string x) = "_" ~ x; int main() { mixin("int ", f!"x", " = 3;"); return _x; } This uses a templated [1] manifest constant [2] to generate the variable name at compile time, and a mixin statement [3] to insert the definition of `_x` into the program. [1] https://dlang.org/spec/template.html#variable-template [2] https://dlang.org/spec/enum.html#manifest_constants [3] https://dlang.org/spec/statement.html#mixin-statement
May 30 2020
parent reply mw <mingwu gmail.com> writes:
On Saturday, 30 May 2020 at 22:21:14 UTC, Paul Backus wrote:
 enum f(string x) = "_" ~ x;

 int main() {
   mixin("int ", f!"x", " = 3;");
   return _x;
 }

 This uses a templated [1] manifest constant [2] to generate the 
 variable name at compile time, and a mixin statement [3] to 
 insert the definition of `_x` into the program.

 [1] https://dlang.org/spec/template.html#variable-template
 [2] https://dlang.org/spec/enum.html#manifest_constants
 [3] https://dlang.org/spec/statement.html#mixin-statement
Thank you all for the reply. I hate to write boilerplate code: class Point { private int _x; public int x() {return _x;} public Point x(int v) {_x=v; return this;} ... // ... y, z } this is what I've got: $ cat b.d -------------------------------------------------------------------------------- // dmd -unittest -vcg-ast -c b.d import std.format; enum RW(string T, string name) = format(q{ private %1$s _%2$s; public %1$s %2$s() {return _%2$s;} public auto %2$s(%1$s v) {_%2$s = v; return this;} }, T, name); class Point { mixin(RW!("int", "x")); mixin(RW!("double", "y")); mixin(RW!("string", "z")); } $ dmd -unittest -vcg-ast -c b.d $ head -n 24 b.d.cg import object; import std.format; enum RW(string T, string name) = format("\x0a private %1$s _%2$s;\x0a public %1$s %2$s() {return _%2$s;}\x0a public auto %2$s(%1$s v) {_%2$s = v; return this;}\x0a ", T, name); class Point : Object { mixin(RW!("int", "x") { enum string RW = ['\x0a', ' ', ' ', ' ', ' ', 'p', 'r', 'i', 'v', 'a', 't', 'e', ' ', 'i', 'n', 't', ' ', '_', 'x', ';', '\x0a', ' ', ' ', ' ', ' ', 'p', 'u', 'b', 'l', 'i', 'c', ' ', ' ', 'i', 'n', 't', ' ', ' ', 'x', '(', ')', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '{', 'r', 'e', 't', 'u', 'r', 'n', ' ', '_', 'x', ';', '}', '\x0a', ' ', ' ', ' ', ' ', 'p', 'u', 'b', 'l', 'i', 'c', ' ', ' ', 'a', 'u', 't', 'o', ' ', ' ', 'x', '(', 'i', 'n', 't', ' ', 'v', ')', ' ', ' ', '{', '_', 'x', ' ', '=', ' ', 'v', ';', ' ', ' ', 'r', 'e', 't', 'u', 'r', 'n', ' ', 't', 'h', 'i', 's', ';', '}', '\x0a', ' ', ' ']; } ); mixin(RW!("double", "y") { enum string RW = ['\x0a', ' ', ' ', ' ', ' ', 'p', 'r', 'i', 'v', 'a', 't', 'e', ' ', 'd', 'o', 'u', 'b', 'l', 'e', ' ', '_', 'y', ';', '\x0a', ' ', ' ', ' ', ' ', 'p', 'u', 'b', 'l', 'i', 'c', ' ', ' ', 'd', 'o', 'u', 'b', 'l', 'e', ' ', ' ', 'y', '(', ')', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '{', 'r', 'e', 't', 'u', 'r', 'n', ' ', '_', 'y', ';', '}', '\x0a', ' ', ' ', ' ', ' ', 'p', 'u', 'b', 'l', 'i', 'c', ' ', ' ', 'a', 'u', 't', 'o', ' ', ' ', 'y', '(', 'd', 'o', 'u', 'b', 'l', 'e', ' ', 'v', ')', ' ', ' ', '{', '_', 'y', ' ', '=', ' ', 'v', ';', ' ', ' ', 'r', 'e', 't', 'u', 'r', 'n', ' ', 't', 'h', 'i', 's', ';', '}', '\x0a', ' ', ' ']; } ); mixin(RW!("string", "z") { enum string RW = ['\x0a', ' ', ' ', ' ', ' ', 'p', 'r', 'i', 'v', 'a', 't', 'e', ' ', 's', 't', 'r', 'i', 'n', 'g', ' ', '_', 'z', ';', '\x0a', ' ', ' ', ' ', ' ', 'p', 'u', 'b', 'l', 'i', 'c', ' ', ' ', 's', 't', 'r', 'i', 'n', 'g', ' ', ' ', 'z', '(', ')', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '{', 'r', 'e', 't', 'u', 'r', 'n', ' ', '_', 'z', ';', '}', '\x0a', ' ', ' ', ' ', ' ', 'p', 'u', 'b', 'l', 'i', 'c', ' ', ' ', 'a', 'u', 't', 'o', ' ', ' ', 'z', '(', 's', 't', 'r', 'i', 'n', 'g', ' ', 'v', ')', ' ', ' ', '{', '_', 'z', ' ', '=', ' ', 'v', ';', ' ', ' ', 'r', 'e', 't', 'u', 'r', 'n', ' ', 't', 'h', 'i', 's', ';', '}', '\x0a', ' ', ' ']; } ); } -------------------------------------------------------------------------------- Am I doing the right thing in D? any improvement you'd suggest? e.g. I don't quite like have to put the type and var name in the quotes as string: mixin(RW!("int", "x")); Is there a better way to achieve this? esp. for the type `int`, is there any way I don't have to quote it as string? Thanks.
May 30 2020
next sibling parent Johannes Loher <johannes.loher fg4f.de> writes:
On Saturday, 30 May 2020 at 23:39:31 UTC, mw wrote:
 On Saturday, 30 May 2020 at 22:21:14 UTC, Paul Backus wrote:
 [...]
Thank you all for the reply. I hate to write boilerplate code: [...]
import std.stdio : writeln; mixin template f(T, string name, T value = T.init) { mixin("T _" ~ name ~ " = value;"); } void main() { mixin f!(int, "x", 3); _x.writeln; // prints 3 mixin f!(float, "y", 1.23f); _y.writeln; // prints 1.23 mixin f!(string, "z"); _z.writeln; // prints an empty string (== string.init) }
May 30 2020
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Saturday, 30 May 2020 at 23:39:31 UTC, mw wrote:
 Thank you all for the reply.

 I hate to write boilerplate code:

 class Point {
   private   int _x;
   public    int  x()      {return _x;}
   public  Point  x(int v) {_x=v; return this;}

   ...
   // ... y, z
 }


 this is what I've got:
 $ cat b.d
 --------------------------------------------------------------------------------
 // dmd -unittest -vcg-ast -c b.d
 import std.format;


 enum RW(string T, string name) =
   format(q{
     private %1$s _%2$s;
     public  %1$s  %2$s()        {return _%2$s;}
     public  auto  %2$s(%1$s v)  {_%2$s = v;  return this;}
   }, T, name);


 class Point {
   mixin(RW!("int",     "x"));
   mixin(RW!("double",  "y"));
   mixin(RW!("string",  "z"));
 }
[...]
 Is there a better way to achieve this? esp. for the type `int`, 
 is there any way I don't have to quote it as string?

 Thanks.
You can simplify this considerably using a mixin template [1]: --- mixin template RW(T, string name) { private T var; public T get() { return var; } public typeof(this) set(T val) { var = val; return this; } mixin("private alias _", name, " = var;"); // two aliases with the same name create an overload set mixin("public alias ", name, " = get;"); mixin("public alias ", name, " = set;"); } class Point { mixin RW!(int, "x"); mixin RW!(int, "y"); // etc. } --- You still need string mixins to make the names work, but the rest can be done without them. Large string mixins tend to be error-prone and difficult to debug, so it's usually a good idea to make them as small as you reasonably can [2]. [1] https://dlang.org/spec/template-mixin.html [2] http://www.arsdnet.net/this-week-in-d/2016-feb-21.html
May 30 2020
parent reply mw <mingwu gmail.com> writes:
On Sunday, 31 May 2020 at 00:46:09 UTC, Paul Backus wrote:
 You can simplify this considerably using a mixin template [1]:

 ---
 mixin template RW(T, string name) {
     private T var;
     public T get() { return var; }
     public typeof(this) set(T val) { var = val; return this; }

     mixin("private alias _", name, " = var;");
     // two aliases with the same name create an overload set
     mixin("public alias ", name, " = get;");
     mixin("public alias ", name, " = set;");
 }

 class Point {
     mixin RW!(int, "x");
     mixin RW!(int, "y");
mixin RW!(string, "z"); // add
 }
 ---
This is better, ... but it breaks std.traits: void main() { auto fields = FieldNameTuple!(Point); writeln(fields); } $ ./b varvarvar And normally, we cannot define 2 fields with different types: class P { int x; double x; // b.d(45): Error: variable b.P.x conflicts with variable b.P.x at b.d(44) } With the above template we somehow tricked the compiler to be able to do this? Is this a loop-hole we should file a bug?
May 30 2020
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 5/30/20 11:28 PM, mw wrote:
 On Sunday, 31 May 2020 at 00:46:09 UTC, Paul Backus wrote:
 You can simplify this considerably using a mixin template [1]:

 ---
 mixin template RW(T, string name) {
 =C2=A0=C2=A0=C2=A0 private T var;
 =C2=A0=C2=A0=C2=A0 public T get() { return var; }
 =C2=A0=C2=A0=C2=A0 public typeof(this) set(T val) { var =3D val; retur=
n this; }
 =C2=A0=C2=A0=C2=A0 mixin("private alias _", name, " =3D var;");
 =C2=A0=C2=A0=C2=A0 // two aliases with the same name create an overloa=
d set
 =C2=A0=C2=A0=C2=A0 mixin("public alias ", name, " =3D get;");
 =C2=A0=C2=A0=C2=A0 mixin("public alias ", name, " =3D set;");
 }

 class Point {
 =C2=A0=C2=A0=C2=A0 mixin RW!(int, "x");
 =C2=A0=C2=A0=C2=A0 mixin RW!(int, "y");
=20 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 mixin RW!(string, "z");=C2=A0 // add =20
 }
 ---
=20 This is better, ... but it breaks std.traits:
The following code solves that I think the following syntax is an=20 improvement over Paul Backus's solution because it allows .x instead of=20 "x" by taking advantage of a static opDispatch. But it requires=20 parenthesis because now it's a string mixin, which is likely to be=20 noticeably slow to compile too. struct RW(T) { static string opDispatch(string name)() { import std.format; return format!q{ private %s _%s; public auto %s() { return _%s; } public auto %s(%s val) { _%s =3D val; return this; } }(T.stringof, name, name, name, name, T.stringof, name); } } struct Point { mixin (RW!int.x); mixin (RW!int.y); // etc. } import std.traits; import std.stdio; void main() { pragma(msg, FieldNameTuple!(Point)); auto p =3D Point(1, 2); p.x =3D 42; p.y =3D 43; writeln(p); } The spec allows opDispatch to be an eponymous template: https://dlang.org/spec/operatoroverloading.html#dispatch Unfortunately, I could not reach the following cleaner syntax with a=20 mixin template: mixin RW!int.x; Ali
May 31 2020
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 5/31/20 2:26 AM, Ali =C3=87ehreli wrote:

 Unfortunately, I could not reach the following cleaner syntax with a=20
 mixin template:
=20
  =C2=A0 mixin RW!int.x;
Ok, I solved that too with a very convoluted "eponymous mixin template=20 opDispatch." :) struct RW(T) { template opDispatch(string name) { static codeImpl() { import std.format; return format!q{ private %s _%s; public auto %s() { return _%s; } public auto %s(%s val) { _%s =3D val; return this; } }(T.stringof, name, name, name, name, T.stringof, name); } mixin template opDispatch(alias code =3D codeImpl()) { mixin (code); } } } struct Point { mixin RW!int.x; // <-- NICE :) mixin RW!int.y; // etc. } import std.traits; import std.stdio; void main() { pragma(msg, FieldNameTuple!(Point)); auto p =3D Point(1, 2); p.x =3D 42; p.y =3D 43; writeln(p); } Ali
May 31 2020
parent reply mw <mingwu gmail.com> writes:
On Sunday, 31 May 2020 at 09:37:24 UTC, Ali Çehreli wrote:
 On 5/31/20 2:26 AM, Ali Çehreli wrote:

 Ok, I solved that too with a very convoluted "eponymous mixin 
 template opDispatch." :)

 struct RW(T) {
   template opDispatch(string name) {
     static codeImpl() {
       import std.format;

       return format!q{
         private %s _%s;
         public auto %s() { return _%s; }
         public auto %s(%s val) { _%s = val; return this; }
       }(T.stringof, name,
         name, name,
         name, T.stringof, name);
     }

     mixin template opDispatch(alias code = codeImpl()) {
       mixin (code);
     }
   }
 }

 struct Point {
   mixin RW!int.x;    // <-- NICE :)
   mixin RW!int.y;
     // etc.
 }

 import std.traits;
 import std.stdio;

 void main() {
   pragma(msg, FieldNameTuple!(Point));

   auto p = Point(1, 2);
   p.x = 42;
   p.y = 43;
   writeln(p);
 }

 Ali
Thank you all for the refinement. One question: in Sebastiaan's solution opDispatch is performed at run-time, how efficient is the D runtime's implementation of opDispatch compared with a direct regular method call (or a virtual method call in general): ``` public final int x() {return _x;} // in this example it can be final ``` In short, while I want to reduce the boilerplate code I have to write, I don't want to pay any extra run-time cost; compile-time cost is fine. Ali in your solution, opDispatch is also performed at run-time instead of compile-time (I'm not sure)? since it's inside struct RW, (each x, y is actually a struct inside Point)?
May 31 2020
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 5/31/20 1:00 PM, mw wrote:> On Sunday, 31 May 2020 at 09:37:24 UTC,=20
Ali =C3=87ehreli wrote:

 One question: in Sebastiaan's solution opDispatch is performed at
 run-time
Templates don't exist at run time. They are used for generating code at=20 compile time and that's it. opDispatch is a template. Like all templates, it's evaluated at compile=20 time and code is generated for it at compile time. However, if=20 opDispatch() is a member function template, then there will be a=20 function call execution at runtime but this is not different from any=20 other member function call. Here is a struct with an opDispatch() used as a member function template.= struct XmlElement { static opDispatch(string tag)(string value) { import std.range; import std.format; enum opening =3D format!"<%s>"(tag); enum closing =3D format!"</%s>"(tag); // Note: Although we return a chained range, this could return a // string[3] as well, which may possibly be more performant. return chain(opening, value, closing); } } import std.stdio; void main() { writeln(XmlElement.foo("hello")); writeln(XmlElement.bar("world")); } opDispatch() is instantiated with two strings in the program: "foo" and=20 "bar". As a result, the XmlElement struct will be the equivalent of the=20 following one: struct XmlElement { static foo(string value) { enum opening =3D "<foo>"; enum closing =3D "</foo>"; return chain(opening, value, closing); } static bar(string value) { enum opening =3D "<bar>"; enum closing =3D "</bar>"; return chain(opening, value, closing); } } Note how two member functions are added to XmlElement and all of the=20 'opening' and 'closing' strings are computed at compile time. In my solution opDispatch() boils down to a "mixin template" itself. As=20 Paul Backus's code does, that mixin template is used for adding the=20 following members to the struct for each instantiation of opDispatch().=20 For example, for x, the struct will gain the following template=20 definition. (_FOR_X is my annotation). mixin template opDispatch_FOR_X(alias code =3D codeImpl()) { private int _x; public auto x() { return _x; } public auto x(T val) { _x =3D val; return this; } } Note that each instance of opDispatch() for "x", "y", etc. will gain a=20 template definition. Those definitions are a compile-time cost.=20 Eventually, when user code mixes-in the corresponding template, then the = three member above will be added to the user's struct. After that, there is no run-time cost more than adding those members by=20 hand. Ali
May 31 2020
prev sibling parent reply mw <mingwu gmail.com> writes:
On Sunday, 31 May 2020 at 06:28:11 UTC, mw wrote:
 This is better, ... but it breaks std.traits:

 void main() {
   auto fields = FieldNameTuple!(Point);
   writeln(fields);
 }

 $ ./b
 varvarvar

 And normally, we cannot define 2 fields with different types:

 class P {
   int x;
   double x;  // b.d(45): Error: variable b.P.x conflicts with 
 variable b.P.x at b.d(44)
 }

 With the above template we somehow tricked the compiler to be 
 able to do this?

 Is this a loop-hole we should file a bug?
This is related to D's silent crept-in multiple inheritance problem, a solution via language improvement is here: https://forum.dlang.org/post/lsnhqdoyatkzbzqbsrbb forum.dlang.org I'd imagine something like this: ---------------------------------------------------------------------- class Person : NameI, AddrI { mixin NameT!Person rename equals as name_equals; mixin AddrT!Person rename equals as addr_equals; bool equals(Person other) { return this.name_equals(other) && this.addr_equlas(other); } } ----------------------------------------------------------------------
Jun 04 2020
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Jun 04, 2020 at 06:09:35PM +0000, mw via Digitalmars-d-learn wrote:
[...]
 ----------------------------------------------------------------------
 class Person : NameI, AddrI {
   mixin NameT!Person rename equals as name_equals;
   mixin AddrT!Person rename equals as addr_equals;
 
   bool equals(Person other) {
     return this.name_equals(other) &&
            this.addr_equlas(other);
   }
 }
 ----------------------------------------------------------------------
TBH, whenever I run into a diamond inheritance problem or similar, my first reaction is, I'm using the wrong tool for modelling my data; I should be using some kind of component-based system instead of OO inheritance. Nowadays I rarely use OO-style inheritance for data modelling anymore; it's still useful for rare cases where a straight hierarchy makes sense (traditional GUI widgets, for example, or parse trees), but for complex modelling I just stop pretending that there's a direct mapping from problem domain to language constructs, and instead build containers that have arbitrary component combinations as an infrastructure instead. Recently I've been dabbling in ECS (entity-component-system) adaptations from gamedev: the system part is not useful to me, but the idea behind entity-component storage is very useful for modelling complex data, much more suitable than traditional OO inheritance IMO. T -- I am Ohm of Borg. Resistance is voltage over current.
Jun 04 2020
parent reply mw <mingwu gmail.com> writes:
On Thursday, 4 June 2020 at 18:42:05 UTC, H. S. Teoh wrote:
 TBH, whenever I run into a diamond inheritance problem or 
 similar, my first reaction is, I'm using the wrong tool for 
 modelling my data; I should be using some kind of 
 component-based system instead of OO inheritance.
I have no problem with whatever modelling technique the programmer want to choose. What I hate to see is: if a language does provide the OO mechanism, but having some loop-holes that causes trouble for the programmer who choose to use it. Esp. when these loop-holes are crept in after the language's initial design. (For example, I won't discuss multiple inheritance on a C forum, it's not that language designed for.)
Jun 04 2020
parent mw <mingwu gmail.com> writes:
On Thursday, 4 June 2020 at 19:42:57 UTC, mw wrote:
 On Thursday, 4 June 2020 at 18:42:05 UTC, H. S. Teoh wrote:
 TBH, whenever I run into a diamond inheritance problem or 
 similar, my first reaction is, I'm using the wrong tool for
^^^^^^^^^^^^^^ Haha, it's also because most of the populate OO languages didn't solve the diamond problem, and made the multiple inheritance hard to use in such situation, that's how the programmers learnt this "first reaction" from past bad experience. Conversely, if all the OO languages solved the diamond problem cleanly from the very start, the programmers will never learn such reaction.
Jun 04 2020
prev sibling parent Sebastiaan Koppe <mail skoppe.eu> writes:
On Saturday, 30 May 2020 at 23:39:31 UTC, mw wrote:
 Am I doing the right thing in D? any improvement you'd suggest?

 e.g. I don't quite like have to put the type and var name in 
 the quotes as string:

   mixin(RW!("int",     "x"));

 Is there a better way to achieve this? esp. for the type `int`, 
 is there any way I don't have to quote it as string?

 Thanks.
This would also be an option. ``` import std; class Point { struct Inner { int x; double y; } private Inner inner; Point opDispatch(string name, T)(T value) { mixin("inner."~name~" = value;"); return this; } auto opDispatch(string name)() { mixin("return inner."~name~";"); } } void main() { Point b = new Point(); b.x(4).y(5.0); writeln(b.x); } ```
May 31 2020