September 14, 2020
On Monday, 14 September 2020 at 12:32:35 UTC, Sebastiaan Koppe wrote:
>
> 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;
> }

There's no guarantee that `T.stringof` produces a name that's valid in the scope where you're mixing it in:

import somelib: Y = X;
alias blaY = bla!Y;
// Error: undefined identifier `X`

In general, it's impossible to round-trip a type through a string. The closest thing is .mangleof, which is guaranteed to produce a globally-unique result, but can't be converted back into a type afterward. So maybe what you actually want is `__traits(demangle)`?
September 14, 2020
On Monday, 14 September 2020 at 12:32:35 UTC, Sebastiaan Koppe wrote:
> 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;
> }

You mean:

mixin(generate!"T").

"token" :)
September 14, 2020
On Monday, 14 September 2020 at 15:38:07 UTC, FeepingCreature wrote:
> You mean:
>
> mixin(generate!"T").
>
> "token" :)

You are mixin-in the T right inside the template, where T is defined. That is not always the case though.

----

struct S {
  mixin(generate!int);
}

string generate(T)() {
  // [...] bunch of introspect code
  return "T blah;"; // no works
}

---

but by introducing an intermediate layer it works:

---

struct S {
  mixin generate!int;
}

mixin template generate(T) {
  mixin(generateImpl!T);
}

string generateImpl(T)() {
  // [...] bunch of introspect code
  return "T blah;";
}

---

So yeah, it can work without stringof. But is it any better? I don't think so.
September 14, 2020
On Monday, 14 September 2020 at 09:14:42 UTC, Sebastiaan Koppe wrote:
>
> 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;
> }

Thanks for the simple example to illustrate my points:

-- in D, there are too many choices, with no clear guideline which one is *THE* one to use for a particular purpose
-- in D, there is no easy way to convert between token <==> string.

from the following discussions of this example, it clearly shows people have different choices to pass either the token <int>, or string "int", and use ".stringof" or ".mangleof", some leads to dead end, some cause scope errors.


(And Sebastiaan is a long timer on this forum than me, yet still puzzled to make this simple meta-programming example to work.)

September 14, 2020
On Sunday, 13 September 2020 at 18:32:17 UTC, mw wrote:
>>> 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(...)
>>     {
>>         // ...
>>     }


Actually I just realized, that Paul literally means `impl(...)`, I have thought `(...)` it's an abbreviation for illustration purpose.

The reason is that it's not easy (or not possible at all! to pass two or more sets of variadic parameters[1] to a function in D for meta-programming.

Well, here Paul give a nice trick to pass them (FUNC_INS, and FUNC_OUTS) as template parameters. However, if we take a look of the C-macro generated function signature:

https://github.com/mingwugmail/talibd/blob/master/source/talib_func.d#L103

bool TA_MACD(double[] inData ,

    // the following 3 is FUNC_OUTS
    double[] outMACD, double[] outMACDSignal, double[] outMACDHist ,

    // the following 3 is FUNC_INS
    int optInFastPeriod=default_optInFastPeriod,
    int optInSlowPeriod=default_optInSlowPeriod,
    int optInSignalPeriod=default_optInSignalPeriod) { ... }


I would naturally think this is the most straight-forward target function signature I want to generate. And I can do it without any trouble at all with C-macro.

https://github.com/mingwugmail/talibd/blob/master/source/talib_func.h#L118


But if we compare it with Paul's D solution siganature:

    bool impl(...)  // literally `(...)`  here!

That's the difficulty I have experienced. The natural most straight-forward target function signature (that I have in mind when I start to program) have become a convoluted work-around in D. That's why I cannot figure out how to do it.

Now even with all the suggestions of using `-vcg-ast` or `pragma(msg, X.stringof)` debug utilities to help, the mental burden from the most straight-forward targeted function signature to the actual D-generated function signature is still far away!

Further more, if when people use such generated function, at the call-site: e.g.

https://github.com/mingwugmail/talibd/blob/master/source/oo.d#L47

    return TA_MACD(_prices, macd, macdSignal, macdHist);

If something goes wrong, and the user want to go back and check the function definition:

-- C-macro generated as I showed above, v.s
-- D version: bool impl(...)  // literally `(...)`  here!
   or, even with the `-vcg-ast` or `pragma(msg ...)` output


You tell me which one is more clear & helpful to identify the problem!


So here is my point (4) on the problems of D meta-programming:

