Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Beginner
22 Views

Assigning a string that will be used in as argument to c function

Hi

I have a list of fixed size strings (character*6) that I want to give to a c function.  To be fully interoperable between Fortran and C I must use an array of single characters.  But how do I assign a character*6 that I have to this array?  The code below works except for this character*6 to character(len=1) var(6) issue.

Example code

    type, bind(C) :: RelayElement

        integer (kind=C_INT) :: lzop_tag
        integer (kind=C_INT) :: relay_tag
        character (kind=C_CHAR) :: codes(6) 
        character (kind=C_CHAR) :: desigs(21)
        character (kind=C_CHAR) :: zuns(2)
    end type RelayElement
    character(len=6) :: new_codes
    type (RelayElement) :: element
    
    interface 
      subroutine c_interop(element) bind(C)
        use iso_c_binding
        import::RelayElement
        implicit none
        type(RelayElement) :: element
      end subroutine c_interop
    end interface  
    new_codes = 'codea'
    !below, it is a pain to assign codes as an array, hard to maintain and read

    element = RelayElement(lzop_tag=1,relay_tag=2, codes=['c','o','d','e','s',C_NULL_CHAR],desigs='desigs',zuns='a')

    element%codes = new_codes  ! this is what I need.  I have the codes in fortran  and need to give it to c. Does not work

    ! element%codes is now just the first character of new_codes repeated

    call c_interop(element) ! this works except for the issue above.

Thanks

Tim

0 Kudos
4 Replies
Highlighted
Beginner
22 Views

Hi

I can get it to work by assigning the Fortran string (Character*6) to the Fortran character array in a loop:

    DO i = 1,SIZE(element%codes)
      element%codes(i:i) = new_codes(i:i)
    end do

It works but it seems very busy.  I can't find another way.

In other parts of my Fortran 77 code, it uses conversion which lets me set the character*6 codes [reference] in the interface block.  This works and is straight forward.  The modern way seems noisier.

interface to subroutine c_interop

   &(carray)

   character*6 carray [reference]

end

Tim

0 Kudos
Highlighted
Valued Contributor II
22 Views

If you need NUL-termination, you have to put it there explicitly:

new_codes = 'codea'//C_NULL_CHAR

Given that, the natural way to copy raw data from one variable to another is via TRANSFER:

element%codes = TRANSFER(new_codes,element%codes,SIZE(element%codes))

In this expression, if LEN(new_codes) > SIZE(element%codes), only the first SIZE(element%codes) characters will be copied. If LEN(new_codes) < SIZE(element%codes), all of new_codes will be copied with garbage following to pad out to SIZE(element%codes). Assuming the C code uses NUL-termination, that extra garbage will not be a problem. Remember that LEN(new_codes) is the declared length of the variable, not the length of what you usefully assigned to it (normally that would be LEN_TRIM(new_codes)).

 

0 Kudos
Highlighted
Honored Contributor I
22 Views

Be careful about NUL termination when the size of codes is less than the length of the source if it is always expected on the C side  Or you can do something like the following:

      new_codes = ..     !  No null ternination here
      asc: associate( lenm => min(size(element%codes), len_trim(newcode)+1) )
         element%codes(1:lenm-1) = transfer( new_codes, mold=element%codes, size=lenm-1 )
         element%codes(lenm:lenm) = c_null_char
      end associate asc

 

0 Kudos
Highlighted
Beginner
22 Views

Thanks!  That helps.

Tim

0 Kudos