Jump to page: 1 24  
Page
Thread overview
reflection based on my experience so far on compile-time meta-programming in D as a novice user: the problems
Sep 13
mw
Sep 14
mw
Sep 14
mw
Sep 14
mw
Sep 14
mw
Sep 14
mw
Sep 14
mw
Sep 14
mw
Sep 15
mw
Sep 15
mw
Sep 14
mw
Sep 14
mw
September 13
I don't know how to cross-post on the web interface, so I copy & paste here.

this reflection comes from the other thread in Learn:

https://forum.dlang.org/thread/ujcbioaghvofwowihygq@forum.dlang.org?page=2

On Sunday, 13 September 2020 at 10:16:46 UTC, Paul Backus wrote:
> On Sunday, 13 September 2020 at 07:00:36 UTC, mw wrote:
>>
>> Here it is: D wrapper for https://ta-lib.org/
>>
>> https://github.com/mingwugmail/talibd
>>
>> I end up using C macro to generate D functions, the single template is this one:
>>
>> https://github.com/mingwugmail/talibd/blob/master/source/talibd.h#L117
>> #define DECL_TA_FUNC(TA_FUNC, FUNC_INS, FUNC_OUTS, expected_lookback) __NL__\
>
> The most straightforward way to do this in D is with a mixin template. Something like:
>
> mixin template DECL_TA_FUNC(string TA_FUNC, FUNC_INS, FUNC_OUTS, int expected_lookback)
> {
>     bool impl(...)
>     {
>         // ...
>     }
>
>     // Could also wrap the whole function in a string mixin,
>     // but this is easier.
>     mixin("alias ", TA_FUNC, " = impl;");
> }
>
> Which you would then use like this:
>
> mixin DECL_TA_FUNC!(
>     "TA_MA",
>     Tuple!(int, "MA_optInTimePeriod", TA_MAType, "opInMAType"),
>     Tuple!(double[], "outMA"),
>     MA_optInTimePeriod - 1
> );


Thanks, I will do the exercise some other day.

But, I'd reflect on my experience so far on compile-time meta-programming in D as a novice user, the big problems are:

-- in D, there are too many choices, with no clear guideline which one is *THE* one to use for a particular purpose: language or library mechanisms? mixin? template? AliasSeq / aliasSeqOf? Tuple? (non-)?-eponymous version of ...?; and even for a particular option, e.g. Tuple!(int, "MA_optInTimePeriod", TA_MAType, "opInMAType"),  there are choices to use either token (int) or string ("MA_optInTimePeriod"). And user does not have a strong guide on which choice is *THE* way to proceed. Each mechanism seems to have / fit a particular purpose, but when you start to use it, you'll probably find there are new problems come-in later, and you want to revisit the choice you made earlier on.

By contrast, in C: there is only *ONE* mechanism, i.e. macro, that's it.

-- in D, there is no easy way to convert between token <==> string. Given a token, does token.stringof always work to paste with other strings to generate a new token? and given a string, does mixin!"string" always work to be a valid token?

By contrast, in C: there is only *ONE* way: xxx ## yyy.

-- in D, there is no easy way to see directly the generated source code by the compiler at compile-time, which makes the debug difficult during development.

By contrast, in C: one can easily see the result via: cpp -P foo.h > foo.c


As I said earlier, I'm not very experienced with C macros either, however with some googling, I was able to work out a C macro version to generate D code; but with all the help so far, I still have no confidence that I can work out a solution in D to implement this:

```
     bool impl(...)
     {
         // ...
     }
```

September 14
On Sunday, 13 September 2020 at 18:32:17 UTC, mw wrote:

