- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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]
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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]
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you very much. I hadn't thought of using strlen - an interesting idea!
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page