Jump to page: 1 2
Thread overview
Composing features at compile time
Nov 24, 2013
Jacob Carlborg
Nov 24, 2013
Rémy Mouëza
Nov 24, 2013
Jacob Carlborg
Nov 24, 2013
Philippe Sigaud
Nov 24, 2013
Philippe Sigaud
Nov 24, 2013
Dicebot
Nov 24, 2013
Philippe Sigaud
Nov 24, 2013
Dicebot
Nov 24, 2013
Dicebot
Nov 24, 2013
Jacob Carlborg
Nov 24, 2013
Dicebot
Nov 24, 2013
Philippe Sigaud
Nov 24, 2013
Jacob Carlborg
Nov 24, 2013
Dejan Lekic
Nov 24, 2013
Jacob Carlborg
Nov 24, 2013
Philippe Sigaud
Nov 24, 2013
Jacob Carlborg
Nov 24, 2013
Philippe Sigaud
Nov 24, 2013
Jacob Carlborg
Nov 25, 2013
Shammah Chancellor
November 24, 2013
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
November 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?

November 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
November 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.

#1) Create a wrapper, say Interceptor!(Foo, "__ctor", "bar").
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.
Bad #2: no hook inside a method, they are only called before or after a method.
The ugly: the pre- and post-hooks could all have different types :-(
(from void delegate() to... whatever).

#2) Same idea, but the intercepting code is inside Foo (maybe with a mixin?). The real methods are private, and opDispatch catch the calls, calls the pre-hook, then the inner method, then the post-hook.


#3) The string mixin road. I know what you're thinking. Learn to love
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.

November 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.
November 24, 2013
> Unfortunately logging and events were more of an example, for the real usage

Ah then, you can discard my own proposals. Only #3 is still possible. Try not to think to much of AST macros :-)
November 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.
November 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? :)
November 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.
November 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: http://en.wikipedia.org/wiki/Composition_over_inheritance . C# example is exactly how I do this (in Java and D).

-- 
Dejan Lekic
dejan.lekic (a) gmail.com
http://dejan.lekic.org
« First   ‹ Prev
1 2