digitalmars.D - The most awesome "forward to member" solution?
- Andrei Alexandrescu (41/41) May 02 2015 Hey folks,
- Dicebot (1/1) May 02 2015 Sounds similar to http://dlang.org/phobos/std_typecons.html#.Proxy
- Meta (3/5) May 02 2015 That's a good idea. Proxy could be improved to take a list of
- Andrei Alexandrescu (24/28) May 02 2015 Here's what I have right now - simple as they come:
- Meta (31/57) May 02 2015 Is there any particular reason to generate 1 function per parent
- Andrei Alexandrescu (6/10) May 02 2015 Yeh, that's the first solution that comes to mind. alias doesn't work
- Meta (4/15) May 03 2015 IMO, using __traits and opDispatch is a fair bit cleaner, and I
- Meta (2/2) May 03 2015 Wow, ParameterTypeTuple even works with ref arguments with no
- Andrei Alexandrescu (27/46) May 03 2015 You're right, that is lovely! I've improved it as follows:
- Dicebot (2/3) May 03 2015 All similar utilities are currently in std.typecons
- Andrei Alexandrescu (2/5) May 03 2015 worksforme -- Andrei
- Andrei Alexandrescu (9/57) May 03 2015 Take that back, the opDispatch-based solution has a fatal flaw: doesn't
- Andrei Alexandrescu (35/35) May 03 2015 OK, here's what I have now - two templates that are self contained and
- Meta (3/8) May 03 2015 I can't picture what you're talking about. Could you post an
- Andrei Alexandrescu (16/24) May 03 2015 struct A
- Meta (14/43) May 03 2015 I still don't get it. Your example works with both
- Andrei Alexandrescu (5/47) May 03 2015 Hmm, I didn't try it assuming it won't work (thought __traits(getMember,...
- Meta (3/7) May 03 2015 So is a function that generates a string mixin necessary or not
- Andrei Alexandrescu (16/23) May 03 2015 I'm not sure. It's a great question. In some ways a string mixin is the
- Temtaime (2/2) May 04 2015 Will allocator ever be added to phobos as maybe
- Andrei Alexandrescu (2/4) May 04 2015 Affirmative. Getting Real Close Now. -- Andrei
- Jacob Carlborg (5/8) May 04 2015 I would guess the opDispatch solution doesn't integrate so well with an
- Meta (3/11) May 04 2015 Hmm, that's true. I guess it'd be better to start with a
- Meta (5/32) May 06 2015 I have forgotten to mention that I will make a pull request for
- Meta (2/41) Aug 13 2015 http://forum.dlang.org/post/mqnhhbbyctwrfzvbrocg@forum.dlang.org
- Jacob Carlborg (6/7) May 03 2015 Yes, I would think so. Although, I would prefer a regular template mixin...
- tcak (4/6) May 03 2015 Great documentation by the way. I understood what it does with
- Jakob Ovrum (2/8) May 03 2015 http://dlang.org/phobos-prerelease/std_typecons.html#.Proxy
- bioinfornatics (2/2) May 05 2015 That is not exactly what I requested years ago --->
- Laeeth Isharc (33/45) May 09 2015 This was very useful, by the way, with synchronicitous timing. I
Hey folks, So in working with the allocator, a common matter has come again to the fore: I need to forward certain functions to a member. Consider code in https://github.com/andralex/phobos/blob/allocator/std/experimental/allocator/free_tree.d: struct FreeTree(ParentAllocator) { ... static if (hasMember!(ParentAllocator, "expand")) bool expand(ref void[] b, size_t delta) { return parent.expand(b, delta); } static if (hasMember!(ParentAllocator, "reallocate")) bool reallocate(ref void[] b, size_t n) { return parent.reallocate(b, n); } static if (hasMember!(ParentAllocator, "allocateAll")) void[] allocateAll() { return parent.allocateAll; } } A simple solution have FreeTree support these functions would be to use simple subtyping with alias this. However, I'd like more control than that - yes, I want forwarding to work but not accessing the parent object directly. So I'm thinking of defining a mixin that would be used like this: struct FreeTree(ParentAllocator) { ... mixin(forwardIfDefined("parent", "expand", "reallocate", "allocateAll")); } and, boom, all appropriate forwarding is generated, either with sheer definitions or by using opDispatch. Furthermore, we should probably define this for Phobos because it's a common thing people like to do. Before embarking on this, does anyone have some code around that does that kind of stuff? Andrei
May 02 2015
On Sunday, 3 May 2015 at 00:25:13 UTC, Dicebot wrote:Sounds similar toThat's a good idea. Proxy could be improved to take a list of names of members to forward. That'd be pretty cool actually.
May 02 2015
On 5/2/15 5:42 PM, Meta wrote:On Sunday, 3 May 2015 at 00:25:13 UTC, Dicebot wrote:Here's what I have right now - simple as they come: http://dpaste.dzfl.pl/7ec11459a125. Kudos to whoever defined ParameterTypeTuple, it's exactly what the doctor prescribed. The gist of it is: string forwardToMember(string member, string[] funs...) { string result; foreach (fun; funs) { result ~= " auto "~fun~"(ParameterTypeTuple!(typeof("~member~"."~fun~")) args) { return "~member~"."~fun~"(args); } "; } return result; } which is used as: mixin(forwardToMember("member", "expand", "reallocate", "owns")); Is this a common thing people wanna do? Put in Phobos? AndreiThat's a good idea. Proxy could be improved to take a list of names of members to forward. That'd be pretty cool actually.
May 02 2015
On Sunday, 3 May 2015 at 04:20:46 UTC, Andrei Alexandrescu wrote:On 5/2/15 5:42 PM, Meta wrote:Is there any particular reason to generate 1 function per parent function? I was actually surprised the following doesn't work: struct Test1 { void foo(string) { writeln("Test1 foo"); } int bar(int) { writeln("Test1 bar"); return 0; } void baz() { writeln("Test1 baz"); } } struct TestWrapper(T) { T parent; alias foo = parent.foo; alias bar = parent.bar; alias baz = parent.baz; } void main() { TestWrapper!Test1 t; //Error: this for foo needs to be type Test1 not type TestWrapper!(Test1) t.foo("test"); //ditto t.bar(2); //ditto t.baz(); } It seems like it'd be a lot cheaper and cleaner to just be able to alias the parent method. Also, it could probably be made a bit simpler with opDispatch.On Sunday, 3 May 2015 at 00:25:13 UTC, Dicebot wrote:Here's what I have right now - simple as they come: http://dpaste.dzfl.pl/7ec11459a125. Kudos to whoever defined ParameterTypeTuple, it's exactly what the doctor prescribed. The gist of it is: string forwardToMember(string member, string[] funs...) { string result; foreach (fun; funs) { result ~= " auto "~fun~"(ParameterTypeTuple!(typeof("~member~"."~fun~")) args) { return "~member~"."~fun~"(args); } "; } return result; } which is used as: mixin(forwardToMember("member", "expand", "reallocate", "owns")); Is this a common thing people wanna do? Put in Phobos? Andrei
May 02 2015
On 5/2/15 10:00 PM, Meta wrote:It seems like it'd be a lot cheaper and cleaner to just be able to alias the parent method.Yeh, that's the first solution that comes to mind. alias doesn't work here but of course we could change the language.Also, it could probably be made a bit simpler with opDispatch.I'd have to see the code, but my intuition is that things could get quite a bit more hairy. Andrei
May 02 2015
On Sunday, 3 May 2015 at 05:49:52 UTC, Andrei Alexandrescu wrote:On 5/2/15 10:00 PM, Meta wrote:IMO, using __traits and opDispatch is a fair bit cleaner, and I prefer the syntax of a mixin template to regular mixin. http://dpaste.dzfl.pl/d60498246577It seems like it'd be a lot cheaper and cleaner to just be able to alias the parent method.Yeh, that's the first solution that comes to mind. alias doesn't work here but of course we could change the language.Also, it could probably be made a bit simpler with opDispatch.I'd have to see the code, but my intuition is that things could get quite a bit more hairy. Andrei
May 03 2015
Wow, ParameterTypeTuple even works with ref arguments with no problem. That's impressive.
May 03 2015
On 5/3/15 12:18 AM, Meta wrote:On Sunday, 3 May 2015 at 05:49:52 UTC, Andrei Alexandrescu wrote:You're right, that is lovely! I've improved it as follows: mixin template forwardToMember(alias member, methods...) { import std.algorithm : among; import std.traits : ParameterTypeTuple; template opDispatch(string sym) if ((methods.length == 0 || sym.among(methods))) { auto ref opDispatch( ParameterTypeTuple!(__traits(getMember, member, sym)) args) { return __traits(getMember, member, sym)(args); } } } So now ref returns are preserved and the mixin is self-contained (doesn't require imports from the outside). Compared to my solution, this has the advantage that if the child defines a method, it will take precedence over the formatted one. So that allowed me to add a feature: if no methods are specified, all are forwarded. There are a couple of ways in which this could and should be improved, most notably overloads control. Even as is it's pretty darn awesome, Meta could you please make it into a pull request? I think it should go in std.functional. AndreiOn 5/2/15 10:00 PM, Meta wrote:IMO, using __traits and opDispatch is a fair bit cleaner, and I prefer the syntax of a mixin template to regular mixin. http://dpaste.dzfl.pl/d60498246577It seems like it'd be a lot cheaper and cleaner to just be able to alias the parent method.Yeh, that's the first solution that comes to mind. alias doesn't work here but of course we could change the language.Also, it could probably be made a bit simpler with opDispatch.I'd have to see the code, but my intuition is that things could get quite a bit more hairy. Andrei
May 03 2015
On Sunday, 3 May 2015 at 18:54:45 UTC, Andrei Alexandrescu wrote:I think it should go in std.functional.All similar utilities are currently in std.typecons
May 03 2015
On 5/3/15 12:04 PM, Dicebot wrote:On Sunday, 3 May 2015 at 18:54:45 UTC, Andrei Alexandrescu wrote:worksforme -- AndreiI think it should go in std.functional.All similar utilities are currently in std.typecons
May 03 2015
On 5/3/15 11:54 AM, Andrei Alexandrescu wrote:On 5/3/15 12:18 AM, Meta wrote:Take that back, the opDispatch-based solution has a fatal flaw: doesn't compose properly. For example, in std.allocator it's frequent that allocators stack on top of one another, so one forwards a method call to another one which in turn forwards to another. With code generation this obviously works out of the box because the generated code is identical to what one would write by hand to achieve the same. But the opDispatch-based solution only works one level. AndreiOn Sunday, 3 May 2015 at 05:49:52 UTC, Andrei Alexandrescu wrote:You're right, that is lovely! I've improved it as follows: mixin template forwardToMember(alias member, methods...) { import std.algorithm : among; import std.traits : ParameterTypeTuple; template opDispatch(string sym) if ((methods.length == 0 || sym.among(methods))) { auto ref opDispatch( ParameterTypeTuple!(__traits(getMember, member, sym)) args) { return __traits(getMember, member, sym)(args); } } } So now ref returns are preserved and the mixin is self-contained (doesn't require imports from the outside). Compared to my solution, this has the advantage that if the child defines a method, it will take precedence over the formatted one. So that allowed me to add a feature: if no methods are specified, all are forwarded. There are a couple of ways in which this could and should be improved, most notably overloads control. Even as is it's pretty darn awesome, Meta could you please make it into a pull request? I think it should go in std.functional.On 5/2/15 10:00 PM, Meta wrote:IMO, using __traits and opDispatch is a fair bit cleaner, and I prefer the syntax of a mixin template to regular mixin. http://dpaste.dzfl.pl/d60498246577It seems like it'd be a lot cheaper and cleaner to just be able to alias the parent method.Yeh, that's the first solution that comes to mind. alias doesn't work here but of course we could change the language.Also, it could probably be made a bit simpler with opDispatch.I'd have to see the code, but my intuition is that things could get quite a bit more hairy. Andrei
May 03 2015
OK, here's what I have now - two templates that are self contained and work well: The first uses opDispatch to dispatch to a member. The second is a simple string function that generates the appropriate code. As discussed the latter composes but the former doesn't. mixin template dispatchToMember(alias member, methods...) { import std.algorithm : among; import std.traits : ParameterTypeTuple; template opDispatch(string sym) if ((methods.length == 0 || sym.among(methods))) { auto ref opDispatch(ParameterTypeTuple!(__traits(getMember, member, sym)) args) { return __traits(getMember, member, sym)(args); } } } string forwardToMember(string member, string[] funs...) { string result = " import std.traits : hasMember, ParameterTypeTuple;\n"; foreach (fun; funs) { result ~= " static if (hasMember!(typeof("~member~"), `"~fun~"`)) auto "~fun~"(ParameterTypeTuple!(typeof("~member~"."~fun~")) args) { return "~member~"."~fun~"(args); }\n"; } return result; } Andrei
May 03 2015
On Sunday, 3 May 2015 at 20:25:45 UTC, Andrei Alexandrescu wrote:OK, here's what I have now - two templates that are self contained and work well: The first uses opDispatch to dispatch to a member. The second is a simple string function that generates the appropriate code. As discussed the latter composes but the former doesn't.I can't picture what you're talking about. Could you post an example?
May 03 2015
On 5/3/15 4:20 PM, Meta wrote:On Sunday, 3 May 2015 at 20:25:45 UTC, Andrei Alexandrescu wrote:struct A { void fun(int); } struct B { A a; mixin forwardToMember!(a, "fun"); } struct C { B b; mixin forwardToMember!(b, "fun"); } AndreiOK, here's what I have now - two templates that are self contained and work well: The first uses opDispatch to dispatch to a member. The second is a simple string function that generates the appropriate code. As discussed the latter composes but the former doesn't.I can't picture what you're talking about. Could you post an example?
May 03 2015
On Sunday, 3 May 2015 at 23:54:49 UTC, Andrei Alexandrescu wrote:On 5/3/15 4:20 PM, Meta wrote:I still don't get it. Your example works with both dispatchToMember and forwardToMember. What is the difference between these two exactly? auto opDispatch(string sym: foo)(ParemeterTypeTuple!(/*etc*/) args) { return parent.foo(args); } //Automatically generated auto foo(ParameterTypeTuple!(/*etc*/) args) { return parent.foo(args); }On Sunday, 3 May 2015 at 20:25:45 UTC, Andrei Alexandrescu wrote:struct A { void fun(int); } struct B { A a; mixin forwardToMember!(a, "fun"); } struct C { B b; mixin forwardToMember!(b, "fun"); } AndreiOK, here's what I have now - two templates that are self contained and work well: The first uses opDispatch to dispatch to a member. The second is a simple string function that generates the appropriate code. As discussed the latter composes but the former doesn't.I can't picture what you're talking about. Could you post an example?
May 03 2015
On 5/3/15 5:29 PM, Meta wrote:On Sunday, 3 May 2015 at 23:54:49 UTC, Andrei Alexandrescu wrote:Hmm, I didn't try it assuming it won't work (thought __traits(getMember, member, sym) would fail with opDispatch). Tried it just now, it does work like a charm. Thanks! AndreiOn 5/3/15 4:20 PM, Meta wrote:I still don't get it. Your example works with both dispatchToMember and forwardToMember. What is the difference between these two exactly? auto opDispatch(string sym: foo)(ParemeterTypeTuple!(/*etc*/) args) { return parent.foo(args); } //Automatically generated auto foo(ParameterTypeTuple!(/*etc*/) args) { return parent.foo(args); }On Sunday, 3 May 2015 at 20:25:45 UTC, Andrei Alexandrescu wrote:struct A { void fun(int); } struct B { A a; mixin forwardToMember!(a, "fun"); } struct C { B b; mixin forwardToMember!(b, "fun"); } AndreiOK, here's what I have now - two templates that are self contained and work well: The first uses opDispatch to dispatch to a member. The second is a simple string function that generates the appropriate code. As discussed the latter composes but the former doesn't.I can't picture what you're talking about. Could you post an example?
May 03 2015
On Monday, 4 May 2015 at 01:01:05 UTC, Andrei Alexandrescu wrote:Hmm, I didn't try it assuming it won't work (thought __traits(getMember, member, sym) would fail with opDispatch). Tried it just now, it does work like a charm. Thanks! AndreiSo is a function that generates a string mixin necessary or not necessary?
May 03 2015
On 5/3/15 8:13 PM, Meta wrote:On Monday, 4 May 2015 at 01:01:05 UTC, Andrei Alexandrescu wrote:I'm not sure. It's a great question. In some ways a string mixin is the straight deal - you get complete control over what you generate, ultimate flexibility, and you get to inspect it if you so want. (BTW I think compiler errors in mixins should either print the failing mixin with line numbers, or make it available as a file). But then the opDispatch solution is more structured and restricted in a good way. I think we need a bit more experience with both to figure out which is the best way to go. More specifically: * forward to enumerated types * distinguish static/non-static methods, and define forwarding appropriately * define properties to forward to member variables * alias member types and templates * other similar stuff I didn't think of, in essence "defer stuff to this member" AndreiHmm, I didn't try it assuming it won't work (thought __traits(getMember, member, sym) would fail with opDispatch). Tried it just now, it does work like a charm. Thanks! AndreiSo is a function that generates a string mixin necessary or not necessary?
May 03 2015
Will allocator ever be added to phobos as maybe std.experimental.allocator for a first time ?
May 04 2015
On 5/4/15 9:31 AM, Temtaime wrote:Will allocator ever be added to phobos as maybe std.experimental.allocator for a first time ?Affirmative. Getting Real Close Now. -- Andrei
May 04 2015
On 2015-05-04 07:28, Andrei Alexandrescu wrote:But then the opDispatch solution is more structured and restricted in a good way. I think we need a bit more experience with both to figure out which is the best way to go.I would guess the opDispatch solution doesn't integrate so well with an already present opDispatch (if there's such a use case). -- /Jacob Carlborg
May 04 2015
On Monday, 4 May 2015 at 19:17:26 UTC, Jacob Carlborg wrote:On 2015-05-04 07:28, Andrei Alexandrescu wrote:Hmm, that's true. I guess it'd be better to start with a mixin-based solution.But then the opDispatch solution is more structured and restricted in a good way. I think we need a bit more experience with both to figure out which is the best way to go.I would guess the opDispatch solution doesn't integrate so well with an already present opDispatch (if there's such a use case).
May 04 2015
On Sunday, 3 May 2015 at 18:54:45 UTC, Andrei Alexandrescu wrote:You're right, that is lovely! I've improved it as follows: mixin template forwardToMember(alias member, methods...) { import std.algorithm : among; import std.traits : ParameterTypeTuple; template opDispatch(string sym) if ((methods.length == 0 || sym.among(methods))) { auto ref opDispatch( ParameterTypeTuple!(__traits(getMember, member, sym)) args) { return __traits(getMember, member, sym)(args); } } } So now ref returns are preserved and the mixin is self-contained (doesn't require imports from the outside). Compared to my solution, this has the advantage that if the child defines a method, it will take precedence over the formatted one. So that allowed me to add a feature: if no methods are specified, all are forwarded. There are a couple of ways in which this could and should be improved, most notably overloads control. Even as is it's pretty darn awesome, Meta could you please make it into a pull request? I think it should go in std.functional. AndreiI have forgotten to mention that I will make a pull request for this ASAP. I have been in the process of moving and starting a new job this week and last so I only have enough time to occasionally post currently.
May 06 2015
On Wednesday, 6 May 2015 at 22:59:16 UTC, Meta wrote:On Sunday, 3 May 2015 at 18:54:45 UTC, Andrei Alexandrescu wrote:http://forum.dlang.org/post/mqnhhbbyctwrfzvbrocg forum.dlang.orgYou're right, that is lovely! I've improved it as follows: mixin template forwardToMember(alias member, methods...) { import std.algorithm : among; import std.traits : ParameterTypeTuple; template opDispatch(string sym) if ((methods.length == 0 || sym.among(methods))) { auto ref opDispatch( ParameterTypeTuple!(__traits(getMember, member, sym)) args) { return __traits(getMember, member, sym)(args); } } } So now ref returns are preserved and the mixin is self-contained (doesn't require imports from the outside). Compared to my solution, this has the advantage that if the child defines a method, it will take precedence over the formatted one. So that allowed me to add a feature: if no methods are specified, all are forwarded. There are a couple of ways in which this could and should be improved, most notably overloads control. Even as is it's pretty darn awesome, Meta could you please make it into a pull request? I think it should go in std.functional. AndreiI have forgotten to mention that I will make a pull request for this ASAP. I have been in the process of moving and starting a new job this week and last so I only have enough time to occasionally post currently.
Aug 13 2015
On 2015-05-03 06:20, Andrei Alexandrescu wrote:Is this a common thing people wanna do? Put in Phobos?Yes, I would think so. Although, I would prefer a regular template mixin and taking the member as an alias parameter instead of a string, if possible. -- /Jacob Carlborg
May 03 2015
On Sunday, 3 May 2015 at 00:25:13 UTC, Dicebot wrote:Sounds similar toGreat documentation by the way. I understood what it does with one read: "Make proxy for a."
May 03 2015
On Sunday, 3 May 2015 at 07:55:30 UTC, tcak wrote:On Sunday, 3 May 2015 at 00:25:13 UTC, Dicebot wrote:Sounds similar toGreat documentation by the way. I understood what it does with one read: "Make proxy for a."
May 03 2015
That is not exactly what I requested years ago ---> http://forum.dlang.org/thread/1330792937.10754.7.camel jonathan
May 05 2015
On Sunday, 3 May 2015 at 00:19:37 UTC, Andrei Alexandrescu wrote:Hey folks, So in working with the allocator, a common matter has come again to the fore: I need to forward certain functions to a member.This was very useful, by the way, with synchronicitous timing. I have various derivative instruments made up of legs. For example an interest rate swap where I pay fixed cash flows on one leg and receive floating cash flows on another. So each swap leg is itself an entity (I made them structs), and there are a collection of such legs (of differing types) comprising a swap (or FRA, FX trade etc). The parent entity (eg a swap with a fixed leg and a float leg; or with two float legs) needs to forward calls to the legs. Eg if you change the notional or maturity of the parent, you probably want to change the notionals of the component legs, although in special cases they might be different. I don't want to muck about with Byzantine inheritance systems I won't likely myself remember in a year (people seem to underrate the value of coherence these days, and fragmentation doesn't help). On the other hand I didn't fancy writing lots of boilerplate code just to forward calls to each leg (or to only one where it made sense). So I modified forwardToMember to forwardToLegs (I need to return a Swap object, since I want to return the Swap for UFCS and the calls to leg methods return the leg itself). setMaturityDate(DateTime maturity( calls fixLeg.setMaturity(maturity) and floatLeg.setMaturity. And setFloatMaturity(DateTime maturity) just calls floatLeg.setMaturity(maturity) - ie the call is remapped to a different name when forwarding, so the user doesn't need to get her hands messy with the nitty gritty of the individual legs. So now all the glue logic is specified in simple arrays that produce the requisite code at compile time. It's easy to see what is going on, maintainable, and the parents are only a very spaced-out 100 lines. So thank you for this, and for all the other nice features in D.So I'm thinking of defining a mixin that would be used likethis: struct FreeTree(ParentAllocator) { ... mixin(forwardIfDefined("parent", "expand", "reallocate", "allocateAll")); }
May 09 2015