- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
This is for the v16.0.3 compiler.
I could be wrong about this, but I have in mind that with the latest C interoperability features it is possible to pass a c-array of characters to a fortran procedure as a fortran string. Am I right about this? If so how do I do it? Thanks.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The ISO_C_BINDING module passes arrays of single characters to and from C. That is: a Fortran string is considered to be a C array of single characters. The length is not passed as a hidden argument, as it used to be. So you are supposed to supply the terminating NUL character yourself or use "counted strings" on the C side. That works fine if you know the length to be expected (for instance: from Fortran to C).
You can, however, get a pointer to the C string and convert it to a Fortran pointer. Something along these lines:
type(c_ptr) :: c_string character(len=1), dimension(:), pointer :: f_string call c_f_pointer( c_string, f_string ) write(*,*) 'Length: ', index( f_string, char(0) ) - 1
The pointer c_string can come from the C routine as a char ** argument.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Arjen Markus wrote:The module isn't particularly special - it is the use of the BIND(C) suffix on the relevant function or subroutine statement that is important.
The ISO_C_BINDING module passes arrays of single characters to and from C
The C interoperability stuff in Fortran 2015 also allows a C function to work with a descriptor for a CHARACTER(*) argument.
MODULE m IMPLICIT NONE CONTAINS SUBROUTINE sub(str) BIND(C, NAME='sub') USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_CHAR CHARACTER(*,KIND=C_CHAR), INTENT(IN) :: str PRINT *, str END SUBROUTINE sub END MODULE m
#include "ISO_Fortran_binding.h" void sub(CFI_cdesc_t* str); int main() { char *string = "Hello from C"; CFI_CDESC_T(0) str; CFI_establish( (CFI_cdesc_t*) &str, /* descriptor */ string, /* base address */ CFI_attribute_other, /* attributes */ CFI_type_char, /* type */ strlen(string) * sizeof(char), /* storage size */ 0, /* rank */ 0 ); /* extents (ignored) */ sub((CFI_cdesc_t*) &str); return 0; }
>ifort /c /check:all /warn:all /standard-semantics "2016-06-03 c-interop-f.f90" Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 16.0.3.207 Build 20160415 Copyright (C) 1985-2016 Intel Corporation. All rights reserved. >cl "2016-06-03 c-interop-c.c" "2016-06-03 c-interop-f.obj" Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23918 for x64 Copyright (C) Microsoft Corporation. All rights reserved. 2016-06-03 c-interop-c.c Microsoft (R) Incremental Linker Version 14.00.23918.0 Copyright (C) Microsoft Corporation. All rights reserved. "/out:2016-06-03 c-interop-c.exe" "2016-06-03 c-interop-c.obj" "2016-06-03 c-interop-f.obj" >"2016-06-03 c-interop-c.exe" Hello from C
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks, that's what I was trying to find.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Arjen, ISO_C_BINDING doesn't have anything to do with this! I understand that people use the module name as a shorthand for "C interoperability", but really, the module is just that, a module with some declarations.
Through Fortran 2008 (and version 15 of the Intel compiler), Arjen is correct that there is no straightforward way for C to pass a character string to a Fortran routine that accepts it as a character variable (of length other than 1). I'm not sure I understand the point of Arjen's example, as one could declare the Fortran dummy argument as an array of characters and then use c_f_pointer to get something more useful. Something like this:
subroutine fsub (str) bind(C) use, intrinsic :: iso_c_binding character, dimension(*) :: str character(10000), pointer :: lcl_str integer str_len call c_f_pointer (c_loc(str), lcl_str) str_len = index(lcl_str,C_NULL_CHAR) print *, lcl_str(1:str_len) ...
However, an addendum to the Fortran standard, TS29113 "Further Interoperability with C", part of draft Fortran 2015, adds a new capability which requires a bit of work on the C side but full interoperability with character variables on the Fortran side. This is supported by Intel Fortran 16.
Here's an example:
#include "ISO_Fortran_binding.h" #include <string.h> extern void fsub(CFI_cdesc_t * dv); int main() { CFI_cdesc_t stringdesc; int retval; char * stringval = "This is a C string!"; // Initialize the C descriptor as a scalar character nonpointer retval = CFI_establish(&stringdesc, stringval, CFI_attribute_other, CFI_type_char, strlen(stringval), 0, NULL); // Call fsub fsub(&stringdesc); return 0; }
subroutine fsub (stringval) bind(C) implicit none character(*) :: stringval print *, len(stringval), stringval end subroutine fsub
The C code needs to reference the Fortran Include folder and the Fortran run-time libraries.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Ah, not fast enough!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Okay, I should not use "ISO_C_BINDING" to describe the C interoperability.
What I meant to do with my sketchy code is: get access to the string on the C side, where you do not know in advance how long that is going to be. But I should have explained that better.
That said, the Fortran 2015 method looks interesting. I knew of it, but haven't studied the possibilities.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
In Ian's example, the name='sub' in the BIND(C) isn't necessary, since the standard behavior is to downcase the Fortran name. Doesn't hurt, though. I also note that he uses the CFI_CDESC_T macro specifying zero dimensions - that is, scalar. Also perfectly reasonable, but I thought I should point it out in case people got confused.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
This has proved to be a bit more than the noop I was expecting on the C++ side. The essence of the problem is that the header file doesn't compile nicely (VS 2010):
1>C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2016.3.207\windows\compiler\include\ISO_Fortran_binding.h(156): error C2220: warning treated as error - no 'object' file generated
1>C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2016.3.207\windows\compiler\include\ISO_Fortran_binding.h(156): warning C4200: nonstandard extension used : zero-sized array in struct/union
1> Cannot generate copy-ctor or copy-assignment operator when UDT contains a zero-sized array
Is there a recommended way of handling this? Currently I'm going through all the projects disabling the 4200 warning. It ought to be possible to do this with peoperty pages but I can't figure out how to 'and' the DisableSpecificWarnings setting.
Thanks.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You are compiling with /W4 and /WX. Some of microsoft's own headers won't survive that.
You could put "#pragma warning(disable: 4200)" immediately before you #include the ISO_Fortran_binding.h file.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The warning C4200 you have to live with if you're using MSVC. Our C++ expert tells me the usage is valid C. Intel C++ doesn't complain about this.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I've settled for
#pragma warning( push ) #pragma warning( disable : 4200 ) #include "ISO_Fortran_binding.h" #pragma warning( pop )
which does the job as far as the warnings are concerned. As for it working in practice I still haven't conquered that:
string wd("a/b/c"); CFI_cdesc_t stringdesc; int retval = CFI_establish(&stringdesc, const_cast<char*>(wd.c_str()), CFI_attribute_other, CFI_type_char, wd.length(), 0, NULL); mysub(&stringdesc);
subroutine mysub(wf) bind(c) character(*), intent(in) :: wf integer :: len_wf len_wf = len(wf) ! Gives correct value of 5 ...
However the value of wf is incorrect and the debugger says that the subscripts are out of range and attempts to use it only give the first character as correct. Is there some additional magic I need when using this in the C++ world? I had to load the libifportmdd.lib library in the debug build, was that correct?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
To access the 3rd character use wf(3:3) using substring notation
Do not use wf(3), this is array subscript notation.
You've declared wf as a single character string (no dimension) with an externally defined length.
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Simon Geard wrote:
.. However the value of wf is incorrect and the debugger says that the subscripts are out of range and attempts to use it only give the first character as correct. Is there some additional magic I need when using this in the C++ world? ..
Try (void *) cast instead for the 2nd parameter in CFI_establish function.
To reiterate Steve's point, if all you're interested in is passing (char *) information to the Fortran side, you don't need to do all that gymnastics with C descriptors on the C++ side; instead the following would be ok too
#include <iostream> #include <string> using namespace std; extern "C" { void fsub(const char *); } int main() { int rc = 0; string wd = "a/b/c"; fsub( wd.c_str() ); return rc; }
with first fsub defined as shown by Steve in Message #5 above (by the way, I would apply the TARGET attribute on str dummy argument in Steve's example just to be explicit about the intended processing of that argument in the procedure for the compiler as well as anyone else reading the code). Now, if you have control over the Fortran procedure (equivalent to fsub in Steve's example), I would suggest including an additional parameter to represent the string length with INTENT(IN), VALUE attribute; this will allow one to avoid declaring a local variable of some arbitrary large length (10000 as in the example above which may be an overkill or not long enough depending on one's needs). On the C++ side, one can simply add wd.length() to the parameters for the Fortran procedure, as you know,
And you would know only if the parameters involve previously inoperable types in Fortran 2008 and 2003 such as assumed shape arrays, etc. and parameters with ALLOCATABLE attributes are the newer features from Fortran 2015 needed. See this thread, especially Message #13 for an additional illustration of a case where the enhanced interoperability features from Fortran 2015 help:
https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/601190
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
FortranFan wrote:
Quote:
Simon Geard wrote:
.. However the value of wf is incorrect and the debugger says that the subscripts are out of range and attempts to use it only give the first character as correct. Is there some additional magic I need when using this in the C++ world? ..
Try (void *) cast instead for the 2nd parameter in CFI_establish function.
If all you're interested in is passing (char *) information to the Fortran side, you don't need to do all that gymnastics with C descriptors on the C++ side; instead the following would be ok too
#include <iostream> #include <string> using namespace std; extern "C" { void fsub(const char *); } int main() { int rc = 0; string wd = "a/b/c"; fsub( wd.c_str() ); return rc; }with fsub defined as shown by Steve in Message #5 above (by the way, I would apply the TARGET attribute on str dummy argument in Steve's example just to be explicit about the intended processing of that argument in the procedure for the compiler as well as anyone else reading the code).
(There are two fsub's in #5 - the above is only applicable to the first, where the dummy argument is a character(1) array.)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
ianh wrote:
.. (There are two fsub's in #5 - the above is only applicable to the first, where the dummy argument is a character(1) array.)
Good point, message #14 has now been edited to reflect this. Thanks,

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