Creating a FORTRAN interface for a C function that returns char *

I was delayed by this for about a week, now, and searched the forum after the forum to get a clear explanation of how to send char * from C to FORTRAN. To make things more unpleasant, sending the char * argument from FORTRAN to C was direct ...

Sending char * argument from FORTRAN to C (this works fine):

// The C header declaration (using __cdecl in a def file): extern "C" double GetLoggingValue(char* name); 

And from FORTRAN:

 ! The FORTRAN interface: INTERFACE REAL(8) FUNCTION GetLoggingValue [C, ALIAS: '_GetLoggingValue'] (name) USE ISO_C_BINDING CHARACTER(LEN=1, KIND=C_CHAR), DIMENSION(*), INTENT(IN) :: name END FUNCTION GetLoggingValue END INTERFACE ! Calling the function: GetLoggingValue(user_name) 

When I try to use similar logic to return char * from C, I get a problem after the problem. One of the attempts that I felt should work:

 // The C declaration header (using __cdecl in a def file): extern "C" const char* GetLastErrorMessage(); 

And the FORTRAN interface:

 INTERFACE FUNCTION GetLastErrorMessage [C, ALIAS: '_GetLastErrorMessage'] () USE ISO_C_BINDING CHARACTER(LEN=1, KIND=C_CHAR), DIMENSION(255), :: GetLastErrorMessage END FUNCTION GetLastErrorMessage END INTERFACE 

(I cannot literally use DIMENSION (*), so I moved to 255.)

This should return a pointer to an array of 255 C-style characters, but if so, I could not convert it to a meaningful string. In practice, it returns a random character set, from Wingdings to the bell character ...

I also tried to return:

  • Pointer to a CHARACTER (LEN = 255, KIND = C_CHAR).
  • Literally CHARACTER (LEN = 255, KIND = C_CHAR).
  • INTEGER (C_SIZE_T) and tried to guess this in a pointer to a string array.
  • CHARACTER.
  • and etc.

If anyone can give me an example of how to do this, I would be very grateful ...

Yours faithfully,

Mike

+8
source share
7 answers

Dynamic-length strings are always a little complicated with C interaction. A possible solution is to use pointers.

First, a simple case where you need to pass a string with a null character to a C function. If you really only pass the string inside, you must ensure that it terminates with c_null_char, so this direction is pretty straight forward. Here are examples from the 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 lua_getfield interface looks like this:

 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:

 void lua_getfield (lua_State *L, int idx, const char *k) 

Now a slightly more complicated case when we have to deal with a returned string from C with dynamic length. The most portable solution I've found so far is using pointers. Here is an example with a pointer where the line is given by C-Routine (also from Aotus, 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's an attempt to figure out how c_ptr can be interpreted as a Fortran character string: Suppose you get c_ptr pointing to a string:

 type(c_ptr) :: a_c_string 

And its length is set by the variable len 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 are doing the mapping:

 call c_f_pointer(a_c_string, string, [ stringlen ]) 
+12
source

We thank the herald for giving me a solution to this very unpleasant problem. I publish what I eventually implemented, and which roles convert the pointer to an interface, that is, the final application can call the C function without knowing about the pointer conversion:

C function:

 // The C declaration header (using __cdecl in a def file): extern "C" const char* GetLastErrorMessage(); 

Interface module FORTRAN:

 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 the FORTRAN program:

 USE MINTERFACES PRINT *, "--> GetLastErrorMessage: '", TRIM(GetLastErrorMessage()), "'" 

Thanks again to heraldkl for providing this solution - I would not know how to do this without entering it.

+5
source

This thread is a bit old, but since I had a similar problem (and maybe there will be others), I will still post an answer.

The codes above will lead to a segmentation error if, for any reason, the string C is NULL. In addition, there is no need to return a string of 255 characters (which you probably need to trim before use), since Fortran 2003/2008 supports functions that return selected objects. Using all the information published above, I got the following function, which receives the string C (pointer) and returns the corresponding Fortran string; If the string C is null, it returns "NULL", similar to C "(null)" printed in such 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 
+4
source

I always struggle with these interaction features. I think your interface should declare

 CHARACTER(KIND=C_CHAR),DIMENSION(*) :: getlasterrormessage 

and that when you call the function, you pass the corresponding Fortran character variable with a length equal to or greater than the length of the array of C characters that you expect to return.

Since you seem to have an Intel Fortran, check out the provided code samples, they give a complete example for this.

I think you know that what you posted is not Fortran syntactically correct?

+2
source

I also struggled with calling a C routine that returns a string, and the answers above were very useful, but since I know almost nothing about C and the answers are a bit confusing, I just wanted to make my decision that uses the C pointer, I did Cannot use any other suggestions above. The C program that I call opens a separate window for finding the file name.

 program test use iso_c_binding implicit none ! AC 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 

We hope that this example will simplify the next option with the same problem.

+2
source

In Fortran, an element should be declared as "character (kind = c_char, len = 1), dimension (255)" and not len ​​= 255. This will create an array of characters one in length, which you need on the C side. Which can be confusing , so this is an exception that allows Fortran to match strings with one-dimensional arrays.

You want to say that you want to call the Fortran procedure from C? See This Example: Calling a FORTRAN Subroutine with C.

EDIT: Both format and gfortran say that arrays are not allowed, since the function returns in this context. Which makes returning strings as arguments of a function from C to Fortran more stringent than using a string as an argument (example in the link above) ... you need to use a pointer and then c_f_pointer Fortran, built-in to convert from a C-string to a Fortran string, as explained haralkl. 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 
+1
source

If you know the length of the string, then the Pap 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 takes a C pointer with a string and a string length and returns a copy as a Fortran string.

0
source

All Articles