Silverfrost Forums

Welcome to our forums

Workaround for C_F_POINTER

8 Mar 2024 11:22 #31234

Is there any workaround code for the F2003 intrinsic procedure C_F_POINTER?

I am trying to adapt a DLL interface to FTN95 using the following function calling C_F_POINTER:

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

Courtesy of: https://stackoverflow.com/questions/9972743/creating-a-fortran-interface-to-a-c-function-that-returns-a-char

8 Mar 2024 10:35 #31237

Interesting problem.

So this function takes a pointer to a string (null terminated) and transfers this to a FORTRAN compatible CHARACTER*(*).

Do you need it to be fully portable, or can it be compatible with FTN95 alone?

9 Mar 2024 7:57 #31240

I am not able to provide an immediate answer to this question but I will add it to my list of issues to investigate.

9 Mar 2024 8:47 #31242

Bill I 'only' need a FTN95 compatible workaround. I encountered two difficulties: the F2003 intrinsic procedure C_F_POINTER and the dynamic allocation of a string length allocate(character(len=length)::f_string).

Paul Thanks for your help.

9 Mar 2024 3:29 #31246

jlb,

Can you give me an example of how this function would be used?

I'll admit to not being 2003 aware of all the changes in syntax, etc. That said, I may have an answer to your query, albeit perhaps not as powerful as the intrinsic might have been.

Bill

9 Mar 2024 5:12 #31247

Here's the code that take a pointer to a 'C' style string and returns the characters in a FORTRAN CHARACTER data type.

Note: You have to declare the function in the calling program with a specific length. That said, you could use a similar function to return the length of the string BEFORE calling the routine that declares the function and use that, if it something that really, truly needs to be variable.

