Creating a FORTRAN interface to a C function that returns a char*
Solution 1:
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 ])
Solution 2:
My thanks to heraldkl for giving me the solution to this very frustrating problem. I'm posting what I've eventually implemented, which roles the pointer conversion into the interface, meaning the final application can call the C function without having to know about the pointer conversion:
The C function:
// The C declaration header (using __cdecl in a def file):
extern "C" const char* GetLastErrorMessage();
The FORTRAN interface module:
MODULE mINTERFACES
USE ISO_C_BINDING
INTERFACE
FUNCTION GetLastErrorMessagePtr [C, ALIAS: '_GetLastErrorMessage'] ()
USE ISO_C_BINDING
TYPE(C_PTR) :: GetLastErrorMessagePtr
END FUNCTION GetLastErrorMessagePtr
END INTERFACE
CONTAINS ! this must be after all INTERFACE definitions
FUNCTION GetLastErrorMessage()
USE ISO_C_BINDING
CHARACTER*255 :: GetLastErrorMessage
CHARACTER, POINTER, DIMENSION(:) :: last_message_array
CHARACTER*255 last_message
INTEGER message_length
CALL C_F_POINTER(GetLastErrorMessagePtr(), last_message_array, [ 255 ])
DO 10 i=1, 255
last_message(i:i+1) = last_message_array(i)
10 CONTINUE
message_length = LEN_TRIM(last_message(1:INDEX(last_message, CHAR(0))))
GetLastErrorMessage = last_message(1:message_length-1)
END FUNCTION GetLastErrorMessage
And to call this function from a FORTRAN program:
USE MINTERFACES
PRINT *, "--> GetLastErrorMessage: '", TRIM(GetLastErrorMessage()), "'"
My thanks again to heraldkl for providing this solution - I wouldn't have had a clue how do do this without his input.
Solution 3:
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
Solution 4:
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 ?
Solution 5:
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.