From: Guillaume Jacquenot on
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
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
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
> 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
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