Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29391 Discussions

Converting c-string to Fortran string

Simon_Geard
New Contributor I
3,966 Views

In the brave new world of c interoperability the standard requires that c-strings passed into a Fortran subroutine are interpreted as arrays of characters of length 1. So to use them in a Fortran program and array of characters has to be converted into a character string. So my question is what is the best way of doing this? At the moment I'm using the function below with example usage but I'm not convinced it is optimal. Thanks.

[fortran]

module cstr

contains

function c_to_f_string(s) result(str)
  use iso_c_binding
  character(kind=c_char,len=1), intent(in) :: s(*)
  character(len=:), allocatable :: str
  integer i, nchars

  i = 1
  do
     if (s(i) == c_null_char) exit
     i = i + 1
  end do
  nchars = i - 1  ! Exclude null character from Fortran string
  allocate(character(len=nchars) :: str)

  str = transfer(s(1:nchars), str)

end function c_to_f_string

subroutine pstr(s) bind(c,name='pstr')
  use iso_c_binding
  use cstr
  character(kind=c_char,len=1), intent(in) :: s(*)
  character(len=:), allocatable :: str
  integer i, nchars

  write(*,'(a)') c_to_f_string(s)

end subroutine pstr

[/fortran]

[cpp]

extern "C" {
  void pstr(const char*);
};

int main(int argc, char** argv) {
  if (argc > 1) {
    pstr(argv[1]);
  } else {
    pstr("hello");
  }
  return 0;
}

[/cpp]

0 Kudos
5 Replies
TimP
Honored Contributor III
3,966 Views

If "optimal" can be related to "less obscure," why not study several examples you would find in web search or in textbooks?

http://www-01.ibm.com/support/docview.wss?uid=swg1LI72736 is a posted example of direct interoperation between C and Fortran strings according to standard, where there seems little point nowadays in avoiding acknowledged analysis bugs of early compilers.

I suppose the index intrinsic might be used to make a Fortran equivalent of C strnlen.

0 Kudos
Simon_Geard
New Contributor I
3,966 Views

Sorry but I don't understand what you're saying. The code I posted has nothing to do with avoiding compiler bugs and is certainly not obscure. The code to which you refer does not convert a c-string to a fortran string. By optimal I meant efficient and, possibly, compact. For example when it is implemented the findloc function could be used to locate the c_null_char and I would expect that to be at least as efficient as the do loop in my implementation. I'm using transfer but perhaps it would be more efficient to use a do loop. Perhaps there is an Intel intrinsic that does the job, something like QQCTOFSTR.

0 Kudos
IanH
Honored Contributor III
3,966 Views

I've wondered about this to.  What you've got looks ok to me. Observations that aren't particularly significant, and note I've never bothered to measure performance or look at generated assembly, etc:

- I tend to use FORALL or a DO loop around an assignment to explicitly copy the array across to the scalar - transfer like you do might be more efficient, but using transfer always makes me twitchy.  Plus one day you might use a processor where C_CHAR /= KIND('a'), and use of transfer would break silently in that situation.  Assignment at least gives you some character set conversion guarantees.

- It would have been nice if C_TO_F_STRING (perhaps with a CHARACTER(:) pointer on the fortran side) was part of ISO_C_BINDING.  Perhaps one day it might be - and if so there's a reasonable chance that a name like that would be used.  If they do add a function of that name to the intrinsic module, your `USE iso_c_binding` statement is going to cause dramas.  Consequently I always qualify my use of intrinsic modules with ONLY.

- I tend to cheat a bit and use the C library variant of strlen rather than writing out an explicit loop.  That's partly motivated by delusions on my part that some C library programmer has spent months writing hand tuned assembly in order to do strlen efficiently.

- You forgot implicit none.

Something akin to what I use (slightly different starting point of a C_PTR versus a character array, but the approaches become equivalent) is here.

Another option I've mucked around with is to avoid a copy using Fortran pointers.  The lifetime of the fortran pointer is then restricted by the life of the corresponding C object.  Perhaps:

[fortran]

MODULE c_f_string_ptr
  IMPLICIT NONE
  PRIVATE
  PUBLIC :: C_F_STRING
CONTAINS
  FUNCTION C_F_STRING(c_str) RESULT(f_str)
    USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_CHAR
    TYPE(C_PTR), INTENT(IN) :: c_str
    CHARACTER(:,KIND=C_CHAR), POINTER :: f_str
    CHARACTER(KIND=C_CHAR), POINTER :: arr(:)
    INTERFACE
      ! Steal std C library function rather than writing our own.
      FUNCTION strlen(s) BIND(C, NAME='strlen')
        USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_SIZE_T
        IMPLICIT NONE
        !----
        TYPE(C_PTR), INTENT(IN), VALUE :: s
        INTEGER(C_SIZE_T) :: strlen
      END FUNCTION strlen
    END INTERFACE
    !****
    CALL C_F_POINTER(c_str, arr, [strlen(c_str)])
    CALL get_scalar_pointer(SIZE(arr), arr, f_str)
  END FUNCTION C_F_STRING
  SUBROUTINE get_scalar_pointer(scalar_len, scalar, ptr)
    USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_CHAR
    INTEGER, INTENT(IN) :: scalar_len
    CHARACTER(KIND=C_CHAR,LEN=scalar_len), INTENT(IN), TARGET :: scalar(1)
    CHARACTER(:,KIND=C_CHAR), INTENT(OUT), POINTER :: ptr
    !***
    ptr => scalar(1)
  END SUBROUTINE get_scalar_pointer
END MODULE c_f_string_ptr

[/fortran]

0 Kudos
onkelhotte
New Contributor II
3,966 Views

Maybe I´m doing this completely wrong, but I use this method for data exchange between C# and Fortran via a C++ wrapper:

[cpp]
extern "C" int INPUT(const char*, int*);
int openProjectFile(const char* filename)
{
    int lenFilename = sizeof(filename);
    return INPUT(filename, &lenFilename);
}[/cpp]

[fortran]
integer(kind=4) function input(originalFilename)
implicit none
character*255 Filename, originalFilename
write(filename,'(a)') originalFilename
call cutChar0(filename) ! a subroutine that remove the char(0) from filename
input = 0
! some code where input is being altered when there is an error
end function input[/fortran]

In my C# program I can pass a string to openProjectFile. Of course it couldn´t be longer than 255 characters. I haven´t had problems with this approach.

Markus

 

 

0 Kudos
Simon_Geard
New Contributor I
3,966 Views

Thank you very much. I hadn't thought of using strlen - an interesting idea!

0 Kudos
Reply