May 31, 2020 Re: how to achieve C's Token Pasting (##) Operator to generate variable name in D? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | 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 Re: how to achieve C's Token Pasting (##) Operator to generate variable name in D? | ||||
---|---|---|---|---|
| ||||
Posted in reply to mw | On 5/31/20 1:00 PM, mw wrote:> On Sunday, 31 May 2020 at 09:37:24 UTC, Ali Çehreli 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 compile time and that's it. opDispatch is a template. Like all templates, it's evaluated at compile time and code is generated for it at compile time. However, if opDispatch() is a member function template, then there will be a function call execution at runtime but this is not different from any 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 = format!"<%s>"(tag); enum closing = 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 "bar". As a result, the XmlElement struct will be the equivalent of the following one: struct XmlElement { static foo(string value) { enum opening = "<foo>"; enum closing = "</foo>"; return chain(opening, value, closing); } static bar(string value) { enum opening = "<bar>"; enum closing = "</bar>"; return chain(opening, value, closing); } } Note how two member functions are added to XmlElement and all of the 'opening' and 'closing' strings are computed at compile time. In my solution opDispatch() boils down to a "mixin template" itself. As Paul Backus's code does, that mixin template is used for adding the following members to the struct for each instantiation of opDispatch(). For example, for x, the struct will gain the following template definition. (_FOR_X is my annotation). mixin template opDispatch_FOR_X(alias code = codeImpl()) { private int _x; public auto x() { return _x; } public auto x(T val) { _x = val; return this; } } Note that each instance of opDispatch() for "x", "y", etc. will gain a template definition. Those definitions are a compile-time cost. 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 hand. Ali |
June 04, 2020 Re: how to achieve C's Token Pasting (##) Operator to generate variable name in D? | ||||
---|---|---|---|---|
| ||||
Posted in reply to mw | 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); } } ---------------------------------------------------------------------- |
June 04, 2020 Re: how to achieve C's Token Pasting (##) Operator to generate variable name in D? | ||||
---|---|---|---|---|
| ||||
Posted in reply to mw | 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. |
June 04, 2020 Re: how to achieve C's Token Pasting (##) Operator to generate variable name in D? | ||||
---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | 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.)
|
June 04, 2020 Re: how to achieve C's Token Pasting (##) Operator to generate variable name in D? | ||||
---|---|---|---|---|
| ||||
Posted in reply to mw | 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.
|
Copyright © 1999-2021 by the D Language Foundation