Jump to page: 1 25  
Page
Thread overview
Perfect forwarding
Jul 26, 2020
Adam D. Ruppe
Jul 26, 2020
Adam D. Ruppe
Jul 26, 2020
Adam D. Ruppe
Jul 26, 2020
Mathias LANG
Jul 26, 2020
Adam D. Ruppe
Jul 26, 2020
Jacob Carlborg
Jul 26, 2020
Andrej Mitrovic
Jul 28, 2020
Andrej Mitrovic
Aug 24, 2020
Atila Neves
Jul 26, 2020
Jean-Louis Leroy
Jul 26, 2020
Jean-Louis Leroy
Jul 28, 2020
Manu
Jul 28, 2020
Jean-Louis Leroy
Jul 29, 2020
Manu
Jul 29, 2020
Jean-Louis Leroy
Jul 30, 2020
Manu
Jul 30, 2020
Jean-Louis Leroy
Jul 29, 2020
Adam D. Ruppe
Jul 29, 2020
Jean-Louis Leroy
Jul 29, 2020
Jean-Louis Leroy
Jul 29, 2020
Adam D. Ruppe
Jul 30, 2020
Manu
Jul 31, 2020
Boris Carvajal
Jul 30, 2020
Jean-Louis Leroy
Jul 30, 2020
Adam D. Ruppe
Jul 30, 2020
Manu
Jul 29, 2020
Adam D. Ruppe
Jul 30, 2020
Manu
Jul 29, 2020
Stefan Koch
Jul 31, 2020
Chad Joan
Jul 31, 2020
Adam D. Ruppe
Jul 31, 2020
Chad Joan
Jul 31, 2020
Paul Backus
Jul 31, 2020
Chad Joan
July 25, 2020
This topic came about during beerconf (it was fun!): Write an idiomatic template `forward` that takes an alias `fun` and defines (generates) one overload for each overload of `fun`. Example:

template forward(alias fun)
{
    ...
}

Now for this function:

void myfun(int, ref double, out string);
int myfun(in string, inout double);

the instantiation would be (stylized):

template forward!myfun
{
    void myfun(int a, ref double b, out string c)
    {
        return myfun(a, b, c);
    }
    int myfun(in string a, inout double b)
    {
        return myfun(a, b);
    }
}

So the challenge is implementing forward() to do this.
July 26, 2020
On Sunday, 26 July 2020 at 01:29:21 UTC, Andrei Alexandrescu wrote:
> So the challenge is implementing forward() to do this.

I know the trick to do this, but I won't post the answer yet because I don't want to ruin it for others who want to try.

However I'm going to point out that my trick doesn't work if you are trying to forward a template, since template params cannot be introspected. As far as I know, there is no solution to that.

My impl also does a bonus: forwarding UDAs as well. That requires very new dmd though because until very recently that would hit a compiler bug! But it is possible.


Note: part of the challenge is making sure it works with user-defined types too. Your first thought of an implementation might not, so test that with a struct from an import when there's a local struct with the same name....
July 25, 2020
On 7/25/20 9:44 PM, Adam D. Ruppe wrote:
> However I'm going to point out that my trick doesn't work if you are trying to forward a template, since template params cannot be introspected. As far as I know, there is no solution to that.

That needs fixed. What primitives would you need for that?
July 26, 2020
On Sunday, 26 July 2020 at 02:10:19 UTC, Andrei Alexandrescu wrote:
> That needs fixed. What primitives would you need for that?

Hmm, hard to say, and I'm also hesitant because it makes template optimization even harder - the more of the innards we expose, the less the compiler is able to discard, even in theory. (We probably will need some kind of specifically restricted template at some point as a practical optimization matter. Stefan's not wrong about the complications here.)

But anyway, I think we'd have to see at a minimum a list of template parameters. Constraint might be useful too, but that can be at least tested via __traits(compiles) once you have the template parameter list.

The tricky thing is figuring out how to represent them in the language. Regular function parameters are not representable either... you probably know how awkward it is to get function default arguments. (Oh, btw, those of you doing the OP challenge, remember default arguments should be forwarded too!) You have to make a helper function slicing the params and return the value since D doesn't really represent this data, it is more like an opaque object you can just use in certain contexts.

But at least with ordinary parameters, you can drill down with typeof(param). That would work for template value parameters, but what is T? Well, is(T) might help there.

static if(is(Param)) { /* template type param */ }
else static if(is(typeof(Param) Type)) { /* template value param */ }

The big remaining question is an alias param. If we had some kind __traits(isAlias) that could be used here as well as in other places of reflection. Good idea regardless imo. Then you need to be able ot get the alias name itself, not just the identifier of what it is an alias of. I think __traits(identifier) and/or .stringof can do this now but unable to check as I type this email. But should make that formally possible - remember a template alias param will not actually be an alias of anything yet! Ditto on T, it is a speculative type, not an actual type. So regular reflection might not quite work, just it is as close as we can get.

And then T... params. That probably needs a specific detection too, could be like __traits(isAliasSeq) or something.


