7

I have no problem with simple callbacks when free function passed as parameter to another, thanks to @flexo.

But assume bit more difficult C interface:

typedef struct
{
    int id;
    const char* name;
} Item;

typedef struct
{
    int value;
    Items_Callback callback;
    void *context;
} Items_Call;

typedef int (*Items_Callback)(const Item *item, void *context);

int Items_create(const Item *item, Items_Call *call) {
  ...
  call->callback(item, call->context);
  ...
}

I intent to generate some nice Java wrapper for code like this. I assume to have as result

class Item {
  public int id;
  public String name;
}

class Items_Call {
  public int value;
  public Object context;
  public Interface callback;
  public void setInterface(Interface i){ callback=i; };
}

public interface Interface {
  public int Items_Callback(Item item, Object context);
} 

int Items_create(Item item, Items_Call call) {
  ...
  call.callback.Items_Callback(item, call.context);
  ...
}

I realize that SWIG have some problem with generation of pure Java interfaces, but I believe it's not major problem. The problem is I have no idea how to reinterpret such nested structure to acceptable Java code.

2
  • Consider using JNA. You can map a callback to an interface, then either instantiate that interface manually or JNA will generate one if it finds one in a native structure. The callback is invokable by both Java and native code. Commented Dec 20, 2013 at 12:50
  • @technomage, thanks but performance and portability are crucial for me. Thus whould be nice to use JNI. Commented Dec 20, 2013 at 17:37

1 Answer 1

4
+300

Not SWIG, but the following works with JavaCPP (which does not come with the kind of overhead JNA has, and works wherever JNI works):

// items.h
typedef struct
{
    int id;
    const char* name;
} Item;

typedef int (*Items_Callback)(const Item *item, void *context);

typedef struct
{
    int value;
    Items_Callback callback;
    void *context;
} Items_Call;

int Items_create(const Item *item, Items_Call *call) {
//  ...
    call->callback(item, call->context);
//  ...
    return 0;
}

And in Java:

import com.googlecode.javacpp.*;
import com.googlecode.javacpp.annotation.*;

@Platform(include="items.h")
public class Items {
    static { Loader.load(); }

    public static class Item extends Pointer {
        public Item() { allocate(); }
        private native void allocate();

        public native int id();           public native Item id(int id);
        @Cast("const char*")
        public native BytePointer name(); public native Item name(BytePointer name);
    }

    public static class Items_Callback extends FunctionPointer {
        protected Items_Callback() { allocate(); }
        private native void allocate();

        public native int call(@Const Item item, Pointer context);
    }

    public static class Items_Call extends Pointer {
        public Items_Call() { allocate(); }
        private native void allocate();

        public native int value();               public native Items_Call value(int value);
        public native Pointer context();         public native Items_Call context(Pointer context);
        public native Items_Callback callback(); public native Items_Call callback(Items_Callback value);

        public void setInterface(Items_Callback i) { callback(i); }
    }

    public static native void Items_create(Item item, Items_Call call);

    public static void main(String[] args) {
        BytePointer s = new BytePointer("Hello");
        Item i = new Item();
        i.id(42);
        i.name(s);

        Items_Callback cb = new Items_Callback() { 
            public int call(Item item, Pointer context) {
                System.out.println(item.id() + " " + item.name().getString());
                return 0;
            }
        };
        Items_Call ic = new Items_Call();
        ic.callback(cb);

        Items_create(i, ic);

        // if we remove these references, the GC may prematurely deallocate them
        s.deallocate();
        cb.deallocate();
    }
}

Which outputs the expected result:

42 Hello

Disclaimer: I'm the author of JavaCPP :)

Sign up to request clarification or add additional context in comments.

10 Comments

I have few question. (1) Is JavaCPP thread safe against callbacks coming in on multiple threads? (2) What about performance? Does framework cache method- and objectID to invoke call or retrieve they on echo call? Thanks.
@triclosan Yes, the method ID and object reference get cached. I guess it is thread safe, as long as JNI is, but I haven't done some stress testing to make sure...
Not exactly as I know about thread-safety. Please refer Attaching to the VM here docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/…
Or am I missing something?
@triclosan AttachCurrentThread() does get called as necessary. If there is a bug, I would be happy to fix it, obviously. Performance-wise, however, since we can't do anything to guarantee that the native side is going to call DetachCurrentThread(), it gets called defensively as necessary after the callback. We can work around that by making sure the callbacks come from threads initialized in Java...
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.