digitalmars.D.learn - Composing features at compile time
- Jacob Carlborg (24/24) Nov 24 2013 Does anyone know a good way of composing features at compile time? Say I...
- =?UTF-8?B?IlLDqW15IE1vdcOremEi?= (58/82) Nov 24 2013 This looks like a case study for aspect oriented programming:
- Jacob Carlborg (6/59) Nov 24 2013 Unfortunately logging and events were more of an example, for the real
- Philippe Sigaud (2/3) Nov 24 2013 Ah then, you can discard my own proposals. Only #3 is still possible.
- Philippe Sigaud (24/24) Nov 24 2013 The general problem is to inject code at predetermined points. As Rémy
- Dicebot (5/5) Nov 24 2013 Well you can make one step forward and use policy-based design as
- Philippe Sigaud (3/7) Nov 24 2013 But how do you use policies to inject different code in different
- Dicebot (9/12) Nov 24 2013 Don't really get the question. Isn't it what policies do pretty
- Dicebot (20/27) Nov 24 2013 To clarify, OP example will look like this:
- Jacob Carlborg (5/24) Nov 24 2013 So StubLogger would implement "info" but do nothing? How good would the
- Dicebot (7/9) Nov 24 2013 Last time I checked such stuff LDC is quite capable at
- Philippe Sigaud (8/20) Nov 24 2013 Hmm. Then you still need to define where the policy will act. OK, I
- Jacob Carlborg (5/11) Nov 24 2013 Please see this post for an actual problem I'm trying to solve:
- Dejan Lekic (9/36) Nov 24 2013 Jakob, whenever I need something like you describe, I do more/less the s...
- Jacob Carlborg (14/18) Nov 24 2013 Unfortunately this looks like it works best when providing fairly
- Philippe Sigaud (3/10) Nov 24 2013 If references are not supported, you want the else clause all by
- Jacob Carlborg (6/9) Nov 24 2013 I suspected that. They not so much coupled with each other, rather
- Philippe Sigaud (7/11) Nov 24 2013 I don't see how to do that, except by using flags & string mixins.
- Jacob Carlborg (5/9) Nov 24 2013 Here's an actual problem I'm trying to solve:
- Shammah Chancellor (7/7) Nov 24 2013 Based on what your actual problem is -- it seems like you need to
Does anyone know a good way of composing features at compile time? Say I have a struct or class that I want to support different features that are configurable at compile time. One way would be to just pass in a couple of boolean flags and use static-if, like this: class Foo (bool logging, bool events) { static if (logging) Logger logger; this () { static if (logging) logger = new Logger; } void action () { static if (logging) logger.info("performing action"); // perform action } } Using this approach I get the feeling that there will quickly become a big nest of static-ifs. Does anyone have any better ideas? -- /Jacob Carlborg
Nov 24 2013
This looks like a case study for aspect oriented programming: several separated concerns that start to intertwine within your code; if left unchecked this could result in some messy code. In Python, I used reflection and "magic" things and have already used the spring AOP in Java that is done with some Aspect classes and annotation. I suppose that D could bring the best of both world with its compile time reflection and UDA. But if you are in urgent need for a solution, here is a C++ trick called "mixin classes", template classes parameterized on their base class: interface IFoo { void action (); } class Foo : IFoo { this () {} void action () { // perform action } } class FooLogging (T : IFoo) : T { Logger logger; this () { logger = new Logger; } override void action () { logger.info("performing action"); super.action(); // perform action } } void main () { Foo foo; static if (logging) foo = new FooLogging!Foo; else foo = new Foo; foo.action (); } You get separation of concerns: the logging features are in FooLogging, overriding the basic behaviour in Foo; you can add a FooEvent mixin class to deal with events and so on to deal with any concerns you need. The "weaving" is done in the main() function in my example. You could prefer to put a big `static if (logging)` in the FooLogging class body and always instanciate foo instances with the full chain of mixin classes (foo = new FooLogging!(FooEvent!(FooAuthentication!(...!Foo)))), or anything else that fits you better. This works nicely if your "aspect" (Logging, Events...) applies on the whole action() method. Otherwise, you'll have to cut action() into smaller methods that will have to be decorated in the appropriate mixin class(es) (as the Strategy design pattern). I am eager to know if anybody else has a better (or more concise) solution. I hope this helps. On Sunday, 24 November 2013 at 13:18:15 UTC, Jacob Carlborg wrote:Does anyone know a good way of composing features at compile time? Say I have a struct or class that I want to support different features that are configurable at compile time. One way would be to just pass in a couple of boolean flags and use static-if, like this: class Foo (bool logging, bool events) { static if (logging) Logger logger; this () { static if (logging) logger = new Logger; } void action () { static if (logging) logger.info("performing action"); // perform action } } Using this approach I get the feeling that there will quickly become a big nest of static-ifs. Does anyone have any better ideas?
Nov 24 2013
On 2013-11-24 15:06, "Rémy Mouëza" wrote:This looks like a case study for aspect oriented programming: several separated concerns that start to intertwine within your code; if left unchecked this could result in some messy code. In Python, I used reflection and "magic" things and have already used the spring AOP in Java that is done with some Aspect classes and annotation. I suppose that D could bring the best of both world with its compile time reflection and UDA. But if you are in urgent need for a solution, here is a C++ trick called "mixin classes", template classes parameterized on their base class: interface IFoo { void action (); } class Foo : IFoo { this () {} void action () { // perform action } } class FooLogging (T : IFoo) : T { Logger logger; this () { logger = new Logger; } override void action () { logger.info("performing action"); super.action(); // perform action } } void main () { Foo foo; static if (logging) foo = new FooLogging!Foo; else foo = new Foo; foo.action (); } You get separation of concerns: the logging features are in FooLogging, overriding the basic behaviour in Foo; you can add a FooEvent mixin class to deal with events and so on to deal with any concerns you need. The "weaving" is done in the main() function in my example. You could prefer to put a big `static if (logging)` in the FooLogging class body and always instanciate foo instances with the full chain of mixin classes (foo = new FooLogging!(FooEvent!(FooAuthentication!(...!Foo)))), or anything else that fits you better.I thought of doing something similar with template mixins.This works nicely if your "aspect" (Logging, Events...) applies on the whole action() method. Otherwise, you'll have to cut action() into smaller methods that will have to be decorated in the appropriate mixin class(es) (as the Strategy design pattern).Unfortunately logging and events were more of an example, for the real usage it's not as easy to add before or after actions.I am eager to know if anybody else has a better (or more concise) solution. I hope this helps.-- /Jacob Carlborg
Nov 24 2013
Unfortunately logging and events were more of an example, for the real usageTry not to think to much of AST macros :-)
Nov 24 2013
The general problem is to inject code at predetermined points. As Rémy said, that should remind us of AOP. I have three other very vague ideas ;-) Not tested in any way. Interceptor will act as a Foo, except it will have hooks that are called before and after any calls to "__ctor" (so Foo.this()) and Foo.bar. The good: I imagine the hooks can be changed at runtime. By default, they do nothing. The bad: Interceptor!(Foo) is not a Foo anymore. But then, your own Foo!(option0) is not related to Foo!(option1) also. The ugly: the pre- and post-hooks could all have different types :-( (from void delegate() to... whatever). mixin?). The real methods are private, and opDispatch catch the calls, calls the pre-hook, then the inner method, then the post-hook. the strings! Define Foo only as a string to be compiled, with some points where string interpolation is possible. string makeFoo(string hooks()...) { return "struct Foo { " ~ hook[0]() ~ ... } The good: you can now inject parameterized code anywhere you want into Foo code. The bad: well, string mixins. You're coding blind.
Nov 24 2013
Well you can make one step forward and use policy-based design as described in Andrei's old C++ book ;) (replace boolean flags with template aliases to mixin, providing stub ones for "false") It does not fix `static-if` sequence but scales better in terms of configurability.
Nov 24 2013
But how do you use policies to inject different code in different places of your struct? On Sun, Nov 24, 2013 at 3:15 PM, Dicebot <public dicebot.lv> wrote:Well you can make one step forward and use policy-based design as described in Andrei's old C++ book ;) (replace boolean flags with template aliases to mixin, providing stub ones for "false") It does not fix `static-if` sequence but scales better in terms of configurability.
Nov 24 2013
On Sunday, 24 November 2013 at 14:23:55 UTC, Philippe Sigaud wrote:But how do you use policies to inject different code in different places of your struct?Don't really get the question. Isn't it what policies do pretty much by definition? You decompose part of target functionality into smaller blocks with optional behavior (including no-op behavior) and provide specific ones as alias parameters. D has additional benefit of being able to use one as template mixin for cleaner syntax. But I am pretty sure you know this, so what is the question again? :)
Nov 24 2013
On Sunday, 24 November 2013 at 17:25:49 UTC, Dicebot wrote:Don't really get the question. Isn't it what policies do pretty much by definition? You decompose part of target functionality into smaller blocks with optional behavior (including no-op behavior) and provide specific ones as alias parameters. D has additional benefit of being able to use one as template mixin for cleaner syntax. But I am pretty sure you know this, so what is the question again? :)To clarify, OP example will look like this: class Foo (alias Logger) if (isLoggger!Logger) { private Logger logger; this () { logger = new Logger; } void action () { logger.info("performing action"); // perform action } } // ... auto foo = new Foo!StubLogger(); // "false" case It works better with compilers like LDC of course, because of good inlining and AST restructuring during optimization pass.
Nov 24 2013
On 2013-11-24 18:31, Dicebot wrote:To clarify, OP example will look like this: class Foo (alias Logger) if (isLoggger!Logger) { private Logger logger; this () { logger = new Logger; } void action () { logger.info("performing action"); // perform action } } // ... auto foo = new Foo!StubLogger(); // "false" caseSo StubLogger would implement "info" but do nothing? How good would the compiler be at optimizing this?It works better with compilers like LDC of course, because of good inlining and AST restructuring during optimization pass.-- /Jacob Carlborg
Nov 24 2013
On Sunday, 24 November 2013 at 19:37:44 UTC, Jacob Carlborg wrote:So StubLogger would implement "info" but do nothing? How good would the compiler be at optimizing this?Last time I checked such stuff LDC is quite capable at eliminating it completely. It works somewhat better for state-less stuff though as you still get extra pointer overhead because of instantiated field. If you could call static methods instead, only overhead will be of few extra unused symbols in binary.
Nov 24 2013
On Sun, Nov 24, 2013 at 6:25 PM, Dicebot <public dicebot.lv> wrote:On Sunday, 24 November 2013 at 14:23:55 UTC, Philippe Sigaud wrote:Hmm. Then you still need to define where the policy will act. OK, I can get that. I thought the OP wanted something more flexible, where different loggers could inject code at many different places. But if there is no Logging policy asked for (the user doesn't care for logging), you must insure that the default policy 'disappears', so to speak. I'm not sure that's always possible.But how do you use policies to inject different code in different places of your struct?Don't really get the question. Isn't it what policies do pretty much by definition? You decompose part of target functionality into smaller blocks with optional behavior (including no-op behavior) and provide specific ones as alias parameters. D has additional benefit of being able to use one as template mixin for cleaner syntax. But I am pretty sure you know this, so what is the question again? :)Exactly.auto foo = new Foo!StubLogger(); // "false" caseSo StubLogger would implement "info" but do nothing? How good would the compiler be at optimizing this?
Nov 24 2013
On 2013-11-24 20:53, Philippe Sigaud wrote:Hmm. Then you still need to define where the policy will act. OK, I can get that. I thought the OP wanted something more flexible, where different loggers could inject code at many different places. But if there is no Logging policy asked for (the user doesn't care for logging), you must insure that the default policy 'disappears', so to speak. I'm not sure that's always possible.Please see this post for an actual problem I'm trying to solve: http://forum.dlang.org/thread/l6suan$22f4$1 digitalmars.com?page=2#post-l6tlhj:242ms1:241:40digitalmars.com -- /Jacob Carlborg
Nov 24 2013
Jacob Carlborg wrote:Does anyone know a good way of composing features at compile time? Say I have a struct or class that I want to support different features that are configurable at compile time. One way would be to just pass in a couple of boolean flags and use static-if, like this: class Foo (bool logging, bool events) { static if (logging) Logger logger; this () { static if (logging) logger = new Logger; } void action () { static if (logging) logger.info("performing action"); // perform action } } Using this approach I get the feeling that there will quickly become a big nest of static-ifs. Does anyone have any better ideas?Jakob, whenever I need something like you describe, I do more/less the same what is described on this Wikipedia page: exactly how I do this (in Java and D). -- Dejan Lekic dejan.lekic (a) gmail.com http://dejan.lekic.org
Nov 24 2013
On 2013-11-24 19:28, Dejan Lekic wrote:Jakob, whenever I need something like you describe, I do more/less the same what is described on this Wikipedia page: exactly how I do this (in Java and D).Unfortunately this looks like it works best when providing fairly separate features that doesn't interact with each other or the main functionality. Perhaps I should describe the real usage I have for this. I'm trying to figure out a good way to make std.serialization more composeable without any overhead for unwanted features. One example is this function: http://pastebin.com/XYGtTarn Lines 19-25, 34-37 and 42 are only needed if pointers are supported. Lines 27-30 are only needed if references are supported. There are already both a couple of static-ifs and regular ifs. Adding even more static-ifs will make the code a bigger mess than it already is. -- /Jacob Carlborg
Nov 24 2013
Unfortunately this looks like it works best when providing fairly separate features that doesn't interact with each other or the main functionality.If features are coupled, I see no easy way out.http://pastebin.com/XYGtTarn Lines 19-25, 34-37 and 42 are only needed if pointers are supported. Lines 27-30 are only needed if references are supported. There are already both a couple of static-ifs and regular ifs. Adding even more static-ifs will make the code a bigger mess than it already is.If references are not supported, you want the else clause all by itself? (lignes 34-43)
Nov 24 2013
On 2013-11-24 21:14, Philippe Sigaud wrote:If features are coupled, I see no easy way out.I suspected that. They not so much coupled with each other, rather coupled with the default functionality that will always be there.If references are not supported, you want the else clause all by itself? (lignes 34-43)Yes, exactly. -- /Jacob Carlborg
Nov 24 2013
On Sun, Nov 24, 2013 at 9:44 PM, Jacob Carlborg <doob me.com> wrote:On 2013-11-24 21:14, Philippe Sigaud wrote:I don't see how to do that, except by using flags & string mixins. It'll still be the same intricate (and not so easily extensible) code, but at least by using string mixins the functionality can be defined elsewhere and reused. I suppose that the standard advice would be to decouple features as much as possible, but then you probably already do that :)If features are coupled, I see no easy way out.I suspected that. They not so much coupled with each other, rather coupled with the default functionality that will always be there.
Nov 24 2013
On 2013-11-24 14:18, Jacob Carlborg wrote:Does anyone know a good way of composing features at compile time? Say I have a struct or class that I want to support different features that are configurable at compile time. One way would be to just pass in a couple of boolean flags and use static-if, like this:Here's an actual problem I'm trying to solve: http://forum.dlang.org/thread/l6suan$22f4$1 digitalmars.com?page=2#post-l6tlhj:242ms1:241:40digitalmars.com -- /Jacob Carlborg
Nov 24 2013
Based on what your actual problem is -- it seems like you need to refactor your code a little. Also, you should trust that the compiler optimizes correctly. eg. if( valueMeta.isValid && pointersSupported) should be optimized out when pointerSupported == false and the comparison of it should be optimized out when it is true.
Nov 24 2013