Reputation: 689
This is a follow-up of the question How to use a ByteBuffer return from C++ to Java - I will acknowledge Flexo for his relentless support so far - I guess I just am not getting hang of typemaps yet... :-(
I have a C function with four arguments as follows, where the third and fourth arguments are passed by reference
extern "C" int foo(const char* passByValText, const int passbyValLen,
unsigned char* retTextByRef, int *retTextLen)
The Calling Java Wrapper function needs to look something like this
int foo(String passByValText, int passbyValLen, byte[] retBuf, SWIGTYPE_p_int retTextLen);
Alternatively it could be
int foo(String passByValText, int passbyValLen, ByteBuffer retBuf, SWIGTYPE_p_int retTextLen);
I can share an apology for a typemap that I wrote - but basically that might confuse matters even more ... so I am keeping this a clean slate ..
below is example code on c side
extern "C" int foo (const char* passByValText, const int passbyValLen, char *retTextByRef, int *retTextLen){
// binary value stuffed in the retTextByRef. In real program I compute this
unsigned char someText[10]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x00};
memcpy(cipherText,someText,9);
// I set the length of the value I stuffed into the buffer in c program
*cipherLen=9;
return 0;
}
And the Java program that calls this c function
class Caller{
public static void main(String[] args) {
//Define the parameter to pass by reference!!
SWIGTYPE_p_int retTextLen=MYMODULE.new_intp();
//Create a Java Wrapper Instance
JavaWrapClass myWrap=new JavaWrapClass();
StringBuffer sBuf = new StringBuffer(50);
int retTextLenVal=0;
myWrap.foo("PASSED-STRING-BY-VALUE", 24, sBuf, retTextLen);
retTextLenVal= MYMODULE.intp_value(retTextLen);
System.out.println("JAVA :: got length "+ retTextLenVal);
System.out.println("JAVA :: got BUFFER "+ sBuf);
}
}
Thanks a ton!!!
Upvotes: 1
Views: 1428
Reputation: 88711
I used the following header to test:
static int foo(const char *passByValText, const int passByValLen,
unsigned char *retTextByRef, int *retTextLen) {
*retTextLen = passByValLen;
if (retTextByRef) {
memcpy(retTextByRef, passByValText, passByValLen);
}
return NULL == retTextByRef;
}
I then put together the simplest (e.g. least custom typemaps) SWIG interface I could to wrap this function sensibly:
%module test
%{
#include "test.h"
%}
%apply (char *STRING, size_t LENGTH) { (const char *passByValText, const int passByValLen) };
// Use String instead of byte[] for input as requested in Q:
%typemap(jstype) (const char *passByValText, const int passByValLen) "String"
%typemap(javain) (const char *passByValText, const int passByValLen) "$javainput.getBytes()"
%include <arrays_java.i>
// Force unsigned char array to be byte[] in Java:
%apply signed char[] { unsigned char *retTextByRef };
%include <typemaps.i>
// Use the OUTPUT typemap - it passes an array with only one element
// could also use cpointers.i if you prefer
%apply int *OUTPUT { int *retTextLen };
%include "test.h"
Which ends up exposing the function foo
as a function that takes three inputs - a String
, a byte[]
for the output array and an int[]
for the output int
.
This allowed me to write:
public class run {
public static void main(String[] argv) {
System.loadLibrary("test");
byte arr[] = new byte[100];
int sz[] = {100};
test.foo("Hello world", arr, sz);
System.out.println(new String(arr, 0 , sz[0]));
}
}
Which worked as expected when tested. The typemaps in arrays_java.i reject null
arrays, so the if
in the sample I wrote can't be used to query the output size.
If you want to use a StringBuffer
instead of a byte[]
the simplest solution is to use %pragma
to generate an overload in Java:
%pragma(java) modulecode = %{
public static int foo(String in, StringBuffer out) {
int sz[] = {out.capacity()};
byte ret[] = new byte[out.capacity()];
final int v = test.foo(in, ret, sz);
// Or whatever your preferred semantics are:
out.replace(0, sz[0], new String(ret, 0, sz[0]));
return v;
}
%}
Upvotes: 1