www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Function pointer callback from C

reply Mike Johnson <mrjohnson miketec.org> writes:
Hello,

I've been beating my head against D's delegate support. I've probably 
read every mention in the docs and post on the news server multiple 
times and I'm just not getting it. Any help would be awesome.

I'm currently trying to connect a signal handler to a member function.

The member function needs access to the `this' pointer. The callback 
itself works, but I get undefined weirdness and crashes on trying to 
modify any D member variables.

(This is GTK.)

The signal handler is installed:

         this.sourceBuffer.connectSignal("changed",
                                         &on_sv_changed);

The function doing black magic:

     extern(C) public void connectSignal(
         char[] signalName,
         void delegate(int* widget, gpointer data) func,
         int data = 0,
         GConnectFlags flags = cast(GConnectFlags) 0) {

         g_signal_connect_data(this.self,
                               String.stringz(signalName),
                               func.funcptr,
                               cast(gpointer) data,
                               cast(int *) 0,
                               flags);
     }

Of course, I can pass void* data through the gpointer variable. But I 
can't convert a class instance to an pointer... which would be really 
*cough* cool *cough* :-) But maybe something else?

The signal handler itself:

     private extern(C) void on_sv_changed(int *widget, int *data) {
         printf("changed\n");
	this.changed = true; // GTK spews warnings and dies weirdly
     }

Thanks so much,
Mike Johnson
Feb 25 2007
next sibling parent reply Tyler Knott <tywebmail mailcity.com> writes:
Mike Johnson wrote:
 
 Of course, I can pass void* data through the gpointer variable. But I 
 can't convert a class instance to an pointer... which would be really 
 *cough* cool *cough* :-) But maybe something else?
 

You can't convert /instances/, but you can cast any object /references/ to void pointers (because references are just pointers with syntactic sugar that hides all the details). When you cast back from void* to a class type, D even checks to make sure it's a sane cast (the cast will return null if it's not sane). The only caveat is that you're going to be careful to keep a reference to that object in memory the GC is aware of, otherwise it could get prematurely collected and cause weird runtime crashes.
Feb 25 2007
parent Mike Johnson <mrjohnson miketec.org> writes:
Aw, geez. You're right. Works perfectly. I guess I misunderstood 
something I had read.

Thanks,
Mike

For google cache:

connect the signal:

         this.sourceBuffer.connectSignal("changed",
                                         &on_sv_changed,
                                         cast(gpointer) this);

callback:

     private extern(C) void on_sv_changed(int *widget, gpointer data) {
         printf("changed\n");

         GtkSourceView v = cast(GtkSourceView) data;
         if(v)
             v.changed = true;
     }


Tyler Knott wrote:
 Mike Johnson wrote:
 Of course, I can pass void* data through the gpointer variable. But I 
 can't convert a class instance to an pointer... which would be really 
 *cough* cool *cough* :-) But maybe something else?

You can't convert /instances/, but you can cast any object /references/ to void pointers (because references are just pointers with syntactic sugar that hides all the details). When you cast back from void* to a class type, D even checks to make sure it's a sane cast (the cast will return null if it's not sane). The only caveat is that you're going to be careful to keep a reference to that object in memory the GC is aware of, otherwise it could get prematurely collected and cause weird runtime crashes.

Feb 25 2007
prev sibling next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Sun, 25 Feb 2007 22:12:51 -0800, Mike Johnson wrote:

 The function doing black magic:
 
      extern(C) public void connectSignal(

. . .
 The signal handler itself:
 
      private extern(C) void on_sv_changed(int *widget, int *data) {

Why do you use "extern (C)" ? -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 26/02/2007 5:35:23 PM
Feb 25 2007
parent Mike Johnson <mrjohnson miketec.org> writes:
I was having a lot of trouble getting the delegate declaration to work 
and dmd was saying something about not implicitly converting "void (C *) 
blah".

I didn't know how to declare a delegate with C conventions. When I added 
the extern(c), it works. I'm not sure I understand why yet.

Here's the error message without extern:
mainwindow.d(43): function gtk.gtkwidget.GtkWidget.connectSignal 
(char[],void delegate(int* widget, void* data),void*,GConnectFlags) does 
not match parameter types (char[11],void delegate(int* widget, void* 
data),void*,GConnectFlags)
mainwindow.d(44): Error: cannot implicitly convert expression 
(&this.on_toolNotebook_changePage) of type void delegate(int* widget, 
void* data) to void delegate(int* widget, void* data)

To my eyes, those types look the same. *shrugs*

Derek Parnell wrote:
 On Sun, 25 Feb 2007 22:12:51 -0800, Mike Johnson wrote:
 
 The function doing black magic:

      extern(C) public void connectSignal(

. . .
 The signal handler itself:

      private extern(C) void on_sv_changed(int *widget, int *data) {

Why do you use "extern (C)" ?

Feb 25 2007
prev sibling parent Alan Knowles <alan akbkhome.com> writes:
Did you know Ant has committed the SourceView bindings to subversion:

http://svn.dsource.org/projects/dui/trunk/gtkD/srcsv/gsv/

The bindings for addOnChanged() are in TextBuffer
http://svn.dsource.org/projects/dui/trunk/gtkD/src/gtk/TextBuffer.d

Regards
Alan


Mike Johnson wrote:
 Hello,
 
 I've been beating my head against D's delegate support. I've probably
 read every mention in the docs and post on the news server multiple
 times and I'm just not getting it. Any help would be awesome.
 
 I'm currently trying to connect a signal handler to a member function.
 
 The member function needs access to the `this' pointer. The callback
 itself works, but I get undefined weirdness and crashes on trying to
 modify any D member variables.
 
 (This is GTK.)
 
 The signal handler is installed:
 
         this.sourceBuffer.connectSignal("changed",
                                         &on_sv_changed);
 
 The function doing black magic:
 
     extern(C) public void connectSignal(
         char[] signalName,
         void delegate(int* widget, gpointer data) func,
         int data = 0,
         GConnectFlags flags = cast(GConnectFlags) 0) {
 
         g_signal_connect_data(this.self,
                               String.stringz(signalName),
                               func.funcptr,
                               cast(gpointer) data,
                               cast(int *) 0,
                               flags);
     }
 
 Of course, I can pass void* data through the gpointer variable. But I
 can't convert a class instance to an pointer... which would be really
 *cough* cool *cough* :-) But maybe something else?
 
 The signal handler itself:
 
     private extern(C) void on_sv_changed(int *widget, int *data) {
         printf("changed\n");
     this.changed = true; // GTK spews warnings and dies weirdly
     }
 
 Thanks so much,
 Mike Johnson

Feb 26 2007