Prev: calculation problem
Next: return to begin of function
From: James Tursa on 22 Apr 2010 16:34 "James Tursa" <aclassyguy_with_a_k_not_a_c(a)hotmail.com> wrote in message <hqq9ov$seu$1(a)fred.mathworks.com>... > > !------------------------------------------------------------------------- > mwPointer function myCreateString(str) result(mx) > implicit none > !-ARG > character(len=*) str > !-FUN > mwPointer, external :: mxGetData > mwPointer, external :: mxCreateNumericMatrix > integer*4, external :: mxClassIDFromClassName > !-LOC > mwSize m, n > integer*4 ClassID, ComplexFlag > mwPointer chars > !----- > m = 1 > n = len(str) > ClassID = mxClassIDFromClassName('char') > ComplexFlag = 0 > mx = mxCreateNumericMatrix(m, n, ClassID, ComplexFlag) > chars = mxGetData(mx) > call myCreateStringVal(n, %val(chars), str) > return > end function myCreateString Of course, right after posting this I realized there *is* a function in the API to create a char mxArray that does not have character arguments. So the above can be simplified as: !------------------------------------------------------------------------- mwPointer function myCreateString(str) result(mx) implicit none !-ARG character(len=*) str !-FUN mwPointer, external :: mxGetData mwPointer, external :: mxCreateCharArray !-LOC mwSize ndim, dims(2) mwPointer chars !----- ndim = 2 dims(1) = 1 dims(2) = len(str) mx = mxCreateCharArray(ndim, dims) chars = mxGetData(mx) call myCreateStringVal(n, %val(chars), str) return end function myCreateString James Tursa
From: James Tursa on 22 Apr 2010 17:54 "James Tursa" <aclassyguy_with_a_k_not_a_c(a)hotmail.com> wrote in message <hqqbsb$60v$1(a)fred.mathworks.com>... > > call myCreateStringVal(n, %val(chars), str) Typo. Should be: call myCreateStringVal(dims(2), %val(chars), str) James Tursa
From: James Tursa on 22 Apr 2010 15:58 Guillaume Jacquenot <guillaume.jacquenot(a)gmail.com> wrote in message <f7f38018-4ade-4fda-8379-c002420650c8(a)g30g2000yqc.googlegroups.com>... > > 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 Here is a list of the myGetString and myCreateString functions. The myGetString function is a slight variation of the one already posted except it buries the %val( ) stuff inside another routine so that the top level interface is the same as the MATLAB API interface for mxGetString. myCreateString uses some slight-of-hand and calls mxCreateNumericMatrix with a char classid to create a char array, then it fills in the data. (It just occurred to me that the isnumeric bit in the result might not be right ... I will have to check on that and get back to you). This technique is not documented in the MATLAB API doc but does seem to work OK. I would also like to comment on this statement from a previous poster: "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." On Windows, MATLAB used to use Compaq Fortran as their official supported Fortran compiler. That compiler used the stdcall convention and put the @<n> byte count stuff at the end of the exported names. So the Fortran libraries had those (e.g., their BLAS/LAPACK library). A few years ago MATLAB switched to the Intel Fortran compiler. ifort did not do this @<n> by default (but could be made to do this with a compiler switch). I believe MATLAB has now dropped all of their @<n> name mangled routines from their libraries since they don't need them to support Intel Fortran. But for several past releases the MATLAB BLAS/LAPACK library for Fortran, libdflapack.lib, would not link with Intel Fortran given the settings MATLAB had in their mexopts files ... you had to go in and manually add a compiler switch to get it to work. That has since been fixed. James Tursa caveat: Hand-typed from printout so beware of typos !------------------------------------------------------------------------- subroutine myGetString(pm, str, strlen) implicit none !-ARG mwPointer pm character(len=*) str mwSize strlen !-FUN mwPointer, external :: mxGetData mwSize, external :: mxGetNumberOfElements !-LOC mwPointer chars mwSize n !----- chars = mxGetData(pm) n = mxGetNumberOfElements(pm) call myGetStringVal(n, %val(chars), str, strlen) return end subroutine myGetString !------------------------------------------------------------------------- subroutine myGetStringVal(n, i2chars, str, strlen) implicit none !-ARG mwSize n integer(2) i2chars(n) character(len=*) str mwSize strlen !-LOC mwSize i, m !----- m = min(n, strlen, len(str)) do i=1,m str(i:i) = char(i2chars(i)) enddo if( m < len(str) ) str(m+1:) = ' ' return end subroutine myGetStringVal !------------------------------------------------------------------------- mwPointer function myCreateString(str) result(mx) implicit none !-ARG character(len=*) str !-FUN mwPointer, external :: mxGetData mwPointer, external :: mxCreateNumericMatrix integer*4, external :: mxClassIDFromClassName !-LOC mwSize m, n integer*4 ClassID, ComplexFlag mwPointer chars !----- m = 1 n = len(str) ClassID = mxClassIDFromClassName('char') ComplexFlag = 0 mx = mxCreateNumericMatrix(m, n, ClassID, ComplexFlag) chars = mxGetData(mx) call myCreateStringVal(n, %val(chars), str) return end function myCreateString !------------------------------------------------------------------------- subroutine myCreateStringVal(n, i2chars, str) implicit none !-ARG mwSize n integer(2) i2chars(n) character(len=*) str !-LOC mwSize i !----- do i=1,n i2chars(i) = ichar(str(i:i)) enddo return end subroutine myCreateStringVal
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. |