So to sum up before my laptop dies:

* a way to get the list of template parameters

* a way to identify the different kinds of template params. Some overlap with function parameters is possible but it will be tricky because template(T) is not actually a type. But if the compiler made it a placeholder type for this purpose then we can reuse some of the machinery.

* alias and tuple params could use specific attention, which can be written in a general-purpose manner.


Then I think we can make this happen. One of the guys at work has hit this trouble before too, perhaps on Monday we can loop him in and get a second opinion to see if I missed anything.
July 26, 2020
Oops, I forgot about default arguments and specializations.

Default arguments are relatively straightforward, can do it at least as hacky as the runtime function variety.

But specializations are tricky to represent. The closest thing we have in the normal language is the `is` expression... but it isn't exactly the same... still if it could be like an is lambda it might work...

To be honest I suspect the best way to do this at this point would be to make it part of the opaque slice type. So if you define a new template with it, it inherits that but otherwise you can't really look in. I'm not satisfied with that but it might be the most practical thing to do right now and can be revisited in the future once the basic functionality actually works.
July 26, 2020
On Sunday, 26 July 2020 at 03:59:00 UTC, Adam D. Ruppe wrote:
> Oops, I forgot about default arguments and specializations.
>
> Default arguments are relatively straightforward, can do it at least as hacky as the runtime function variety.

How do you forward this:
```
class Foo
{
    void set (time_t value = time()) {}
}
```
July 26, 2020
On 2020-07-26 04:10, Andrei Alexandrescu wrote:

> That needs fixed. What primitives would you need for that?

It has already been implemented but rejected [1]. Andrei, you were part of the discussions and approved the changes.

[1] https://github.com/dlang/dmd/pull/5201

-- 
/Jacob Carlborg
July 26, 2020
On Sunday, 26 July 2020 at 11:56:16 UTC, Jacob Carlborg wrote:
> On 2020-07-26 04:10, Andrei Alexandrescu wrote:
>
>> That needs fixed. What primitives would you need for that?
>
> It has already been implemented but rejected [1]. Andrei, you were part of the discussions and approved the changes.
>
> [1] https://github.com/dlang/dmd/pull/5201

Ah, this was in the back of my head when someone mentioned template parameters.

I haven't looked at that in a long time.
July 26, 2020
On Sunday, 26 July 2020 at 10:51:44 UTC, Mathias LANG wrote:
> How do you forward this:
> ```
> class Foo
> {
>     void set (time_t value = time()) {}
> }
> ```

The same as the others wrt the default argument. The fact it is in a class complicates things a little since `this` is not propagated through... but if I remove it from there it still works.

Here's my solution as a link so people can look at it (and prolly destroy holes i missed lol) if you want but not click if you still wanna play with the challenge.

http://arsdnet.net/dcode/forward.d

The 8 lines at top are the forward, then the rest are the test conditions.

BTW I'd note that just doing a speculative forward is a bit simpler than this - you can just do (Args...)(auto ref Args args) - but the linked technique does it ahead-of-time, as if you wrote it by hand, meaning reflection still works on the forwarded definitions too.
July 26, 2020
On Sunday, 26 July 2020 at 01:29:21 UTC, Andrei Alexandrescu wrote:
> This topic came about during beerconf (it was fun!): Write an idiomatic template `forward` that takes an alias `fun` and defines (generates) one overload for each overload of `fun`. Example:
>
> template forward(alias fun)
> {
>     ...
> }
>
> Now for this function:
>
> void myfun(int, ref double, out string);
> int myfun(in string, inout double);
>
> the instantiation would be (stylized):
>
> template forward!myfun
> {
>     void myfun(int a, ref double b, out string c)
>     {
>         return myfun(a, b, c);
>     }
>     int myfun(in string a, inout double b)
>     {
>         return myfun(a, b);
>     }
> }
>
> So the challenge is implementing forward() to do this.

Using bolts' experimental refraction module:

module challenge;

import std.format;
import bolts.experimental.refraction;

template forward(alias fun)
{
    enum name = __traits(identifier, fun);
    static foreach (ovl; __traits(getOverloads, __traits(parent, fun), name)) {
        mixin(
            {
                enum original = refract!(ovl, "ovl");
                return original.withBody(q{{
                            return %s(%s);
                        }}.format(original.name, original.argumentMixture))
                    .mixture;
            }());
    }
}

void myfun(int, ref double, out string);

int myfun(in string, inout double);

pragma(msg, typeof(__traits(getOverloads, forward!myfun, "myfun")[0]));
pragma(msg, typeof(__traits(getOverloads, forward!myfun, "myfun")[1]));

Output:

@system void(int _0, ref double _1, out string _2)
@system int(const(string) _0, inout(double) _1)

Okay the "in" is missing in front of the first argument of the second overload, but it doesn't seem to be there in the first place:

pragma(msg, typeof(__traits(getOverloads, challenge, "myfun")[1]));

Output:
int(const(string), inout(double))

Also the body of the forwarders should probably use `std.functional.forward`.
« First   ‹ Prev
1 2 3 4 5