Fortran MEX using Cell arrays - crashing Matlab..

조회 수: 1(최근 30일)
Arwel
Arwel 2016년 7월 20일
편집: James Tursa 2016년 7월 21일
Hi, I am trying to learn how to use cell arrays in Mex functions. I've written a simple test function below, which should take a cell array of doubles, then simply square each element. So from Matlab if...
A = {[1 2 3] ; [1 2 3]}
.. then B = mexGateway(A)
should give B = {[1 4 9];[1 4 9]}.
But, it just crashes Matlab. From the debugger, the crash happens when it tries to call compute. What have I missed here, and how should this be changed to make it work? Cheers, Arwel
#include "fintrf.h"
C-------------------------------------------------------------------------------
C Test program that inputs cell array of double arrays, squares each element
C then outputs the results in a new cell array
C--------------------------------------------------------------------------------
subroutine mexFunction(nlhs, plhs, nrhs, prhs)
C Declarations
implicit none
C Mex function arguments
mwPointer plhs(*), prhs(*), dat
integer nlhs, nrhs
C Function Declarations
mwPointer mxCreateCellMatrix
mwPointer mxGetCell
mwPointer mxSetCell
mwPointer mxGetData
mwPointer mxGetPr
mwPointer mxGetNumberOfElements
mwSize i, m
C Pointers Arrays and vars
mwPointer numberOfCells
mwPointer outputArray
mwPointer inputArray
mwPointer thisCell
mwPointer calcOutpArray
mwPointer size
C input checking to be added..
C Get the size of the input array..
inputArray = prhs(1)
numberOfCells = mxGetNumberOfElements(inputArray)
C Output cell array will have the same dimensions
outputArray = mxCreateCellMatrix(numberOfCells,1)
c Loop over all the elements in the input array and call comp...
do 10 i=1,numberOfCells
thisCell = mxGetCell(inputArray,i)
size = mxGetNumberOfElements(thisCell)
call compute(%VAL(calcOutpArray),%VAL(thisCell),%VAL(size))
call mxSetCell(outputArray,i,calcOutpArray)
10 continue
plhs(1) = outputArray
return
end
C -----------------------------------------------------------
subroutine compute(outArray,inArray,l)
real*8 outArray(l,1), inArray(l,1)
real*8 n
do 20 n=1,l
outArray(n,1) = inArray(n,1)*inArray(n,1)
20 continue
return
end

답변(4개)

dpb
dpb 2016년 7월 20일
Subroutine compute is Fortran, don't need (and can't use) %VAL on the arguments; they'll be handled just like any other Fortran argument.
call compute(calcOutpArray,thisCell,size)

Arwel
Arwel 2016년 7월 20일
Actually, I'm not so sure. This seems to work, in that when watching in the debugger, the actual 'real valued' array does turn up in 'compute' as the inputted numbers, and these then get squared as expected (i.e. if you input {[1 2 3]}, then while still inside comp, outArray is [1 4 9]..
thisCell = mxGetCell(inputArray,i)
size = mxGetNumberOfElements(thisCell)
thisArray = mxGetData(thisCell)
call compute(calcOutpArray,%VAL(thisArray),size)
But, the problem then is that 'calcOutpArray' looks like a very strange value once back in the gateway function, and any attempt to do anything with it then seems to crash Matlab..

James Tursa
James Tursa 2016년 7월 20일
편집: James Tursa 2016년 7월 21일
You are not creating any output cells, so you are writing to garbage address locations which will eventually result in a MATLAB crash. I.e., in this line
call compute(%VAL(calcOutpArray),%VAL(thisCell),%VAL(size))
calcOutpArray hasn't been set to anything prior to this call. Plus you aren't even passing the correct thing anyway as dpb has pointed out. This section of code should look something like this:
mwSize m, n
integer*4 :: ComplexFlag = 0
mwPointer pr_out, pr_in
:
m = numberOfCells
n = 1
outputArray = mxCreateCellMatrix(m,n) ! <-- Always pass variables for sizes, not literal constants
:
do i=1,numberOfCells
thisCell = mxGetCell(inputArray,i) ! <-- This is an (mxArray *)
m = mxGetNumberOfElements(thisCell)
n = 1
calcOutpArray = mxCreateDoubleMatrix(m,n,ComplexFlag) ! <-- This is an (mxArray *)
pr_in = mxGetPr(thisCell) ! <-- This is a (double *)
pr_out = mxGetPr(calcOutpArray) ! <-- This is a (double *)
call compute(%VAL(pr_out),%VAL(pr_in),m) ! <-- Pass value of (double *) to compute
call mxSetCell(outputArray,i,calcOutpArray)
enddo
:
:
subroutine compute(outArray,inArray,l)
mwSize l
real*8 outArray(l,1), inArray(l,1)
mwSize n
The %VAL( ) constructs work in the above code because the subroutine compute has an implicit interface. If it had an explicit interface you would need to do something else.
And, Fortran is an array based language, like MATLAB. So these lines of code:
do 20 n=1,l
outArray(n,1) = inArray(n,1)*inArray(n,1)
20 continue
Can be reduced to this one statement, since * is element-wise multiply in Fortran:
outArray = inArray*inArray
CAUTION: This all assumes that the input argument is EXACTLY as expected. If not, you will probably get a crash. E.g., if one of the cell elements of the input variable is not set, it will physically contain a NULL address (0 in Fortran). So for that iteration you would have thisCell = 0, and subsequent lines that use thisCell will probably crash MATLAB. To make your code robust against this, you need to check that thisCell is not 0, and if you pass that then check to see that it is double class and not sparse etc etc. So you need to put in lots of checks like this to make sure your code doesn't crash MATLAB for unexpected inputs.

Arwel
Arwel 2016년 7월 20일
편집: Arwel 2016년 7월 20일
Ah now I see.... that's awesome James, thank you so much. I will give it a go. What is then the correct way of outputting the final cell array back to Matlab after the loop?
  댓글 수: 1
James Tursa
James Tursa 2016년 7월 20일
You already have that correct:
plhs(1) = outputArray

댓글을 달려면 로그인하십시오.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by