Jump to page: 1 2
Thread overview
Passing member function pointer to external C functions.
Jan 29, 2005
Kris
Jan 29, 2005
pragma
Jan 29, 2005
pragma
Jan 30, 2005
Andrew Fedoniouk
Jan 30, 2005
Andrew Fedoniouk
Re: [OT] Passing member function pointer to external C functions.
Re: [very OT] Passing member function pointer to external C functions.
Feb 01, 2005
h3r3tic
Jan 30, 2005
Russ Lewis
January 29, 2005
I'm trying to register member function of one of my class as expat handler. Of course expat functions are taking pointer to a function and are declared in extern(C) scope. I understand that member functions are in fact normal functions invisibly taking "this" argument and so is this possible to do such thing in D? If answer is "yes": how and "WOW I love this language"; if answer is "no": what would be the best workaround.

For clarifying this is what I'm trying to do:

extern(C){

 void XML_SetElementHandler(XML_Parser p, XML_StartElementHandler start,
   XML_EndElementHandler end);

 typedef void function(void *userData, XML_Char *name, XML_Char **atts)
   XML_StartElementHandler;

 typedef void function(void *userData, XML_Char *name)
   XML_EndElementHandler;
}

class Foo{

 this(){
 _parser = XML_ParserCreate(null);
 XML_SetElementHandler(_parser,
  cast(XML_StartElementHandler)&this.startHandle,
  cast(XML_EndElementHandler)&this.endHandle);
 }

extern(C){
 void startHandle(void *userData,XML_Char *name,XML_Char **atts){
  printf("started: %sn",name);
 }
 void endHandle(void *userData,XML_Char *name){
  printf("ended: %sn",name);
 }

}

I get:
e2ir: cannot cast from void delegate(void*userData,char*name,char**atts) to
void(C *)(void*userData,char*name,char**atts)
e2ir: cannot cast from void delegate(void*userData,char*name) to void(C *
(void*userData,char*name)

Regards,
-- 
Dawid Ciężarkiewicz | arael
jid: arael@fov.pl
January 29, 2005
I think you can do that by making the class members static ...

- Kris

In article <cteolm$12va$1@digitaldaemon.com>, Dawid =?ISO-8859-2?Q?Ci=EA=BFarkiewicz?= says...
>
>I'm trying to register member function of one of my class as expat handler. Of course expat functions are taking pointer to a function and are declared in extern(C) scope. I understand that member functions are in fact normal functions invisibly taking "this" argument and so is this possible to do such thing in D? If answer is "yes": how and "WOW I love this language"; if answer is "no": what would be the best workaround.
>
>For clarifying this is what I'm trying to do:
>
>extern(C){
>
> void XML_SetElementHandler(XML_Parser p, XML_StartElementHandler start,
>   XML_EndElementHandler end);
>
> typedef void function(void *userData, XML_Char *name, XML_Char **atts)
>   XML_StartElementHandler;
>
> typedef void function(void *userData, XML_Char *name)
>   XML_EndElementHandler;
>}
>
>class Foo{
>
> this(){
> _parser = XML_ParserCreate(null);
> XML_SetElementHandler(_parser,
>  cast(XML_StartElementHandler)&this.startHandle,
>  cast(XML_EndElementHandler)&this.endHandle);
> }
>
>extern(C){
> void startHandle(void *userData,XML_Char *name,XML_Char **atts){
>  printf("started: %sn",name);
> }
> void endHandle(void *userData,XML_Char *name){
>  printf("ended: %sn",name);
> }
>
>}
>
>I get:
>e2ir: cannot cast from void delegate(void*userData,char*name,char**atts) to
>void(C *)(void*userData,char*name,char**atts)
>e2ir: cannot cast from void delegate(void*userData,char*name) to void(C *
>(void*userData,char*name)
>
>Regards,
>-- 
>Dawid Ciê¿arkiewicz | arael
>jid: arael@fov.pl


January 29, 2005
These are the culprit lines from your post:

>  cast(XML_StartElementHandler)&this.startHandle,
>  cast(XML_EndElementHandler)&this.endHandle);

I understand what you're trying to do, as I thought the same thing myself: since D passes 'this' as the first argument to a method, why not pass along my class's methods to Expat since they happen to match the same spec.

This would work amazingly well in C++ but D doesn't like it (obviously). There's a lot more going on in those lines than meets the eye:

1) D converts "&this.startHandle" into a *delegate*, which contains a reference
to 'this' as well as the address of "startHandle".
2) D does not provide a means for casting a delegate into a function, so the
cast to XML_StartElementHandler fails miserably.

A more complete explaination of the underpinnings of delegates is here:

http://www.prowiki.org/wiki4d/wiki.cgi?DocComments/Function

Now what you're asking for is a hack to crack the function pointer out of a delegate.  I don't advocate this in the slightest, since it's probing around a grey area that may or may not be changed in the D ABI.

> import std.stdio;
> 
> struct DelegateStruct {
> 	void *ptr;
> 	void *func;
> };
> 
> class Test{
> 	extern(C) int myMethod(int a){
> 		writefln("A is %d",a);
> 		return a+1;
> 	}
> }
> 
> extern(C) typedef int delegate(int) TestDelegate;
> extern(C) typedef int function(void*,int) TestFunctionHandle;
> 
> int testFunction(void* thisPtr,TestFunctionHandle handle,int a){
> 	return handle(thisPtr,a);
> }
> 
> void main(){
> 	Test t = new Test();
> 	writefln("calling directly");
> 	writefln("result is: %d",t.myMethod(42));
> 	TestDelegate del = &t.myMethod;
> 	DelegateStruct ds = *cast(DelegateStruct*)cast(void**)&del;
> 	TestFunctionHandle handle = cast(TestFunctionHandle)ds.func;
> 	writefln("calling indirectly");
> 	writefln("result is: %d",testFunction(cast(void*)t,handle,42));
> }

I'm sure there's probably a way to use a template and/or mixin to make this much more clean.

The secret here is the liberal use of "extern(C)".  The default D linkage likes to throw the last argument around as EAX, which happens to be "this" for object.method calls (at least for DMD, I don't know what GDC is up to).


- EricAnderton at yahoo
January 29, 2005
Kris wrote:

> I think you can do that by making the class members static ...

Well, right but this way I lose information stored in "this" and I don't know on which object I work. I was trying to write multithreaded server application that communicates with xml. Each connection has to be separately parsed. I need tons of parser - each knowing almost nothing about other.
-- 
Dawid Ciężarkiewicz | arael
jid: arael@fov.pl
January 29, 2005
pragma wrote:

> I'm sure there's probably a way to use a template and/or mixin to make this much more clean.
> 
> The secret here is the liberal use of "extern(C)".  The default D linkage likes to throw the last argument around as EAX, which happens to be "this" for object.method calls (at least for DMD, I don't know what GDC is up to).

This is nice hack, but I was thinking about something more portable, stable etc. As I understand whole hack is set on fact that EAX isn't changed and stores "this" pointer, right? Can I really depend on this in multithreaded environment using external C callback? I mean - I will register such function as handler, then many things will happen (EAX will definitely change) and then expat will call my handler and I don't think it will set EAX to something usable. If I understand this will end with random "this" and segfault.

I'm not sure if I understood you so sorry if I'm jabbering.
-- 
Dawid Ciężarkiewicz | arael
jid: arael@fov.pl
January 29, 2005
Dawid Ciężarkiewicz wrote:

> I'm trying to register member function of one of my class as expat handler. Of course expat functions are taking pointer to a function and are declared in extern(C) scope. I understand that member functions are in fact normal functions invisibly taking "this" argument and so is this possible to do such thing in D? If answer is "yes": how and "WOW I love this language"; if answer is "no": what would be the best workaround.

I've found my personal solution. Because every class instance that need his own parser is new thread I register static member function as handler that use "getThis()" to run good instance of member function.

Like this:

static void StartHandler(void *userData,XML_Char *name,XML_Char **atts){
  (cast(ConnectionHandler)getThis()).startHandle(userData,name,atts);
}


-- 
Dawid Ciężarkiewicz | arael
jid: arael@fov.pl
January 29, 2005
In article <ctfqta$287m$1@digitaldaemon.com>, Dawid =?ISO-8859-2?Q?Ci=EA=BFarkiewicz?= says...
>
>pragma wrote:
>
>> I'm sure there's probably a way to use a template and/or mixin to make this much more clean.
>> 
>> The secret here is the liberal use of "extern(C)".  The default D linkage likes to throw the last argument around as EAX, which happens to be "this" for object.method calls (at least for DMD, I don't know what GDC is up to).
>
>This is nice hack, but I was thinking about something more portable, stable etc. As I understand whole hack is set on fact that EAX isn't changed and stores "this" pointer, right?

As long as you use "extern(C)" in all the places illustrated, D will compile
those methods/functions as 'stdcall' which is the C standard (no EAX mojo).  If
you try removing "extern(C)", so the compiler uses the D linkage instead, it
segfaults all over the place.

A hack is a hack. I don't advocate using it unless it gives you a sound advantage that can't be met otherwise.  If it passes muster with testing on all your target platforms, then techically its sound code.  I for one never code with a hack unless it achives a requirement, like better performance or a reduced memory footprint; and even then its a last resort.  Plus I document the heck out of it (flagging it with "//HACK:") to warn other developers of what, why and how.


- EricAnderton at yahoo
January 30, 2005
Hi, Dawid,

Why not to use something simple as below?
(It's just a scetch, never compiled it)

class MyHandler
{
 void startHandler(XML_Char *name,XML_Char **atts)
 {
  printf("started: %sn",name);
 }
 void endHandler(XML_Char *name)
 {
  printf("ended: %sn",name);
 }

 extern(C) static void _startHandler(void *userData,XML_Char *name,XML_Char
**atts)
 {
  (cast(MyHandler)(userData)).startHandler(name,atts);
 }
 extern(C) static void _endHandler(void *userData,XML_Char *name)
 {
  (cast(MyHandler)(userData)).endHandler(name);
 }
 void setup(XML_Parser p)
 {
     XML_SetUserData(p, cast(void*) this);
     XML_SetElementHandler(p,_startHandle,_endHandle);
 }
}

You can wrap it into template if you want to use it in multiple places.


January 30, 2005
Dawid Ciężarkiewicz wrote:
> I'm trying to register member function of one of my class as expat handler.
> Of course expat functions are taking pointer to a function and are declared
> in extern(C) scope. I understand that member functions are in fact normal
> functions invisibly taking "this" argument and so is this possible to do
> such thing in D? If answer is "yes": how and "WOW I love this language"; if
> answer is "no": what would be the best workaround.
> 
> For clarifying this is what I'm trying to do:
> 
> extern(C){
> 
>  void XML_SetElementHandler(XML_Parser p, XML_StartElementHandler start,
>    XML_EndElementHandler end);
> 
>  typedef void function(void *userData, XML_Char *name, XML_Char **atts)
>    XML_StartElementHandler;
> 
>  typedef void function(void *userData, XML_Char *name)
>    XML_EndElementHandler;
> }
> 
> class Foo{
> 
>  this(){
>  _parser = XML_ParserCreate(null);
>  XML_SetElementHandler(_parser,
>   cast(XML_StartElementHandler)&this.startHandle,
>   cast(XML_EndElementHandler)&this.endHandle);
>  }
> 
> extern(C){
>  void startHandle(void *userData,XML_Char *name,XML_Char **atts){
>   printf("started: %sn",name);
>  }
>  void endHandle(void *userData,XML_Char *name){
>   printf("ended: %sn",name);
>  }
> 
> }

You need to create an extern(C) wrapper for the class function, and store the 'this' pointer in a struct, which you use as the userdata. Here's the bare bones of an example, with some nice translation of C strings to D strings thrown in, to boot:

// override this class for your own uses...
class ExpatParser {
  void startHandler(XML_char[] name,XML_Char[][] attrs) { return; }
  void endHandler  (XML_char[] name) { return; }

  this() {
    _parser = XML_ParserCreate(null);

    ExpatParserUserData *ptr = new ExpatParserUserData[1];
    ptr.ref = this;
    XML_SetUserData(_parser, cast(void*)ptr);

    XML_SetElementHandler(_parser, &startHandler_c, &endHandler_c);
  };
};

struct ExpatParserUserData {
  ExpatParser ref;
};

extern(C) void startHandler_c(void *userdata_void,
                              XML_Char  *name_c,
                              XML_Char **attrs_c) {
  ExpatParserUserData *userdata =
          cast(ExpatParserUserData*)userdata_void;
  XML_Char[]   name = name_c[0..strlen(name_c)];
  XML_Char[][] attrs;
    for( ; *attrs_c != null; attrs_c++)
      attrs ~= (*attrs_c)[0..strlen(*attrs_c)];

  userdata.ref.startHandler(name,attrs);
}

you can write your own endHandler_c() the same way...

January 30, 2005
Andrew Fedoniouk wrote:

> Hi, Dawid,
> 
> Why not to use something simple as below?

Brilinat! I didn't know that user that is working like this. Thank you. This is very good answer to my problem.
-- 
Dawid Ciężarkiewicz | arael
jid: arael@fov.pl
« First   ‹ Prev
1 2