!FTN95 application...
PROGRAM main
  INTEGER k
  character*(255),external:: C_TO_F_STRING ! has to be assigned a specific length here
  character*255:: my_string='This is me'//char(0) ! simulated 'C' string, length s/b 10
  character*32:: result
  ! loc(mystring might be the return value of a 'C' funtion of the form 
  ! char* c_function(variables)
  result = c_to_f_string(loc(my_string))
  print *,'Actual non blank data returned=',len_trim(result),'=',trim(result),' full result=[',result,']'
  k= len(c_to_f_string(loc(my_string)))
  print *,'Function length returned=',k
  pause
END PROGRAM main
character*(*) function C_TO_F_STRING(c_string_pointer)
! no error checking has been performed here (like for a NULL result from the allocate()
integer(7):: c_string_pointer ! this is a pointer to somewhere in memory
character(:) , allocatable:: f_string

integer:: array_index ! this will be used to determine how long the string is before allocation
array_index = 0
do while(ichar(ccore1(c_string_pointer+array_index)) .ne.0)
  array_index = array_index+1
  end do
if(array_index.le.0)array_index = 1 ! always return something
allocate(character*(array_index):: f_string)
f_string = ' ' ! make the entire string blank, regardless
print *,'Length of result string to be allocated=',len(f_string) ! comment out for production
do i=1,array_index
  f_string(i:i) = ccore1(c_string_pointer+i-1)
  end do
c_to_f_string = f_string
deallocate(f_string)
return
end

Here's the run-time. Note: The 'full result' here is shown truncated because multiple spaces are removed when posting.

' k= len(c_to_f_string(loc(my_string))) print ,'Function length returned=',k pause END PROGRAM main character(*) function C_TO_F_STRING(c_string_pointer) ! no error checking has been performed here (like for a NULL result from the allocate() integer(7):: c_string_pointer ! this is a pointer to somewhere in memory character(:) , allocatable:: f_string

integer:: array_index ! this will be used to determine how long the string is before allocation
array_index = 0
do while(ichar(ccore1(c_string_pointer+array_index)) .ne.0)
  array_index = array_index+1
  end do
if(array_index.le.0)array_index = 1 ! always return something
allocate(character*(array_index):: f_string)
f_string = ' ' ! make the entire string blank, regardless
print *,'Length of result string to be allocated=',len(f_string) ! comment out for production
do i=1,array_index
  f_string(i:i) = ccore1(c_string_pointer+i-1)
  end do
c_to_f_string = f_string
deallocate(f_string)
return
end

Here's the run-time. Note: The 'full result' here is shown truncated because multiple spaces are removed when posting. [quote:28a3d39e25] Length of result string to be allocated= 10 Actual non blank data returned= 10=This is me full result=[This is me ] Length of result string to be allocated= 10 Function length returned= 255

9 Mar 2024 5:53 #31249

Bill Thanks a lot for your help. I didn't know about the FTN95-function CCORE1. I will test your code as soon as possible. For your complete information, here is the interface module to the 3rd part DLL in its Intel Fortran version:

MODULE Test
USE, INTRINSIC::ISO_C_BINDING

INTERFACE
  TYPE(C_PTR) FUNCTION FuncPtr(handle) BIND(C, NAME='FuncPtrA')
  USE, INTRINSIC :: ISO_C_BINDING
  TYPE(C_PTR), VALUE :: handle          
  END FUNCTION
END INTERFACE

CONTAINS

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

FUNCTION Func(handle) RESULT(f_string)
  USE ISO_C_BINDING 
  TYPE(C_PTR), VALUE :: handle          
  CHARACTER(:), ALLOCATABLE :: f_string
  f_string = C_to_F_string(FuncPtr(handle))    
END FUNCTION

END MODULE
9 Mar 2024 11:06 #31251

Just an FYI. Here's the FTN95 statement for the interface to one of my DLL's. It returns the pointer to a file handle from 'C'. So, it's size will be either 32-bit or 64-bit depending on the memory model.

C_EXTERNAL OPEN_LOG_FILE 'open_log_file'(string,string):INTEGER(7)! returns the file pointer

And. here's the function definition for that call:

compile_header
 FILE *open_log_file(char *name,char *option) // returns the file pointer

where 'compile_header' is:

#define compile_header extern 'C' 

I'm finding that for 64-bit, no header is needed, depending on the 'C' compiler.

11 Mar 2024 12:12 #31263

Bill

Thanks a lot for this workaround, which does the trick.

12 Mar 2024 1:51 #31266

jlb,

Happy to help!

Bill

19 Mar 2024 1:22 #31288

jib

The ClearWin+ interfaces in \Silverfrost\FTN95\source64\clrwin.f95 illustrate one way to pass C strings to and from Fortran code.

See, for example, send_accelerator$ which sends a null terminated string.

For strings passed from C to Fortran, see the subroutine called PAD and note how it is used.

21 Mar 2024 4:07 #31294

C_F_POINTER in the ISO_C_BINDING module (2003 Fortran Standard) has now been implemented for the next full release of FTN95.

It will appear in the next interim release but will also require an extended definition for the ISO_C_BINDING module (in \Silverfrost\FTN95\include).

22 Mar 2024 9:42 #31295

Paul

Thanks for pointing out the send_accelerator$ and PAD subroutines, i wasn't aware of them, as well as for the C_F_POINTER implementation.

30 Oct 2024 11:21 #31665

Should FTN95 v.9.05 be considered an interim release? If so, how to proceed with the required extended definition for the file iso_c_binding.mod (found in the include directory and dated 3rd November 2023)? A call like

CALL C_F_POINTER(c_string_pointer, char_array_pointer, [255])

produces an error at compile time

)

produces an error at compile time [quote:1a0ffcbf8f]error 1251 - Invalid arguments in call to C_F_POINTER

30 Oct 2024 1:30 #31667

jlb

Full releases are described in the Announcements section of this Forum.

Releases such as v9.05 that can be downloaded from the Support 'sticky post' are interim releases.

The recent implementation of C_F_POINTER requires a new version of the related Silverfrost mod file and this will be included in the next full release.

5 Nov 2024 2:23 #31670

Paul Thank you for the clarification.

5 Nov 2024 4:18 #31671

jlb

I should have added...

Users with a support licence are notified directly about full releases by Silverfrost Ltd.

Users of the personal edition can download full releases from the Silverfrost.com website.

5 Nov 2024 4:28 #31672

Paul Thank you. I was just wondering as the new implementation was announced for an interim release, but the also needed mod file as in a full release.

26 Feb 2025 12:41 #31944

Using FTN95 v. 9.10, a call like

CALL C_F_POINTER(c_string_pointer, char_array_pointer, [255])

still produces an error at compile time

)

still produces an error at compile time [quote:25a63a9b4d]error 1251 - Invalid arguments in call to C_F_POINTER

Note: Looking inside ISO_C_BINDING.MOD (in \Silverfrost\FTN95\include) with a editor doesn't show any string C_F_POINTER

26 Feb 2025 3:52 #31945

jlb

C_F_POINTER does not appear in the module because it has to be created by FTN95.

It is non-trivial and I found it difficult to use. The sample program below works for x64 and uses a test function defined in ClearWin64.dll.

program main
  use iso_c_binding,only:c_ptr,c_f_pointer
  implicit none
  interface
    subroutine c_f_pointer_test$(p) bind(c,name='__c_f_pointer_test')
      import :: c_ptr
      type(c_ptr), intent(out) :: p
    end subroutine
  end interface
  type(c_ptr) :: cptr
  real,pointer :: a(:)
  real,pointer :: b(:,:)
  real,pointer :: c
  real::ss1,ss2
  integer s(1)
  call c_f_pointer_test$(cptr)
  s = [12]
  call c_f_pointer(cptr, a, s)
  ss1 = sum(a)
  call c_f_pointer(cptr, b, [3,4])
  ss2 = sum(b)
  call c_f_pointer(cptr, c)
  if(ss1 == ss2 .and. c == 1.0) print*, 'Success'
end program main


extern 'C' void __c_f_pointer_test(float*& a)
{
  a = (float*)malloc(12 * sizeof(float));
  float* b = a;
  for (int i = 1; i <= 12; ++i) *b++ = (float)i;
}
Please login to reply.