www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Pointer to method C++ style

reply Sergey Gromov <snake.scaly gmail.com> writes:
Is there a way to declare and statically initialize some sort of pointer
to method, and later call it for an actual object instance?

Use case: I'm writing an ActiveX plug-in for a dynamic language.  The
language queries the component for implemented methods, then it requests
these methods to be executed.  I want to implement these methods as the
component class non-static members, and create a lookup table at compile
time, sort of

struct Method
{
    string name;
    PointerToMember method;  // some magic goes here
}

class Component : IUnknown
{
    void method1() {}
    void method2() {}

    static Method[] LOOKUP_TABLE =
    [
        { "method1", &method1 },  // won't work, &method1 is non-const
        { "method2", &method2 }
    ];
    
    void call(int i)
    {
        this->*LOOKUP_TABLE[i].method(); // sort of
    }
}
Jul 22 2009
next sibling parent reply BCS <none anon.com> writes:
Hello Sergey,

 Is there a way to declare and statically initialize some sort of
 pointer to method, and later call it for an actual object instance?
 
dosn't work but might point you in the right direction: template Pn2Fn(R, char[] method) { ReturnTypeOf!(mixin("R." ~ method)) Pn2Fn(R r, ArgsOf!(mixin("R." ~ method)) args) { return mixin("r."~method~"(args);"); } } // use auto it = &Pn2Fn!(Type, "method"); //call as it(type);
Jul 22 2009
parent Sergey Gromov <snake.scaly gmail.com> writes:
Thu, 23 Jul 2009 04:11:14 +0000 (UTC), BCS wrote:

 Hello Sergey,
 
 Is there a way to declare and statically initialize some sort of
 pointer to method, and later call it for an actual object instance?
 
dosn't work but might point you in the right direction: template Pn2Fn(R, char[] method) { ReturnTypeOf!(mixin("R." ~ method)) Pn2Fn(R r, ArgsOf!(mixin("R." ~ method)) args) { return mixin("r."~method~"(args);"); } }
Thanks for the advice. The following code actually works: import std.stdio; struct Handler(T) { string name; void function(T instance) handler; } class A { void method1() { writefln("method1"); } void method2() { writefln("method2"); } void call(int i) { LOOKUP_TABLE[i].handler(this); } private: static Handler!(A)[] LOOKUP_TABLE = [ { "method1", &callMethod!("method1") }, { "method2", &callMethod!("method2") } ]; static void callMethod(string name)(A instance) { mixin("instance." ~ name ~ "();"); } } void main() { A a = new A; a.call(0); a.call(1); }
Jul 23 2009
prev sibling next sibling parent reply BLS <windevguy hotmail.de> writes:
Sergey Gromov wrote:
 Use case: I'm writing an ActiveX plug-in for a dynamic language.  The
 class Component : IUnknown
WOW! But shouldn't you use class Component : IDispatch { HRESULT QueryInterface( REFIID riid, LPVOID * ppvObj) {}; ULONG AddRef() {}; ULONG Release(){}; // plus IDispatch methods // GetTypeInfoCount, GetTypeInfo, GetIDsOfNames, Invoke. // to support late binding ? } instead ? Beside, John C. (Juno library) is a specialist (guru) regarding this topic. maybe he is willing to help. However, I am _very_ interested in having/seeing the source of a very basic ActiveX control. Any chance that you share the core implementation with us ? björn
Jul 23 2009
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Thu, 23 Jul 2009 12:37:42 +0200, BLS wrote:

Sergey Gromov wrote:
 Use case: I'm writing an ActiveX plug-in for a dynamic language.  The
However, I am _very_ interested in having/seeing the source of a very basic ActiveX control. Any chance that you share the core implementation with us ?
Sorry, I'm not a guru at all, so ActiveX was a misnomer. What I'm writing is a simple in-process server DLL which implements a couple of interfaces.
Jul 23 2009
parent reply BLS <windevguy hotmail.de> writes:
Sergey Gromov wrote:
 Thu, 23 Jul 2009 12:37:42 +0200, BLS wrote:
 
 Sorry, I'm not a guru at all, so ActiveX was a misnomer.  What I'm
 writing is a simple in-process server DLL which implements a couple of
 interfaces.
Oh, that's sad. :( well, especially in this case I would suggest to have a look on this page : http://www.dsource.org/projects/juno/wiki/ComProgramming --- I think John's juno.com.server module contains almost everything you need. good luck
Jul 23 2009
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Thu, 23 Jul 2009 19:07:43 +0200, BLS wrote:

 Sergey Gromov wrote:
 Sorry, I'm not a guru at all, so ActiveX was a misnomer.  What I'm
 writing is a simple in-process server DLL which implements a couple of
 interfaces.
