- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Consider the following Fortran function,
module String_mod use, intrinsic :: iso_fortran_env, only: IK => int32 implicit none contains function getLowerCase(StrVec) result(StrVecLowerCase) bind(C, name="getLowerCase") character(len=*), intent(in) :: StrVec integer(IK), parameter :: duc = ichar('A') - ichar('a') character(len=1) :: StrVecLowerCase character :: ch integer(IK) :: lenStrVec integer(IK) :: i lenStrVec = len(StrVec) write(*,"(*(g0))") "From Inside Fortran@getLowerCase(): StrVec = ", (StrVec(i:i),i=1,lenStrVec) do i = 1, lenStrVec - 1 ch = StrVec(i:i) if (ch>='A' .and. ch<='Z') ch = char(ichar(ch)-duc) !StrVecLowerCase(i:i) = ch end do StrVecLowerCase = "S" end function getLowerCase end module String_mod
which can take a string from C, and ideally convert it to lower case and send it back to C as the function ouput. I have managed to send a string from C to Fortran with the following C code,
#include "ISO_Fortran_binding.h" #include <string.h> extern char* getLowerCase(CFI_cdesc_t *); int main() { char *StrVec = "This is a C string"; // Initialize the C descriptor as a scalar character nonpointer CFI_cdesc_t StrVecDesc; int retval = CFI_establish(&StrVecDesc, StrVec, CFI_attribute_other, CFI_type_char, strlen(StrVec), 0, NULL); // Call getLowerCase char *success = getLowerCase(&StrVecDesc); printf("%s", &success); return 0; }
However, I am not sure at the moment what the best approach would be according to Fortran-2018 to return a string (not a single character) from Fortran to C as the function output. Any help is appreciated.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
There is not currently a way to have an interoperable function return a character string. Your Fortran function needs to allocate new space for the result - and if you want C to deallocate it you'd really want to pass a CFI_cdesc_t as an argument corresponding to a CHARACTER(:), POINTER dummy argument. The Fortran code would allocate it and the C code could call CFI_DEALLOCATE.
A function could return a C_PTR but you'd need to allocate the memory in a way C could free. As an extension, you can use malloc for that in Intel Fortran.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
A. King wrote:.. Any help is appreciated
This is given your last statement.
- Use a SUBROUTINE procedure in Fortran. If you really prefer a FUNCTION, follow the general paradigm followed by Microsoft, etc. where the function return is an error code but the "data" of interest are function parameters
- Perform all allocations and freeing of memory of your C objects (e.g., modified string object, equivalent of char * success in your example) on the C side of your code. Following point 1, you can pass in the allocated "data" as an actual parameter to the Fortran procedure which simply operates on it. You can then employ the interoperability features introduced as part of Fortran 2003 in your Fortran procedure.
- However if you feel strongly about operating with a Fortran procedure with a dummy argument of ALLOCATABLE/POINTER attribute (say toward a CHARACTER type of variable length) and your source "data" is of intrinsic type in C already (e.g., StrVec object in your example), then still follow point 1 above but use the Fortran 2018 facility of enhanced interoperability but with the modified data such as a "string" transformed to all lower case. See an example of this below.
module String_mod use, intrinsic :: iso_c_binding, only : c_int, c_char, c_size_t, c_null_char, c_f_pointer, c_loc implicit none contains subroutine ToLower( StrIn, Siz, StrOut, Irc ) bind(C, name="ToLower") ! Argument list character(kind=c_char,len=1), intent(in), target :: StrIn(*) integer(kind=c_size_t), intent(in), value :: Siz character(kind=c_char, len=1), allocatable, intent(out), target :: StrOut(:) integer(kind=c_int), intent(inout) :: Irc Irc = 0 ! Error checking elided e.g., what if Siz is < 0 ! Allocate the out string; size is plus 1 for NUL termination allocate( StrOut(Siz+1), stat=Irc ) if ( Irc /= 0 ) return blk: block character(kind=c_char,len=Siz), pointer :: F_STR_IN => null() character(kind=c_char,len=size(StrOut)), pointer :: F_STR_OUT => null() character(kind=c_char,len=1) :: ch integer, parameter :: duc = ichar('A') - ichar('a') integer(c_size_t) :: i call c_f_pointer( cptr=c_loc(StrIn), fptr=F_STR_IN ) call c_f_pointer( cptr=c_loc(StrOut), fptr=F_STR_OUT ) print *, "Inside Fortran subroutine ToLower" print *, "StrVec = ", F_STR_IN F_STR_OUT = F_STR_IN // c_null_char do i = 1, size(StrOut) - 1 ch = F_STR_IN(i:i) if (ch>='A' .and. ch<='Z') ch = char(ichar(ch)-duc) F_STR_OUT(i:i) = ch end do F_STR_IN => null() F_STR_OUT => null() end block blk return end subroutine ToLower end module String_mod
#include <stdio.h> #include <string.h> #include <ISO_Fortran_binding.h> // Prototype for the Fortran procedure void ToLower( const char *, size_t, CFI_cdesc_t *, int * ); int main() { int irc = 0; char* s = "This is a C string"; // You may find it easier to work with the macro from ISO_Fortran_binding.h CFI_CDESC_T(1) str; // Initialize the C descriptor as a scalar character nonpointer data type irc = CFI_establish((CFI_cdesc_t *)&str, NULL, CFI_attribute_allocatable, CFI_type_char, sizeof(char *), (CFI_rank_t)1, NULL); if (irc != CFI_SUCCESS) return(irc); // Call the Fortran procedure for string manipulation ToLower( s, strlen(s), (CFI_cdesc_t *)&str, &irc ); if (irc != 0) { printf("ToLower failed: irc = %d\n", irc); return(irc); } // Consume the modified "string" which is effectively (char *)str.base_addr printf("In C main\nFortran subroutine ToLower returned: %s\n", (char *)str.base_addr); // Free the CFI decriptor object used for Fortran-C interoperability irc = CFI_deallocate( (CFI_cdesc_t *)&str ); return (irc); }
Upon execution with Intel Fortran 2020 BETA Update 1, program output is
Inside Fortran subroutine ToLower StrVec = This is a C string In C main Fortran subroutine ToLower returned: this is a c string
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi Fortran Fan, Thanks for the comprehensive advice provided. I was hoping to have an ultimate yet simple formal Fotran method to make a Fortran function with string output interoperate with C. But in anycase, the solution #3 in your response is great. We need more people like you to write such Fortran examples and advice on the web.

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page