> But, I'd reflect on my experience so far on compile-time meta-programming in D as a novice user, the big problems are:
>
> -- in D, there are too many choices, with no clear guideline which one is *THE* one to use for a particular purpose: language or library mechanisms? mixin? template? AliasSeq / aliasSeqOf? Tuple? (non-)?-eponymous version of ...?; and even for a particular option, e.g. Tuple!(int, "MA_optInTimePeriod", TA_MAType, "opInMAType"),  there are choices to use either token (int) or string ("MA_optInTimePeriod"). And user does not have a strong guide on which choice is *THE* way to proceed. Each mechanism seems to have / fit a particular purpose, but when you start to use it, you'll probably find there are new problems come-in later, and you want to revisit the choice you made earlier on.
Yes, and each has its limitations and the compiler checks that everything is syntactically correct.
>
> By contrast, in C: there is only *ONE* mechanism, i.e. macro,
... where you can put everything including destroying the language, and the compiler never sees it and so can't check for any kind of correctness.

>
> -- in D, there is no easy way to convert between token <==> string. Given a token, does token.stringof always work to paste with other strings to generate a new token? and given a string, does mixin!"string" always work to be a valid token?
>
> By contrast, in C: there is only *ONE* way: xxx ## yyy.
And you also have no way to see if this gives a valid token, the evaluation-order is implementation dependant and still no checks anywhere.

>
> -- in D, there is no easy way to see directly the generated source code by the compiler at compile-time, which makes the debug difficult during development.
Yeah, but after you succeed, it is proven by the compiler that it is correct,
but your C-macro can fail on anything else you try to apply it.

>
> By contrast, in C: one can easily see the result via: cpp -P foo.h > foo.c
D also provides a mechanism to see the result after lowering.

I'm really happy to not have to deal with write-only macros anymore.

September 14
On Monday, 14 September 2020 at 07:15:14 UTC, Dominikus Dittes Scherkl wrote:
> Yes, and each has its limitations and the compiler checks that everything is syntactically correct.

Well, the mechanism is so hard to use that I cannot even write compile-able code, guess this also saving the compiler's trouble to check it.


> ... where you can put everything including destroying the language, and the compiler never sees it and so can't check for any kind of correctness.

This is not true, the generated code will be eventually compiled and checked by the compiler on the 2nd pass.

> I'm really happy to not have to deal with write-only macros anymore.

I know what you are talking about: all the code generation & compilation in one pass, and you can't read the intermediate generated code.

But my usage here is to generate the code, and read for easy debug.

September 14
On Sunday, 13 September 2020 at 18:32:17 UTC, mw wrote:
> -- in D, there are too many choices, with no clear guideline which one is *THE* one to use for a particular purpose: language or library mechanisms? mixin? template? AliasSeq / aliasSeqOf? Tuple? (non-)?-eponymous version of ...?; and even for a particular option, e.g. Tuple!(int, "MA_optInTimePeriod", TA_MAType, "opInMAType"),  there are choices to use either token (int) or string ("MA_optInTimePeriod"). And user does not have a strong guide on which choice is *THE* way to proceed. Each mechanism seems to have / fit a particular purpose, but when you start to use it, you'll probably find there are new problems come-in later, and you want to revisit the choice you made earlier on.
>
> By contrast, in C: there is only *ONE* mechanism, i.e. macro, that's it.
>

In Assembly, there is no choice between while and do/while and for; there is only jmp. High-level structured expression is still preferable.

> -- in D, there is no easy way to convert between token <==> string. Given a token, does token.stringof always work to paste with other strings to generate a new token? and given a string, does mixin!"string" always work to be a valid token?
>

There is an easy way to convert between a token and a string: "token". Seriously, stringof should be renamed to stringof__debugOnlyReallyYouDontNeedThisForMetaprogrammingTrustMe. It's like catnip to C people. I don't know if the docs have a big fat warning saying "DO NOT USE THIS FOR METAPROGRAMMING EXCEPT FOR DEBUGGING AND ERROR MESSAGES, TRUST ME" but they probably should. Just use the name of the token metavariable!

> -- in D, there is no easy way to see directly the generated source code by the compiler at compile-time, which makes the debug difficult during development.