Oh, that's sad. :( well, especially in this case I would suggest to have a look on this page : http://www.dsource.org/projects/juno/wiki/ComProgramming
Thanks, I'll look into it when I have time.
Jul 23 2009
parent Sergey Gromov <snake.scaly gmail.com> writes:
Fri, 24 Jul 2009 02:51:45 +0400, Sergey Gromov wrote:

 Thu, 23 Jul 2009 19:07:43 +0200, BLS wrote:
 
 Sergey Gromov wrote:
 Sorry, I'm not a guru at all, so ActiveX was a misnomer.  What I'm
 writing is a simple in-process server DLL which implements a couple of
 interfaces.
Oh, that's sad. :( well, especially in this case I would suggest to have a look on this page : http://www.dsource.org/projects/juno/wiki/ComProgramming
Thanks, I'll look into it when I have time.
Juno is nice, and implements a lot of boilerplate. But it doesn't seem to care enough about exceptions in user code. My own code wraps every interface function in an exception catching block, and I'd better leave it that way.
Jul 24 2009
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 22 Jul 2009 23:47:30 -0400, Sergey Gromov <snake.scaly gmail.com>  
wrote:

 Is there a way to declare and statically initialize some sort of pointer
 to method, and later call it for an actual object instance?
I don't know why the "non constant expression error" happens, but constructing a delegate from function pointers is pretty simple: import tango.io.Stdout; struct Method { char[] name; void function() method; } class Component { void method1() {Stdout("method1").newline;} void method2() {Stdout("method2").newline;} // this works, but not sure why I couldn't initialize via a simple array assign... static Method[] LOOKUP_TABLE; static this() { LOOKUP_TABLE = new Method[2]; LOOKUP_TABLE[0] = Method("method1", &Component.method1); LOOKUP_TABLE[1] = Method("method2", &Component.method2); } void call(int i) { // construct a delegate to call void delegate() dg; dg.ptr = cast(void*)this; dg.funcptr = LOOKUP_TABLE[i].method; // call the delegate dg(); } } void main() { Component a = new Component; a.call(0); a.call(1); } ------- outputs: method1 method2 -Steve
Jul 23 2009
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Thu, 23 Jul 2009 11:54:40 -0400, Steven Schveighoffer wrote:

 On Wed, 22 Jul 2009 23:47:30 -0400, Sergey Gromov <snake.scaly gmail.com>  
 wrote:
 
 Is there a way to declare and statically initialize some sort of pointer
 to method, and later call it for an actual object instance?
I don't know why the "non constant expression error" happens, but constructing a delegate from function pointers is pretty simple:
It's my understanding that you cannot construct a delegate from a function pointer because they use different calling conventions. Though you show here that it *is* possible to construct a delegate from another delegate you dissected earlier.
          LOOKUP_TABLE[0] = Method("method1", &Component.method1);
          LOOKUP_TABLE[1] = Method("method2", &Component.method2);
These two lines are weird. ``pragma(msg)`` shows that type of ``&method1`` is ``void function()`` while it must be ``void delegate()`` for a non-static member because of difference in calling convention. Actually I think that taking an address of a non-static member in a static context must be a compile time error.
Jul 23 2009
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 23 Jul 2009 22:09:12 -0400, Sergey Gromov <snake.scaly gmail.com>  
wrote:

 Thu, 23 Jul 2009 11:54:40 -0400, Steven Schveighoffer wrote:

 On Wed, 22 Jul 2009 23:47:30 -0400, Sergey Gromov  
 <snake.scaly gmail.com>
 wrote:

 Is there a way to declare and statically initialize some sort of  
 pointer
 to method, and later call it for an actual object instance?
I don't know why the "non constant expression error" happens, but constructing a delegate from function pointers is pretty simple:
It's my understanding that you cannot construct a delegate from a function pointer because they use different calling conventions. Though you show here that it *is* possible to construct a delegate from another delegate you dissected earlier.
          LOOKUP_TABLE[0] = Method("method1", &Component.method1);
          LOOKUP_TABLE[1] = Method("method2", &Component.method2);
These two lines are weird. ``pragma(msg)`` shows that type of ``&method1`` is ``void function()`` while it must be ``void delegate()`` for a non-static member because of difference in calling convention. Actually I think that taking an address of a non-static member in a static context must be a compile time error.
It's because I'm taking the address of the function on the type, not on an instance. It's not a delegate because there's no "this" pointer yet. It makes sense to me anyways. A delegate is a normal function pointer coupled with a hidden context parameter. I think it should be possible to construct the table statically, since the functions exist statically (without 'this' pointers). Either I can't find the right syntax, or it is a bug. -Steve
Jul 24 2009
next sibling parent reply grauzone <none example.net> writes:
          LOOKUP_TABLE[0] = Method("method1", &Component.method1);
          LOOKUP_TABLE[1] = Method("method2", &Component.method2);
These two lines are weird. ``pragma(msg)`` shows that type of ``&method1`` is ``void function()`` while it must be ``void delegate()`` for a non-static member because of difference in calling convention. Actually I think that taking an address of a non-static member in a static context must be a compile time error.
It's because I'm taking the address of the function on the type, not on an instance. It's not a delegate because there's no "this" pointer yet. It makes sense to me anyways. A delegate is a normal function pointer coupled with a hidden context parameter.
But you can't call that function pointer. Actually, you can probably subvert type safety, because functions have a different calling conventions from delegates. This also means that SafeD should disallow taking the address of methods from a type (without instance). That's really silly. A nicer way would be to make &Type.method return a delegate with ptr set to null. Then calling this delegate would result in a (harmless) null pointer exception. But even then, there's no safe way to construct a real delegate out of the method pointer. You can't simply assign an object instance to ptr, because you can't statically know if the funcptr of the delegate really is a method of that object instance. Looks like SafeD proves to be unfeasible again.
Jul 24 2009
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 24 Jul 2009 09:56:41 -0400, grauzone <none example.net> wrote:

          LOOKUP_TABLE[0] = Method("method1", &Component.method1);
          LOOKUP_TABLE[1] = Method("method2", &Component.method2);
These two lines are weird. ``pragma(msg)`` shows that type of ``&method1`` is ``void function()`` while it must be ``void delegate()`` for a non-static member because of difference in calling convention. Actually I think that taking an address of a non-static member in a static context must be a compile time error.
It's because I'm taking the address of the function on the type, not on an instance. It's not a delegate because there's no "this" pointer yet. It makes sense to me anyways. A delegate is a normal function pointer coupled with a hidden context parameter.
But you can't call that function pointer. Actually, you can probably subvert type safety, because functions have a different calling conventions from delegates. This also means that SafeD should disallow taking the address of methods from a type (without instance). That's really silly. A nicer way would be to make &Type.method return a delegate with ptr set to null. Then calling this delegate would result in a (harmless) null pointer exception. But even then, there's no safe way to construct a real delegate out of the method pointer. You can't simply assign an object instance to ptr, because you can't statically know if the funcptr of the delegate really is a method of that object instance.
You mean no *compiler verifyable* safe way. Of course there are provably safe ways to do it (my code is one of them).
 Looks like SafeD proves to be unfeasible again.
That's why there are system modules ;) Note that a delegate's ptr method is a pointer, so you can't assign it/use it from SafeD anyways. I'd propose a system module that defines an "unbound delegate" type. This would be a function pointer coupled parameterized with a type, which has an opCall(T, ...) that would call the function properly. I think it can be done, but I'm not enough of a template guru to do it myself. That could be used in SafeD. -Steve
Jul 24 2009
prev sibling parent Sergey Gromov <snake.scaly gmail.com> writes:
Fri, 24 Jul 2009 09:07:30 -0400, Steven Schveighoffer wrote:

 On Thu, 23 Jul 2009 22:09:12 -0400, Sergey Gromov <snake.scaly gmail.com>  
 wrote:
 
 Thu, 23 Jul 2009 11:54:40 -0400, Steven Schveighoffer wrote:

          LOOKUP_TABLE[0] = Method("method1", &Component.method1);
          LOOKUP_TABLE[1] = Method("method2", &Component.method2);
These two lines are weird. ``pragma(msg)`` shows that type of ``&method1`` is ``void function()`` while it must be ``void delegate()`` for a non-static member because of difference in calling convention. Actually I think that taking an address of a non-static member in a static context must be a compile time error.
It's because I'm taking the address of the function on the type, not on an instance. It's not a delegate because there's no "this" pointer yet. It makes sense to me anyways. A delegate is a normal function pointer coupled with a hidden context parameter.
The ``Type.`` part does not change anything. It simply directs compiler to use overloads from a particular class hierarchy level. Here: static Method[] LOOKUP_TABLE2 = [ { name : "method1", method : &Component.method1 }, { name : "method2", method : &method2 } ]; This code compiled with DMD 1.046 gives the following errors: test2.d(30): Error: non-constant expression & method1 test2.d(30): Error: non-constant expression & method2 Also if you add this code: pragma(msg, "outside: " ~ typeof(&method1).stringof); void method1() { writefln("method1"); pragma(msg, "inmeth: " ~ typeof(&method1).stringof); } you get: outside: void function() inmeth: void delegate() So the type of a non-static method address taken in a static context is obviously wrong.
Jul 24 2009