-- because of the points (1~3), the resulting D meta-programming version is often convoluted, and the code may have very bigger distance from the most straight-forward targeted function signature one have in mind from the beginning. And this makes writing & (and more importantly) reading (by a non-code-author) such code difficult.


[1] https://forum.dlang.org/post/sjkbrycitdsgtyxcpxdj@forum.dlang.org

September 14, 2020
On 9/14/20 1:40 PM, mw wrote:
> -- because of the points (1~3), the resulting D meta-programming version is often convoluted, and the code may have very bigger distance from the most straight-forward targeted function signature one have in mind from the beginning. And this makes writing & (and more importantly) reading (by a non-code-author) such code difficult.
> 
> 

You're looking at C macros, which D specifically does not do, mostly because of the crap that can come out of them (note you can use dpp if you really want them).

Your C macro I think translates pretty easily to a D mixin, because it's generating a complete function:

string DECL_TA_FUNC(string TA_FUNC, string[] FUNC_INS, string[] FUNC_OUTS)
{
   // this would be easier with string interpolation!
   return format(q{
bool %s(double[] inData, %-(double[] %s,%), %-(%s,%)) {
  %(assert %s.length == inData.length;%)
  int begin, num;
  int lookback = %s_Lookback( %(%s... you get the idea
}}, TA_FUNC, FUNC_OUTS, FUNC_INS, FUNC_OUTS, TA_FUNC, ...);
}

enum MA_INS = [
   "int MA_optInTimePeriod",
   "int TA_MAType optInMAType"
];
enum MA_OUTS = [
   "outMA"
];
mixin(DECL_TA_FUNC(TA_MA, MA_INS, MA_OUTS, somethingElse, whatever));

I didn't feel like translating that whole file. I think you can do it with probably 25% of the size of that C macro code, and IMO much more readable. No idea why you would prefer C preprocessor, D has far superior string manipulation capabilities.

-Steve
September 14, 2020
On Monday, 14 September 2020 at 18:20:02 UTC, Steven Schveighoffer wrote:
>    // this would be easier with string interpolation!
> , D has far superior string manipulation capabilities.

Basically, your solution is: let's just use strings! (forget about of all the other fancy mechanism I've listed in point (1)).  Yes, that's right: source code should just be strings.

From that point of view, C-preprocessor is conceptually at a high level.

September 14, 2020
On Mon, Sep 14, 2020 at 02:20:02PM -0400, Steven Schveighoffer via Digitalmars-d wrote: [...]
> You're looking at C macros, which D specifically does not do, mostly because of the crap that can come out of them (note you can use dpp if you really want them).
[...]

Yep:	https://www.ioccc.org/2005/anon/anon.c

;-)


T

-- 
To provoke is to call someone stupid; to argue is to call each other stupid.
September 14, 2020
On Monday, 14 September 2020 at 18:36:53 UTC, H. S. Teoh wrote:
>
> Yep:	https://www.ioccc.org/2005/anon/anon.c

As I said, I'm using the *generated* code:

  cpp -P foo.h > foo.c  // <- this generated code


For a fair comparison, how Steven's *generating* code is more superior to read than anon.c?

```
string DECL_TA_FUNC(string TA_FUNC, string[] FUNC_INS, string[] FUNC_OUTS)
{
   // this would be easier with string interpolation!
   return format(q{
bool %s(double[] inData, %-(double[] %s,%), %-(%s,%)) {
  %(assert %s.length == inData.length;%)
  int begin, num;
  int lookback = %s_Lookback( %(%s... you get the idea
}}, TA_FUNC, FUNC_OUTS, FUNC_INS, FUNC_OUTS, TA_FUNC, ...);
}

enum MA_INS = [
   "int MA_optInTimePeriod",
   "int TA_MAType optInMAType"
];
enum MA_OUTS = [
   "outMA"
];
mixin(DECL_TA_FUNC(TA_MA, MA_INS, MA_OUTS, somethingElse, whatever));
```
September 14, 2020
On Monday, 14 September 2020 at 18:30:16 UTC, mw wrote:
> On Monday, 14 September 2020 at 18:20:02 UTC, Steven Schveighoffer wrote:
>>    // this would be easier with string interpolation!
>> , D has far superior string manipulation capabilities.
>
> Basically, your solution is: let's just use strings! (forget about of all the other fancy mechanism I've listed in point (1)).
>  Yes, that's right: source code should just be strings.
>
> From that point of view, C-preprocessor is conceptually at a high level.

This is hilariously backwards. The c preprocessor is literally just pasting strings around.

-Steve