MEX Fortran routines crashing

Dear all,
I wrote a gateway code to a fairly big FORTRAN77 subroutine quite a while back. I have updated it over the years to remain compatible with Matlab (64-bit operating systems, etc), using the "fintrf.h" header, ".F" suffixes and the mx/mex-specific variable and function types (mostly mwPointer).
But now my codes won't run and I suspect it is a compiler/linker problem because several different mex/mx function calls that previously worked now make my code crash, including:
  • Using mxCopyPtrToReal8() to copy an matlab array to a preallocated FORTRAN array (strangely, it works for 1x1 arrays)
  • Using mexErrMsgTxt() or mexErrIdAndMsg() in any way at all
I am using Matlab R2016A and have tried gfortran 5.1.0 and 5.3.0 (7.10 does not compile). Here are some examples of things that work and don't work:
WORKS (First right-side argument is a 1x1 array):
REAL*8 FAKEARR(1), RUF
CALL MXCOPYPTRTOREAL8(MXGETPR(PRHS(1)),FAKEARR, 1) *** OK
RUF = FAKEARR(1)
DOESN'T WORK (First right-side argument is a 1x2 array):
REAL*8 RUF(2),
CALL MXCOPYPTRTOREAL8(MXGETPR(PRHS(1)),RUF, 2) *** Crashes here
Why don't I just use mxGetScalar in the first example? Because it doesn't work.
And nothing works anymore when I try to take variables from the Matlab calling workspace:
NEITHER (ND is a 1x1 array):
mwPointer mxGetM, mxGetN, mexGetVariable,a
integer*4 ND
a = mexGetVariable('caller','ND')
call mxCopyPtrToReal8(mxGetPr(a), FAKEARR, 1) *** Crashes here
ND = FAKEARR(1)
NOR (D is an 1xN array where N<200):
mwPointer mxGetM, mxGetN, mexGetVariable, a
real*8 D(200)
mwSize mn
a = mexGetVariable('caller','D')
mn = mxGetM(a) * mxGetN(a)
call mxCopyPtrToReal8(mxGetPr(a), D, mn) *** Crashes here
Everything compiles fine, the code just crashes where I marked it.
I have attached the code if anyone wants to try it. I compile using compile.m, then run the code using compinv_example.m. I know, it's a big messy thing, but that's why I've hesitated converting it all to Python for now! It generally crashes after printing out the following:
>> compinv_example
No compliance MEX-file, running slow Matlab code...
ans =
1.0e-20 *
0.6893 0.4966 -0.0097 -0.0245 -0.0143
About to invert
_______________________________________________________________
It ruf tol ModelStp DataStp Status
ENTERING occam1 gateway
First mxGetM
First mxCopyPtrToReal8
loading common data
Entering initcommons()
First mexGetVariable
First mxCopyPtrToReal8...

Answers (1)

James Tursa
James Tursa on 22 Mar 2018
Edited: James Tursa on 22 Mar 2018
NEVER, EVER use literal integer constants as input arguments to the MATLAB API library functions in Fortran! You can never guarantee that they will match up with the correct integer that the function signature requires. You don't get automatic type promotion for input arguments in Fortran (which typically will be pass-by-reference) like you do for C/C++ (which is pass-by-value). So when I look at the first example that supposedly "works" above:
REAL*8 FAKEARR(1), RUF
CALL MXCOPYPTRTOREAL8(MXGETPR(PRHS(1)),FAKEARR, 1) *** OK
RUF = FAKEARR(1)
I immediately cringe when I see that literal 1 as the last input argument. That is a code bomb waiting to happen IMO. That literal 1 is probably by default a 32-bit integer, even with a 64-bit compiler. So it may work in one environment but then bomb in another environment that is expecting a 64-bit integer.
ALWAYS use correctly typed variables for ALL input argument in Fortran. NEVER use literal integer arguments. Also you should make sure that any API functions have return types that the compiler knows about. So that code above should be something like this:
mwPointer, external :: MXGETPR ! <-- The function return type info for the compiler
REAL*8 FAKEARR(1), RUF
mwSize :: N = 1
CALL MXCOPYPTRTOREAL8(MXGETPR(PRHS(1)),FAKEARR, N)
RUF = FAKEARR(1)
The types for the MXGETPR return value and the MXCOPYPTRTOREAL8 input arguments are obtained directly from the doc for those routines. Use them exactly as they appear in the doc.
None of your code that uses literal integer arguments or uses API functions that don't have return types defined for the compiler can be trusted, and you shouldn't even bother trying to debug anything else until you change that to use correctly typed variables for the input arguments and also give the compiler the return types for any API functions you are using. In all of your "crashes" examples above, I don't see anywhere where you told the compiler that MXGETPR returns an mwPointer type, so the compiler may just incorrectly assume it returns a 32-bit integer.
I would hazard a guess that the reason mxGetScalar doesn't work for you is because you either didn't tell MATLAB what the return type of the function was or you didn't pass in an mwPointer type that points to a double mxArray as an input argument.
Final Advice: Always use IMPLICIT NONE in every routine! Get rid of that IMPLICIT REAL*8 (A-H,O-Z) crap!

Categories

Tags

Asked:

on 22 Mar 2018

Edited:

on 22 Mar 2018

Community Treasure Hunt

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

Start Hunting!