`pragma(msg, X.stringof)`. This is what stringof is for. I could not imagine D metaprogramming without pragma(msg).
September 14
On Sunday, 13 September 2020 at 18:32:17 UTC, mw wrote:
> -- in D, there is no easy way to see directly the generated source code by the compiler at compile-time, which makes the debug difficult during development.

You might want to try `-mixin=<file>` (writes expanded mixins into <file>) and -vcg-ast (dumps the semantically analysed code into <source>.cg)
September 14
On Monday, 14 September 2020 at 08:01:47 UTC, FeepingCreature wrote:
> There is an easy way to convert between a token and a string: "token". Seriously, stringof should be renamed to stringof__debugOnlyReallyYouDontNeedThisForMetaprogrammingTrustMe. It's like catnip to C people. I don't know if the docs have a big fat warning saying "DO NOT USE THIS FOR METAPROGRAMMING EXCEPT FOR DEBUGGING AND ERROR MESSAGES, TRUST ME" but they probably should. Just use the name of the token metavariable!

I have been trying, but there is one I can't get...

---

import std;

string generate(T)() {
    return "%s bla;".format(T.stringof);
}

void main() {
    mixin(generate!int);
    bla = 5;
}
September 14
On Monday, 14 September 2020 at 09:14:42 UTC, Sebastiaan Koppe wrote:
> On Monday, 14 September 2020 at 08:01:47 UTC, FeepingCreature wrote:
>> There is an easy way to convert between a token and a string: "token". Seriously, stringof should be renamed to stringof__debugOnlyReallyYouDontNeedThisForMetaprogrammingTrustMe. It's like catnip to C people. I don't know if the docs have a big fat warning saying "DO NOT USE THIS FOR METAPROGRAMMING EXCEPT FOR DEBUGGING AND ERROR MESSAGES, TRUST ME" but they probably should. Just use the name of the token metavariable!
>
> I have been trying, but there is one I can't get...
>
> ---
>
> import std;
>
> string generate(T)() {
>     return "%s bla;".format(T.stringof);
> }
>
> void main() {
>     mixin(generate!int);
>     bla = 5;
> }

Don't overthink it:

string generate(string typeName)() {
    return "%s bla;".format(typeName);
}

void main() {
    mixin(generate!"int");
    bla = 5;
}
September 14
On Monday, 14 September 2020 at 10:54:07 UTC, Paul Backus wrote:
> Don't overthink it:
>
> string generate(string typeName)() {
>     return "%s bla;".format(typeName);
> }
>
> void main() {
>     mixin(generate!"int");
>     bla = 5;
> }

Even better:

mixin template generate(T) {
    T bla;
}

void main() {
    mixin generate!int;
    bla = 5;
}

String mixins, like inline assembly, is a very powerful tool. Just like inline assembly though, it's usually not the tool you should be reaching for. Generally, if it's possible to do something without string mixins, that way will be less error prone and more readable.

--
  Simen
September 14
On Monday, 14 September 2020 at 10:54:07 UTC, Paul Backus wrote:
> Don't overthink it:
>
> string generate(string typeName)() {
>     return "%s bla;".format(typeName);
> }
>
> void main() {
>     mixin(generate!"int");
>     bla = 5;
> }

The implied restriction was that the generate function takes a type as its template argument, and then generates a string containing a valid D statement with said type.

But fear not:

----

string generate(string typeName)() {
    return "%s bla;".format(typeName);
}

void bla(T)() {
    mixin(generate!(T.stringof));
    bla = 5;
}

September 14
On Monday, 14 September 2020 at 12:27:34 UTC, Simen Kjærås wrote:
> String mixins, like inline assembly, is a very powerful tool. Just like inline assembly though, it's usually not the tool you should be reaching for. Generally, if it's possible to do something without string mixins, that way will be less error prone and more readable.

That is not what I was asking for.

There are plenty of cases where a string mixin is the only way out. How then, given a T template argument, do you generate a string representing T without using .stringof?
« First   ‹ Prev
1 2 3 4