Prev: source check
Next: allocate() initialization
From: Guillaume Jacquenot on 19 Apr 2010 05:49 Dear all, I have to create a mex interface for MatLab with a fortran 90 code. The compiler that I have to used is gfortran 4.5 for win xp 32 bits. My interface needs to read a MatLab string to pass its value to the fortran code. This is where problems start: I have problems with function using string, specially 'mxGetString' To have a simple example, I want to create a mex fortran program, that reads a string in MatLab workspace. (The code is provided below) I use GNUmex to generate the script compilation process and also to generate libraries needed to perform the linkage. Since I am using Gfortran, I have to take special care about strings and MatLab mex and mx functions. I use bindings to call the correct library functions with ISO_C_BINDING. To get the value of my string, I want to use the two following commands ptr=mexGetVariablePtr('base', 'input_fast') mxGetString(ptr) where 'input_fast' is the name of the variable I want to read. However, the value of the pointer provided as a result of mexGetVariablePtr does not seem to provide a correct value. Most of the time, its value is zero meaning that an error occurs. I suspect a problem with the strings as mentioned in the file readme- fortran.txt provided with gnumex: " However, gfortran seems to have a different mechanism for passing character strings, and the Fortran versions of these functions do not work. Thus mexinterface_c.f90 binds them to the corresponding C-functions (with mixed case names). This meens that strings passed to them from gfortran must have an ascii zero appended, viz: call mexprintf('A message'//char(0)) call mexerrmsgtxt('An error occurred'//char(10)//char(0)) " My problem is, I think, the same as described on this webpage http://www.rhinocerus.net/forum/lang-fortran/572363-passing-string-argument-matlab-c-function.html Btw, I tried to contact the author of this post, or leave a message on the website, without success. I have tried to compile the code with the -mrtd without success. Does anyone has an idea, or can anyone can explain why the problem with my program. Below is the code of the simple example I want to compile. Best regards, Guillaume Jacquenot My configuration is windows xp 32 bits MATLAB Version 7.5.0.342 (R2007b) GNU Fortran (GCC) 4.5.0 20090421 (experimental) [trunk revision 146519] Commands used to generated the dll/mexw32 file named Test_GETSTRING.dll/mexw32 These commands are generated by gnumex and have been modifed add the preprocessor and other options, such as -mrtd !gfortran -cpp -mrtd -c -g -DMATLAB_MEX_FILE -fno-underscoring -Wall - fmessage-length=0 -ffree-line-length-none -oSfun_all.obj -O0 - DMX_COMPAT_32 Sfun_all.f90 !gfortran -shared d:\GFORTR~1\sfun2\GNUMEX~1\gfortmex.def -g -Wl,-- image-base,0x28000000\n -o Test_GETSTRING.mexw32 -Ld: \GFORTR~1\sfun2\GNUMEX~1 -mrtd -enable-stdcall-fixup -s Sfun_all.obj - lflibmx -lflibmex -lflibmat Once the library has been generated, one needs to create the variable the library will be looking for. > input_fast = 'File000.txt' And then run the library > Test_GETSTRING The library should display the value of the string 'input_fast' Code: #ifndef mwPointer #define mwPointer integer(4) #endif #ifndef mwSize #define mwSize integer(4) #endif module mexinterface use, intrinsic :: ISO_C_BINDING interface function mxgetpr(pm) bind(c, name = 'MXGETPR') import c_int, c_ptr mwPointer :: pm mwPointer :: mxgetpr end function mxgetpr ! function mxgetm(pm) bind(c, name = 'MXGETM') import c_int integer(c_int) :: pm, mxgetm end function mxgetm ! function mxgetn(pm) bind(c, name = 'MXGETN') import c_int integer(c_int) pm, mxgetn end function mxgetn ! function mexprintf(s) bind(C, name = 'mexPrintf') import c_char character(c_char) s(*) integer(4) :: mexprintf end function mexprintf ! function mexgetvariableptr(workspace,varname) bind(C, name = 'MEXGETVARIABLEPTR') import c_char character(kind=c_char), dimension(*) :: workspace character(kind=c_char), dimension(*) :: varname mwPointer :: mexgetvariableptr end function mexgetvariableptr ! GJ subroutine mexerrmsgtxt(s) bind(C, name = 'mexErrMsgTxt') import c_char character(c_char) s(*) end subroutine mexerrmsgtxt function mxgetstring(pm, str, strlen) bind(c, name = 'MXGETSTRING') import c_char, c_int, c_ptr, c_double mwPointer, intent(in) :: pm character(c_char), intent(in) :: str mwSize, intent(in) :: strlen integer(4) :: mxgetstring end function mxgetstring subroutine mexEvalString(command) bind(c, name = 'MEXEVALSTRING') import c_char character(c_char) :: command end subroutine mexEvalString end interface end module mexinterface subroutine mexfunction(nlhs, plhs, nrhs, prhs) use mexinterface implicit none integer :: nlhs, nrhs, plhs(nlhs), prhs(nrhs) integer :: res, M,N mwPointer :: GStr mwPointer :: ptr_retrn CHARACTER*1024 InpFile CHARACTER*100 msg res = mexPrintf('Hello world'//char(10)//char(0)) ! Problem ! ptr_retrn = mexGetVariablePtr(c_char_'base', & ! c_char_'input_fast') !ptr_retrn = mexGetVariablePtr(c_char_'base'// char(0),c_char_'input_fast'//char(0)) ptr_retrn = mexGetVariablePtr('base','input_fast'//char(0)) ! ptr_retrn = mexGetVariablePtr('base'//char(0), 'input_fast') ! Problem write(msg,'(A7 I15)') 'ptr = ',ptr_retrn res = mexPrintf(msg) IF ( ptr_retrn .eq. 0 ) THEN CALL mexErrMsgTxt(char(10)//'ERROR: variable "input_fast" does not exist in the MATLAB workspace.'//char(10)//char(0)) ELSE M = mxGetM(ptr_retrn) N = mxGetN(ptr_retrn) write(msg,'(A7 I5)'//char(10)) 'M= ',M res = mexPrintf(msg) res = mexPrintf('') write(msg,'(A7 I5)'//char(10)) 'N= ',N res = mexPrintf(msg) GStr = mxGetString(ptr_retrn, InpFile, M*N) write(msg,'(A7 I15)'//char(10)) 'GStr = ',GStr res = mexPrintf(msg) IF (GStr /= 0) THEN CALL mexErrMsgTxt('Error getting FAST file name from the MATLAB workspace.'//char(10)//char(0)) ELSE write(msg,'(A20)'//char(10)) InpFile res = mexPrintf(msg) ENDIF ENDIF end subroutine mexfunction
From: Tobias Burnus on 19 Apr 2010 06:29 On 04/19/2010 11:49 AM, Guillaume Jacquenot wrote: > I have to create a mex interface for MatLab with a fortran 90 code. > The compiler that I have to used is gfortran 4.5 for win xp 32 bits. > My interface needs to read a MatLab string to pass its value to the > fortran code. [...] > To get the value of my string, I want to use the two following > commands > ptr=mexGetVariablePtr('base', 'input_fast') > My problem is, I think, the same as described on this webpage > http://www.rhinocerus.net/forum/lang-fortran/572363-passing-string-argument-matlab-c-function.html It would help tremendously if you could also post the function prototype of the relevant Matlab functions. [...] > These commands are generated by gnumex and have been modifed add the > preprocessor and other options, such as -mrtd I strongly suggest to avoid -mrtd; as the manual states: "Warning: this calling convention is incompatible with the one normally used [...]". As the run-time library of gfortran is compiled with CDECL (i.e. without -mrtd/STDCALL), you might run into problems such as memory leaks (very probably) but also all kind of weird behaviour and crashes. If you need to use STDCALL for a function, simply use directives add the attribute to the function interfaces which need it. Cf. http://gcc.gnu.org/onlinedocs/gfortran/GNU-Fortran-Compiler-Directives.html However, as STDCALL routines are dressed under Windows with the suffix @<n> (n = bytesize of the arguments), it might well be that Matlab does not use STDCALL but the default, C calling convention CDECL. In that case, using -mrtd does not make sense at all. > function mexprintf(s) bind(C, name = 'mexPrintf') > import c_char > character(c_char) s(*) > integer(4) :: mexprintf > end function mexprintf How does the C interface look like? If it uses "int mexPrintf (char *, ...)" it won't work: C's "..." is not supported by Fortran's C interoperability. It might work - when you are lucky - if you do not have any %<...> printf formats in the string. Otherwise, all character-taking functions without trailing "..." should work, however. In any case, one needs to make sure that the strings are nul ('\0') terminated by appending a //c_null_char to the string arguments. See also: http://gcc.gnu.org/onlinedocs/gfortran/Interoperability-with-C.html (Using ISO_C_Binding, one should also be able to call the Fortran functions of MatLab - if one knows how the other compiler treats strings, and if one knows whether it uses CDECL or STDCALL calling conventions.) Tobias
From: Guillaume Jacquenot on 19 Apr 2010 08:34 On 19 avr, 12:29, Tobias Burnus <bur...(a)net-b.de> wrote: > On 04/19/2010 11:49 AM, Guillaume Jacquenot wrote:> I have to create a mex interface for MatLab with a fortran 90 code. > It would help tremendously if you could also post the function prototype > of the relevant Matlab functions. > The Fortran and the C equivalent prototypes are provided at the end of this mail. > > I strongly suggest to avoid -mrtd; as the manual states: > "Warning: this calling convention is incompatible with the one normally > used [...]". As the run-time library of gfortran is compiled with CDECL > (i.e. without -mrtd/STDCALL), you might run into problems such as memory > leaks (very probably) but also all kind of weird behaviour and crashes. > The options -mrtd was the trick used by Erik Toussaint author of the post http://www.rhinocerus.net/forum/lang-fortran/572363-passing-string-argument-matlab-c-function.html With this option, he managed to solve his problem. > If you need to use STDCALL for a function, simply use directives add the > attribute to the function interfaces which need it. Cf.http://gcc.gnu.org/onlinedocs/gfortran/GNU-Fortran-Compiler-Directive... > > However, as STDCALL routines are dressed under Windows with the suffix > @<n> (n = bytesize of the arguments), it might well be that Matlab does > not use STDCALL but the default, C calling convention CDECL. In that > case, using -mrtd does not make sense at all. > > > function mexprintf(s) bind(C, name = 'mexPrintf') > > import c_char > > character(c_char) s(*) > > integer(4) :: mexprintf > > end function mexprintf > > How does the C interface look like? If it uses > "int mexPrintf (char *, ...)" > it won't work: C's "..." is not supported by Fortran's C interoperability.. > > It might work - when you are lucky - if you do not have any %<...> > printf formats in the string. The function mexPrintf works fine with the little program I provided. I have not tried with any %<...> printf format > > Otherwise, all character-taking functions without trailing "..." should > work, however. In any case, one needs to make sure that the strings are > nul ('\0') terminated by appending a //c_null_char to the string arguments. > > See also:http://gcc.gnu.org/onlinedocs/gfortran/Interoperability-with-C.html > > (Using ISO_C_Binding, one should also be able to call the Fortran > functions of MatLab - if one knows how the other compiler treats > strings, and if one knows whether it uses CDECL or STDCALL calling > conventions.) I am not familiar with the CDECL or STDCALL convention. I will investigate what convention is used with g95 and ivf when interfacing with Matlab > > Tobias Guillaume Here are the prototypes of the different interface function. Below, mwPointer is defined as integer(4) mwSize is defined as integer(4) mxGetPr: Get real data elements in mxArray Fortran prototype mwPointer mxGetPr(pm) mwPointer pm C prototype double *mxGetPr(const mxArray *pm); mexPrintf: ANSI C printf-style output routine Fortran prototype integer*4 mexPrintf(message) character*(*) message C prototype int mexPrintf(const char *message, ...); mexGetVariablePtr: Get read-only pointer to variable from another workspace C Fortran prototype mwPointer mexGetVariablePtr(workspace, varname) character*(*) workspace, varname C prototype const mxArray *mexGetVariablePtr(const char *workspace, const char *varname); mexErrMsgTxt: Issue error message and return to MATLAB prompt Fortran prototype mexErrMsgTxt(errormsg) character*(*) errormsg C prototype void mexErrMsgTxt(const char *errormsg); mxGetString: Copy string mxArray to C-style string Fortran prototype integer*4 mxGetString(pm, str, strlen) mwPointer pm character*(*) str mwSize strlen C prototype int mxGetString(const mxArray *pm, char *str, mwSize strlen); mexEvalString: Execute MATLAB command in caller's workspace Fortran prototype integer*4 mexEvalString(command) character*(*) command C prototype int mexEvalString(const char *command);
From: Guillaume Jacquenot on 21 Apr 2010 13:58 > Rather than trying to link with and use C routines in your Fortran program, you could opt to write your own version of mxGetString to convert the MATLAB string to a regular Fortran string. e.g., an outline of the code: > > > This assumes your compiler supports %val( ) and that the characters in the string are not multi-byte characters. > > James Tursa I have opted for your option James. However, it is quite a pain to develop all interface functions. I have tested it on the function 'revord.f'. This MatLab function example reverses the order of a string. Below is the code. However, I could not convert all interface function: I did not manage to re-write the function 'mxCreateString' that creates a crash dump with gfortran. I think I will have several other problems with the other interface functions. Do you have some advice before I look deeper? Below is the code of example revord.f adapted to gfortran The compilation commands are the following for the code presented below are gfortran -c -DMATLAB_MEX_FILE -fno-underscoring -cpp -mrtd -Wall - fmessage-length=0 -ffree-line-length-none -fno-automatic - orevord_gfortran.obj -g -DMX_COMPAT_32 revord_gfortran.f90 gfortran -shared D:\GFORTR~1\SFUN_M~2\GNUMEX~1\gfortmex.def -o revord_gfortran.mexw32 -g -Wl,--image-base,0x28000000\n -LD: \GFORTR~1\SFUN_M~2\GNUMEX~1 revord_gfortran.obj -lflibmx -lflibmex - lflibmat File: revord_gfortran.f90 #ifndef mwPointer #define mwPointer integer(4) #endif #ifndef mwSize #define mwSize integer(4) #endif module mexinterface use, intrinsic :: ISO_C_BINDING interface function mxgetnumberofelements(pm) & bind(c, name = 'MXGETNUMBEROFELEMENTS') mwPointer :: pm mwPointer :: mxgetnumberofelements end function mxgetnumberofelements function mxgetdata(pm) & bind(c, name = 'MXGETDATA') mwPointer :: pm mwPointer :: mxgetdata end function mxgetdata function mxgetpr(pm) bind(c, name = 'MXGETPR') import c_int, c_ptr mwPointer :: pm mwPointer :: mxgetpr end function mxgetpr ! function mxgetm(pm) bind(c, name = 'MXGETM') import c_int integer(c_int) :: pm, mxgetm end function mxgetm ! function mxgetn(pm) bind(c, name = 'MXGETN') import c_int integer(c_int) pm, mxgetn end function mxgetn ! function mexprintf(s) bind(C, name = 'mexPrintf') import c_char character(c_char) s(*) integer(4) :: mexprintf end function mexprintf function mexgetvariableptr(workspace,varname) & bind(C, name = 'MEXGETVARIABLEPTR') import c_char character(kind=c_char), dimension(*) :: workspace character(kind=c_char), dimension(*) :: varname mwPointer :: mexgetvariableptr end function mexgetvariableptr mwPointer function mxCreateString(str) & bind(C, name = 'MXCREATESTRING') import c_char character(c_char), intent(in) :: str(*) end function mxCreateString integer(4) function mxIsChar(pm) & bind(C, name = 'MXISCHAR') mwPointer, intent(in) :: pm end function mxIsChar ! GJ subroutine mexerrmsgtxt(s) bind(C, name = 'mexErrMsgTxt') import c_char character(c_char) s(*) end subroutine mexerrmsgtxt function mxgetstring(pm, str, strlen) & bind(c, name = 'MXGETSTRING') !g95 ! bind(c, name = 'mxGetString') !g95 ! bind(c, name = 'mxGetString_700') !g95 import c_char, c_int, c_ptr, c_double mwPointer, intent(in) :: pm character(c_char), intent(in) :: str mwSize, intent(in) :: strlen integer(4) :: mxgetstring end function mxgetstring subroutine mexEvalString(command) & bind(c, name = 'MEXEVALSTRING') import c_char character(c_char) :: command end subroutine mexEvalString end interface end module mexinterface subroutine revord(input_buf, strlen, output_buf) character input_buf(*), output_buf(*) mwSize i, strlen do 10 i=1,strlen output_buf(i) = input_buf(strlen-i+1) 10 continue return end subroutine myGetString(n, retrn, line) implicit none mwSize n integer(2) retrn(n) character(len=*) line mwSize i, m m = len(line) do i=1,min(m,n) line(i:i) = char(retrn(i)) enddo if( n < m ) line(n+1:) = ' ' return end subroutine myGetString ! The gateway routine. subroutine mexFunction(nlhs, plhs, nrhs, prhs) use mexinterface integer nlhs, nrhs CHARACTER*200 msg !----------------------------------------------------------------------- ! mwPointer plhs(*), prhs(*) ! mwPointer mxCreateString, mxGetString !----------------------------------------------------------------------- ! ! mwSize mxGetM, mxGetN ! integer mxIsChar mwSize strlen integer statusGJ character*100 input_buf, output_buf mwPointer chars mwSize n ! Check for proper number of arguments. if (nrhs .ne. 1) then call mexErrMsgTxt('One input required.'//char(10)//char(0)) elseif (nlhs .gt. 1) then call mexErrMsgTxt('Too many output arguments.'//char(10)// char(0)) ! The input must be a string. elseif(mxIsChar(prhs(1)) .ne. 1) then call mexErrMsgTxt('Input must be a string.'//char(10)// char(0)) ! The input must be a row vector. elseif (mxGetM(prhs(1)) .ne. 1) then call mexErrMsgTxt('Input must be a row vector.'//char(10)// char(0)) endif ! Get the length of the input string. strlen = mxGetM(prhs(1))*mxGetN(prhs(1)) write(msg,'(A9 I4)') 'strlen = ',strlen res = mexPrintf(msg//char(10)//char(0)) res = mexprintf('hello'//char(10)//char(0)) n = mxGetNumberOfElements(prhs(1)) chars = mxGetData(prhs(1)) call myGetString(n, %val(chars), input_buf) ! Get the string contents (dereference the input pointer). ! statusGJ = mxgetstring(prhs(1), input_buf, strlen+1) ! Check if mxGetString is successful. !if (statusGJ .ne. 0) then ! call mexErrMsgTxt('String length must be less than 100.'// char(10)//char(0)) !endif res = mexPrintf(input_buf//char(10)//char(0)) ! Initialize outbuf_buf to blanks. This is necessary on some compilers. output_buf = ' ' ! Call the computational subroutine. call revord(input_buf, strlen, output_buf) res = mexPrintf(output_buf//char(10)//char(0)) ! set output_buf to MATLAB mexFunction output ! plhs(1) = mxCreateString(output_buf) return end
From: Erik Toussaint on 29 Apr 2010 07:39 On 19-4-2010 14:34, Guillaume Jacquenot wrote: > On 19 avr, 12:29, Tobias Burnus<bur...(a)net-b.de> wrote: >> I strongly suggest to avoid -mrtd; as the manual states: >> "Warning: this calling convention is incompatible with the one normally >> used [...]". As the run-time library of gfortran is compiled with CDECL >> (i.e. without -mrtd/STDCALL), you might run into problems such as memory >> leaks (very probably) but also all kind of weird behaviour and crashes. >> > > The options -mrtd was the trick used by Erik Toussaint author of the > post > http://www.rhinocerus.net/forum/lang-fortran/572363-passing-string-argument-matlab-c-function.html > With this option, he managed to solve his problem. Ah, yes, I remember working on this. Although perhaps I shouldn't use the word 'working'. It was just a little hobby project. If I remember correctly, using the -mrtd option did indeed make my simple test program run. However, after that, I did some more reading on the specifics of that option and realized that it would not be the right way to proceed. As Tobias states, it would likely be a source of problems in anything more complicated than my simple test program. Instead, I came to the conclusion that it would be better to use compiler directives, exactly as Tobias advised. Unfortunately, at that moment I lost interest in trying to make fortran and matlab work together, so I didn't actually try this for myself. Erik.
|
Pages: 1 Prev: source check Next: allocate() initialization |