I\'ve been held up on this for about a week, now, and have searched forum after forum for a clear explanation of how to send a char* from C to FORTRAN. To make the matter m
This thread is a little old, but since I had a similar problem (and probably others will), I post a answer anyway.
The codes posted above will cause a segmentation fault if, for some reason, the C string is null. In addition, there is no need to return a 255-chars string (which will probably need to be trimmed before used), as Fortran 2003/2008 supports functions returning allocatable entities. Using all the information posted above, I ended up with the following function, which gets a C string (pointer), and returns the corresponding Fortran string; If the C string is null, it returns "NULL", similarly to C's "(null)" printed in similar cases:
function C_to_F_string(c_string_pointer) result(f_string)
use, intrinsic :: iso_c_binding, only: c_ptr,c_f_pointer,c_char,c_null_char
type(c_ptr), intent(in) :: c_string_pointer
character(len=:), allocatable :: f_string
character(kind=c_char), dimension(:), pointer :: char_array_pointer => null()
character(len=255) :: aux_string
integer :: i,length
call c_f_pointer(c_string_pointer,char_array_pointer,[255])
if (.not.associated(char_array_pointer)) then
allocate(character(len=4)::f_string); f_string="NULL"; return
end if
aux_string=" "
do i=1,255
if (char_array_pointer(i)==c_null_char) then
length=i-1; exit
end if
aux_string(i:i)=char_array_pointer(i)
end do
allocate(character(len=length)::f_string)
f_string=aux_string(1:length)
end function C_to_F_string
Strings of dynamic length are always a bit tricky with the C interaction. A possible solution is to use pointers.
First a simple case, where you have to hand over a null-character terminated string to a C-Function. If you really pass the string only in, you have to ensure to finalize it with the c_null_char, thus this direction is pretty straight forward. Here are examples from a LuaFortran Interface:
subroutine flu_getfield(L, index, k)
type(flu_State) :: L
integer :: index
character(len=*) :: k
integer(kind=c_int) :: c_index
character(len=len_trim(k)+1) :: c_k
c_k = trim(k) // c_null_char
c_index = index
call lua_getfield(L%state, c_index, c_k)
end subroutine flu_getfield
And the interface of lua_getfield looks like:
subroutine lua_getfield(L, index, k) bind(c, name="lua_getfield")
use, intrinsic :: iso_c_binding
type(c_ptr), value :: L
integer(kind=c_int), value :: index
character(kind=c_char), dimension(*) :: k
end subroutine lua_getfield
And the C-Code interface is:
void lua_getfield (lua_State *L, int idx, const char *k)
Now the little more complex case, where we have to deal with a returned string from C with a dynamic length. The most portable solution I found so far is using pointers. Here is an example with a pointer, where the string is given by the C-Routine (also from the Aotus library mentioned above):
function flu_tolstring(L, index, len) result(string)
type(flu_State) :: L
integer :: index
integer :: len
character,pointer,dimension(:) :: string
integer :: string_shape(1)
integer(kind=c_int) :: c_index
integer(kind=c_size_t) :: c_len
type(c_ptr) :: c_string
c_index = index
c_string = lua_tolstring(L%state, c_index, c_len)
len = int(c_len,kind=kind(len))
string_shape(1) = len
call c_f_pointer(c_string, string, string_shape)
end function flu_tolstring
where lua_tolstring has the following interface:
function lua_tolstring(L, index, len) bind(c, name="lua_tolstring")
use, intrinsic :: iso_c_binding
type(c_ptr), value :: L
integer(kind=c_int), value :: index
integer(kind=c_size_t) :: len
type(c_ptr) :: lua_tolstring
end function lua_tolstring
Finally, here is an attempt to clarify how a c_ptr can be interpreted as a Fortran character string: Assume you got a c_ptr pointing to the string:
type(c_ptr) :: a_c_string
And the length of it is given by a len variable with the following type:
integer(kind=c_size_t) :: stringlen
You want to get this string in a pointer to a character string in Fortran:
character,pointer,dimension(:) :: string
So you do the mapping:
call c_f_pointer(a_c_string, string, [ stringlen ])
In Fortran the item needs to be declared as "character (kind=c_char,len=1), dimension (255)" rather than len=255. This will create an array of characters of length one, which is what you need on the C-side. What can be confusing is that there is a exception that allows Fortran to match strings against one-dimensional arrays.
You mean that you want to call a Fortran procedure from C? See this example: Calling a FORTRAN subroutine from C.
EDIT: Both ifort and gfortran say that arrays are not allowed as function returns in this context. Which makes returning strings as function arguments from C to Fortran harder than using a string as an argument (example in link above) ... you have to use pointer and then the c_f_pointer Fortran intrinsic to convert from the C-string to a Fortran string, as explained by haraldkl. Here is another code example:
program test_c_func
use iso_c_binding
implicit none
type (C_PTR) :: C_String_ptr
character (len=1, kind=c_char), dimension (:), pointer :: ErrChars => null ()
character (len=255) :: ErrString
integer :: i
INTERFACE
FUNCTION GetLastErrorMessage () bind (C, name="GetLastErrorMessage" )
USE ISO_C_BINDING
type (C_PTR) :: GetLastErrorMessage
END FUNCTION GetLastErrorMessage
END INTERFACE
C_String_ptr = GetLastErrorMessage ()
call c_f_pointer ( C_String_ptr, ErrChars, [255] )
ErrString = " "
xfer_string: do i=1, 255
if ( ErrChars (i) == c_null_char) exit xfer_string
ErrString (i:i) = ErrChars (i)
end do xfer_string
write (*, '( "Fortran: <", A, ">" )' ) trim (ErrString)
end program test_c_func
If you know the length of the string, then Pap's answer above can be greatly simplified:
function stringc2f(n, cstr) result(fstr)
integer, intent(in) :: n
type(c_ptr), intent(in) :: cstr
character(:), allocatable :: fstr
character(n, kind=c_char), pointer :: fptr
call c_f_pointer(cstr, fptr)
fstr = fptr
end function
The above function accepts a C pointer with the string and the length of the string, and returns a copy as a Fortran string.
I always struggle with these interoperability features. I think that your interface should declare
CHARACTER(KIND=C_CHAR),DIMENSION(*) :: getlasterrormessage
and that, when you call the function, you pass a corresponding Fortran character variable with a length equal to or greater than the length of the array of C characters you expect to return.
Since you seem to have Intel Fortran, look through the code samples provided, they give a complete example for this.
I guess you know that what you have posted is not syntactically correct Fortran ?
I have also struggled with calling a C routine that returns a string and the answers above has been very useful but as I know almost nothing of C and the answers are slightly confusing I just wanted to contribute my solution which uses a C pointer, I did not manage to make use any of the other proposals above. The C program I call opens a separate window to browse for a file name.
program test
use iso_c_binding
implicit none
! A C function that returns a string need a pointer to the array of single char
type (c_ptr) :: C_String_ptr
! This is the Fortran equivalent to a string of single char
character (len=1, kind=c_char), dimension(:), pointer :: filchar=>null()
! Interface to a C routine which opens a window to browse for a file to open
interface
function tinyopen(typ) bind(c, name="tinyopen")
use iso_c_binding
implicit none
integer(c_int), value :: typ
type (C_Ptr) :: tinyopen
end function tinyopen
end interface
character (len=256) :: filename
integer typ,jj
typ=1
C_String_ptr = tinyopen(typ)
! convert C pointer to Fortran pointer
call c_f_pointer(C_String_ptr,filchar,[256])
filename=' '
if(.not.associated(filchar)) then
! if no characters give error message
write(*,*)'No file name'
else
! convert the array of single characters to a Fortran character
jj=1
do while(filchar(jj).ne.c_null_char)
filename(jj:jj)=filchar(jj)
jj=jj+1
enddo
endif
write(*,*)'Text is: ',trim(filename)
end program test
Hopefully this example will make it easier for the next one with the same problem.