Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Callback as parameter of C structure - Java wrapper generation

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.

like image 382
triclosan Avatar asked Dec 19 '13 21:12

triclosan


1 Answers

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 :)

like image 139
Samuel Audet Avatar answered Sep 29 '22 07:09

Samuel Audet