Consider MyClass.java:
public class MyClass {
public void firstfunction(double fwd[]) {
fwd[0] = 42;
}
public void secondfunction(Double fwd[]) {
fwd[0] = new Double(42);
}
}
Both functions return the value 42 in fwd
, right?
From within MATLAB, I want to access this value 42:
myobj=MyClass;
var1=0.0;
myobj.firstfunction(var1);
fprintf('%1.1f',var1); %// ... var1 is still 0.0 ... :-(
var2 = javaArray ('java.lang.Double',1);
var2(1)=java.lang.Double(0.0);
myobj.secondfunction(var2); %// var2 now contains the value 42 :-)
While both calls "work" (as is: no error message), only var2 contains the return value 42; var1 still has the value 0.0.
Is there any way to use MATLAB to call the function firstfunction
and retrieve the return value?
Some background: MATLAB can pass Java objects when calling a Java function, and modifications to these objects are afterwards available in MATLAB - except when the Java object is an array of a primitive data type. In this case automatic conversion between MATLAB and Java kicks in, making a Java array-of-primitive-double correspond directly to a double matrix in MATLAB - which is by MATLAB conventions a thing "passed as value" so no return values are possible. So my question can be rephrased as is there any way around this?
(you can stop reading here.)
For reference, my special case was this:
I have a Java class MyClass.java wrapping a DLL, which I want to use in MATLAB. However, the return value of one of the functions is a double[] passed as a parameter, the content of which doesn't make it back to MATLAB due to how interaction with Java is implemented.
Is there any way around this problem, without modifying the way the DLL returns the data?
Here are the ugly details:
public class MyClass
{
static
{
System.load("C:\\fullpath\\mydll.dll");
}
public static native long SetFWD(double fwd);
public static native long GetFWD(double fwd[]);
}
This is visible from within MATLAB once I set the javapath correctly:
>> methods MyClass -full
Methods for class MyClass:
static long GetFWD(double[])
MyClass()
static long SetFWD(double)
[and stuff inherited from java.lang.Object]
I can call the function SetFWD from within MATLAB, but I can't get GetFWD to return anything:
myobj=MyClass;
fwd=3.0;
myobj.SetFWD(fwdval); % this works fine
fwd=0.0;
myobj.GetFWD(fwd); % this does not give an error, but fwd stays unmodified - as one would expect in MATLAB
fwd = javaArray ('java.lang.Double',1);
fwd(1) = java.lang.Double(0.0);
myobj.GetFWD(fwd) % this gives the error "??? No method 'GetFWD' with matching signature found for class 'MyClass'."
From reading MATLAB Documentation Passing Data to a Java Method and Working with Java Arrays as well as SO posts Moving from Java types back to MATLAB types and Strange classes passed from matlab to java, I understand that Matlab automagically converts any double array that I pass to the function into a Java array, and then ignores whatever modifications does in these arrays. It seems that if my function definition in MyClass contained Double objects instead of double primitives, my second attempt could work.
Is there any way to get MATLAB to return the value I'm after, without modifying the original .DLL (mydll.dll)?
Update
I understand that MATLAB usually passes everything "by value". But in Passing Data to a Java Method Mathworks say that
If you need to access changes that a Java method makes to an array, then, rather than passing a MATLAB array, you should create and pass a Java array, which is a reference.
They explain in Working with Java Arrays how to do that using the javaArray function, but I couldn't get this to work for creating an array double[] (i.e. an array of primitive doubles), only for Double[] (i.e. an array of Double objects) which is not what I need here, since my function GetFWD() doesn't eat the latter :-(.
>> A=javaArray ('java.lang.double',1); % works fine, but cannot be used as parameter for my function GetFWD (see "No Method ... with matching signature..." error above)
>> A=javaArray ('double',1);
??? Error using ==> javaArray
No class double can be located on the MATLAB Java classpath
No, it can not.
I have found this workaround, that I can live with: while I can't (myself) modify the DLL, I can add a wrapper function in the java class MyClass, like so:
public static long GetFWDwrp(Double fwd[]) {
double tmp[]=new double[1];
long retval = MyClass.GetFWD(tmp);
fwd[0] = tmp[0];
return retval;
}
This wrapper function has an array of Double objects as a parameter, which I can access from MATLAB like this:
oldFW = javaArray ('java.lang.Double',1);
oldFW(1)=java.lang.Double(0.0);
myobj.GetFWDwrp(oldFW);
oldFW % this now contains the return value
So this answers my question for a workaround to the problem, since it doesn't involve changing the interface of the DLL (only the interface of the Java Class).
However, the more fundamental question in the title is still unanswered: Is it really impossible in MATLAB to pass a reference to an array-of-primitive-double to a Java function, circumventing the automatic conversion of Matlab-double-array to Java-primitive-double-array and back which seems to make it impossible to access any changes the Java code made to such an array.
This is not a bug, it is expected behaviour
firstfunction
accepts a copy of a primitive double array, modifies that copy and returns without altering anything in the MyClass
instance.
secondfunction
gets a reference to a java.lang.Double
array as input - so MATLAB sees any changes made to that array.
Within Java code, you can pass references to double arrays but they will always be passed as copies when provided as output or input from MATLAB.
You might find a wrapper like the Apache Math ArrayRealVector
helps but even there, accessing the underlying array from MATLAB will create a copy - even when calling getDataRef
, as Apache Math will return a reference but MATLAB will then copy it. You can though call the ArrayRealVector setEntry
and getEntry
methods to modify the underlying double array from MATLAB.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With