April 25, 2008
Hi,

I'm trying to generate at compile-time an associative array of function pointers to class member functions where the key in the array is the name of the member function itself.

What my best attempt so far has been is as follows, which doesn't compile:

class ApplicationController
{
  void function()[char[]] action_map;

  this()
  {
    mixin ArrayMap!(build_code(__traits( derivedMembers,
ApplicationController )));
  }

  char[] build_code(invariant(char)[][] actions)
  {
    char[] code;
    foreach(invariant(char)[] action; actions)
      {
        code ~= "action_map[\"" ~ action ~ "\"] = &(" ~ action ~ ");";
      }
    return code;
  }

  template ArrayMap(char[] code)
  {
    const ArrayMap = code;
  }
}

Can anyone suggest other ways to achieve what I want to do? Or point out where I might be going wrong in the above?

Many thanks

Jason

P.S. The compiler error I get is:
applicationcontroller.d(9): Error: expression
this.build_code(["action_map","_ctor","build_code","ArrayMap"]) is not
a valid template value argument
April 25, 2008
"Jason Langenauer" <jason@jasonlangenauer.com> wrote in message news:mailman.466.1209133945.2351.digitalmars-d-learn@puremagic.com...
> Hi,
>
> I'm trying to generate at compile-time an associative array of function pointers to class member functions where the key in the array is the name of the member function itself.
>
> What my best attempt so far has been is as follows, which doesn't compile:
>
> class ApplicationController
> {
>  void function()[char[]] action_map;
>
>  this()
>  {
>    mixin ArrayMap!(build_code(__traits( derivedMembers,
> ApplicationController )));
>  }

Problem number 1: you're using the wrong kind of mixin.  You want a string mixin, which just involves putting parens around the mixin argument:

mixin(ArrayMap!(build_code(__traits(derivedMembers, ApplicationController))));

>  char[] build_code(invariant(char)[][] actions)
>  {
>    char[] code;
>    foreach(invariant(char)[] action; actions)
>      {
>        code ~= "action_map[\"" ~ action ~ "\"] = &(" ~ action ~ ");";
>      }
>    return code;
>  }

Problems 2 and 3: this function can't be evaluated at compile time for 2 reasons.  One, it's not static, so make it so.  Two, for some reason (that I really don't feel like wrapping my head around), the string concatenation fails as-is.  If you change it to:

code ~= "action_map[\"" ~ action.dup  ~ "\"] = &(" ~ action.dup ~ ");";

(note the dups), it works.

>  template ArrayMap(char[] code)
>  {
>    const ArrayMap = code;
>  }
> }

Now, all that will make it work like you expect.  However, it doesn't quite do what you want because (1) derivedMembers gives you all members including data members and not just methods, and (2) unless a member really is a void function(), you're not going to be able to put it in that AA.  If you take the address of a member function in a non-static context anyway, you're going to end up with a delegate, not a function pointer.  So either you'll have to filter, at compile time, the list of methods that you want to wrap and only allow those with a void() signature, or you'd have to do something a bit crazier and come up with some kind of variant delegate type that would allow any kind of delegate to be stored in that AA.