From: James Tursa on
"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
"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
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
> 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.
First  |  Prev  | 
Pages: 1 2
Prev: calculation problem
Next: return to begin of function