I have a Fortran main program under which there are many subroutines. One of the subroutine calls a c++ function. That c++ function is calling another Fortran subroutine. Now I need to compile all of them together to get the output.
I have tried to compile c++ file with icl. Then I have used ifort as a linker between Fortran file and object file created for the c++. But the method is not working. Its showing unresolved external symbol.
I expect that you at least saw this: https://software.intel.com/en-us/node/691954
What they wrote there mostly refers to C, though there is mention of C++ libraries. IF you link with C++ ocde, you need those.
You should read about symbol name mangling in C++. Because C++ supports overloaded funktions but linker requires unique symbols, C++ generates
something like _foo****#8 instead of foo for a function foo(int i, float j), where * depend on compiler and on type of arguments.
Fortran code generates C-styled symbols if BIND(C) used, you can force C++ to generate one for a function , by using extern "C" in C++ code both for prototypes of fortran functions called from C++ and for functions that will be called from fortran.
This example works with C compiler, but for C++ you need to change symbol generated (https://software.intel.com/en-us/node/691929#92BDCE7A-30FA-4A60-BCDB-7CE1521572EC). Note that C and Fortran interoperability isn't standardized and usually not portable from one compiler set to another. I had to deal with problem that Compaq compiler was mangling function names as well but not in C++ way, or that PGI Fortran required stdcall conventions.
Fortran Code Example
subroutine Simulation(alpha, beta, gamma, delta, arrays) BIND(C)
use, intrinsic :: ISO_C_BINDING
implicit none
integer (C_LONG), value :: alpha
real (C_DOUBLE), intent(inout) :: beta
integer (C_LONG), intent(out) :: gamma
real (C_DOUBLE),dimension(*),intent(in) :: delta
type, BIND(C) :: pass
integer (C_INT) :: lenc, lenf
type (C_PTR) :: c, f
end type pass
type (pass), intent(inout) :: arrays
real (C_FLOAT), ALLOCATABLE, target, save :: eta(:)
real (C_FLOAT), pointer :: c_array(:)
...
! Associate c_array with an array allocated in C
call C_F_POINTER (arrays%c, c_array, (/arrays%lenc/) )
...
! Allocate an array and make it available in C
arrays%lenf = 100
ALLOCATE (eta(arrays%lenf))
arrays%f = c_loc(eta)
...
end subroutine Simulation
C Struct declaration Example
struct pass {int lenc, lenf; float *c, *f;};
C Function Prototype Example
void simulation(long alpha, double *beta,
long *gamma, double delta[], struct pass *arrays);
C Calling sequence Example
simulation(alpha, &beta, &gamma, delta, &arrays);
Related
I am working with a legacy Fortran library that requires a character scalar PATH as an argument to the subroutine. The original interface was:
SUBROUTINE MINIMAL(VAR1, ..., PATH)
CHARACTER (LEN=4096) PATH
...
I need to be able to call this from C++ so I have made the following changes:
SUBROUTINE MINIMAL(VAR1, ..., PATH) &
BIND (C, NAME="minimal_f")
USE ISO_C_BINDING, ONLY: C_CHAR, C_NULL_CHAR
CHARACTER (KIND=C_CHAR, LEN=1), DIMENSION(4096), INTENT(IN) :: PATH
CHARACTER (LEN=4096):: new_path
! Converting C char array to Fortran CHARACTER.
new_path = " "
loop_string: do i=1, 4096
if ( PATH (i) == c_null_char ) then
exit loop_string
else
new_path (i:i) = PATH (i)
end if
end do loop_string
as per this answer. This works to convert the C-style char array to its Fortran scalar equivalent, with two problems:
This code is on the critical path so doing the same conversion every time when the answer is the same is inefficient
I would strongly prefer to not have to edit legacy code
I have tried:
Just accepting a CHARACTER (LENGTH=4096) :: new_path directly with the ISO C binding, but I get the following compiler error:
Error: Character argument 'new_path' at (1) must be length 1 because procedure 'minimal' is BIND(C)
This answer and others that I have read suggest that the ISO C binding seems to restrict what I can pass as parameters to the function, although I haven't found any official documentation yet.
This answer, which gives another algorithm to turn a C-style string
into a Fortran-style equivalent in the C code and passing it to the Fortran subroutine without using the ISO C binding. (This function suggests a similar algorithm). This seems like exactly what I want but I have a linker error without the binding:
Undefined symbols for architecture x86_64:
"_minimal", referenced from:
C++-side function declaration:
extern "C" {
double minimal(int* var1, ..., const char* path);
}
This suggests that my compiler (gcc) prepends the function name with an underscore when in an extern block. gfortran, however, does not let me name the subroutine _minimal so the linker can't find the symbol _minimal. (The aforementioned link suggests adding an underscore to the end of the C-side function name but this doesn't work either because of the leading underscore.)
I want to process a C-style string into a Fortran-style character scalar once in my C++ code and be able to pass it into the original interface. Any ideas?
Fortran 2018 allows interoperable procedures to have character dummy arguments of assumed length, relaxing the restriction that such dummy arguments must be of length one.
So we can write a Fortran procedure as
subroutine minimal(path) bind(c)
use, intrinsic :: iso_c_binding, only : c_char
character(*,c_char), intent(in) :: path
...
end subroutine minimal
and continue our life knowing that we've also improved our Fortran code by using an assumed length scalar instead of an explicit length one. No "Fortran side" copy of this character dummy is required.
The sad part of this story is that the dummy argument path is not interoperable with a char. So instead of the formal parameter of the C (or C++) function being char * , it must be CFI_cdesc_t *. For (C) example:
#include "ISO_Fortran_binding.h"
#include "string.h"
void minimal(CFI_cdesc_t *);
int main(int argc, char *argv[]) {
/* Fortran argument will be a scalar (rank 0) */
CFI_CDESC_T(0) fpath;
CFI_rank_t rank = 0;
char path[46] = "afile.txt";
CFI_establish((CFI_cdesc_t *)&fpath, path, CFI_attribute_other,
CFI_type_char, strlen(path)*sizeof(char), rank, NULL);
minimal((CFI_cdesc_t *)&fpath);
return 0;
}
A C++ example will be similar.
An notable part of the story is that you'll need a Fortran compiler which implements this part of Fortran 2018. GCC 11 does not.
IanH's answer draws attention to an approach which avoids modifying the original Fortran subroutine at all. There certainly are times when avoiding any change there is good (repeating slightly what IanH said):
using bind(c) means an explicit interface will now always be required when calling the modified subroutine through Fortran itself. Perhaps some parts of your code used it with an implicit interface
the original was tested (or wasn't) and you don't want to break anything
you don't want to potentially change the argument from default kind to interoperable kind (if these do differ)
the explicit length dummy argument really is wanted
you just don't want to modify it if not required
Any one of those would make a good argument, so in that spirit I'll add to the C example with the thin wrapper.
Fortran:
subroutine minimal_wrap(path) bind(c, name='minimal')
use, intrinsic :: iso_c_binding, only : c_char
character(*,c_char), intent(in) :: path
call minimal(path)
end subroutine minimal_wrap
subroutine minimal(path)
character(4096) path
print*, trim(path)
end subroutine minimal
C:
#include "ISO_Fortran_binding.h"
#include "string.h"
void minimal(CFI_cdesc_t *);
static const int pathlength=4096;
int main(int argc, char *argv[]) {
/* Fortran argument will be a scalar (rank 0) */
CFI_CDESC_T(0) fpath;
CFI_rank_t rank = 0;
char path[pathlength];
/* Set path as desired. Recall that it shouldn't be null-terminated
for Fortran */
CFI_establish((CFI_cdesc_t *)&fpath, path, CFI_attribute_other,
CFI_type_char, pathlength*sizeof(char), rank, NULL);
minimal((CFI_cdesc_t *)&fpath);
return 0;
}
C++ using containers will arguably be nicer.
Recall that this puts responsibility on the C side to ensure the array is long enough (as you have in pure Fortran calls).
Equally if you need to be robust to differences in default character and interoperable character with that copy (as in IanH's answer) you can apply those same tricks to copy as required (or you can do this with conditional compilation and configure-time checks). By this point however, you may as well just assume always copy or use the array argument.
The answer to the question title is typically a std::string object, padded to the relevant fixed Fortran CHARACTER scalar length with spaces. Alternative storage objects (std::vector<char>, or a C-style char array) could be used on the C++ side, but the approach is similar.
(If the Fortran code used an assumed length character argument, rather than fixed length, then the padding would not be required. Whether this change is possible depends on the details of the MINIMAL subroutine. Fixed length character variables are typically an anachronism - this answer is not advocating their use in new code.)
On the Fortran side, you can write a thin wrapper that the C++ can call, that uses sequence and pointer association to avoid the need to copy the string data, for typical C++/Fortran platforms of today. A copy (or modification of the legacy Fortran code) is unavoidable if the interoperable character kind is not the same as the character kind of the legacy Fortran procedure. The example code below is robust to this situation, but I expect platforms that require that code path to be rare.
For default character and C_CHAR interoperable character arguments, sequence association permits an array dummy argument to be associated with the sequence of characters designated by the actual argument. This effectively permits association between character scalars and arrays with different lengths.
(Do not confuse the ISO_C_BINDING intrinsic module with the BIND(C) procedure suffix. BIND(C) fundamentally changes the interface of a procedure to enable calls between C and Fortran - ISO_C_BINDING is just a module with some handy types, constants and procedures for such calls.)
Example C++:
#include <string>
#include <cassert>
const int path_length = 4096;
extern "C" int legacy_cintf(char* array);
int main()
{
std::string some_long_text
= "It was the best of times, it was the worst of times, it was "
"the age of wisdom, it was the age of foolishness, it was the "
"epoch of belief, it was the epoch of incredulity, it was the "
"season of light, it was the season of darkness, it was the "
"spring of hope, it was the winter of despair.";
assert(some_long_text.size() < path_length);
std::string path = std::string(path_length, ' ');
path.replace(0, some_long_text.size(), some_long_text);
legacy_cintf(&path[0]);
return 0;
}
Example Fortran:
MODULE m
IMPLICIT NONE
CONTAINS
SUBROUTINE legacy_cintf(array) BIND(C, NAME='legacy_cintf')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INT, C_CHAR
CHARACTER(LEN=1,KIND=C_CHAR), TARGET :: array(4096)
CHARACTER(LEN=SIZE(array)), POINTER :: scalar
LOGICAL :: copy_required
copy_required = C_CHAR /= KIND(scalar)
IF (copy_required) THEN
ALLOCATE(scalar)
CALL do_copy(array, scalar)
ELSE
CALL do_associate(array, scalar)
END IF
CALL LEGACY(scalar)
IF (copy_required) DEALLOCATE(scalar)
END SUBROUTINE legacy_cintf
SUBROUTINE do_associate(arg, scalar)
CHARACTER(*), INTENT(OUT), POINTER :: scalar
CHARACTER(LEN=LEN(scalar)), INTENT(IN), TARGET :: arg(1)
scalar => arg(1)
END SUBROUTINE
SUBROUTINE do_copy(arg, scalar)
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_CHAR
CHARACTER(*), INTENT(OUT) :: scalar
CHARACTER(LEN=LEN(scalar), KIND=C_CHAR), INTENT(IN) :: arg(1)
scalar = arg(1)
END SUBROUTINE do_copy
END MODULE m
SUBROUTINE LEGACY(PATH)
CHARACTER(4096) :: PATH
PRINT *, TRIM(PATH)
END SUBROUTINE LEGACY
I had read many posts here about mixing languages use of Fortran and C++. However, I'm still stuck with my current problem: my Fortran program always aborted.
I have the Fortran program: test-cc.f90 and the C++ program: deb_cc.cc.
deb_cc.cc contains:
#include <iostream>
using namespace std;
extern "C" void deb_cc_(float*** rh,int* x, int* y , int* z_ext )
{
cout <<"thinkdeb 1"<<endl;
int i, j, k;
cout <<"thinkdeb 1"<<endl;
cout <<"thinktest i=8,j=4,k=1"<< " (*x) " << (*x)<<endl;
cout <<"thinktest i=8,j=4,k=1"<< " x3/rh " << rh[1][1][1]<<endl; //abortion
// here
cout <<"thinkdeb 7"<<endl;
return;
}//end function
test-cc.f90 contains:
use ISO_C_BINDING
implicit none
interface
subroutine deb_cc( rh,x,y,z_ext)
use ISO_C_BINDING
implicit none
real(c_float),allocatable::rh(:,:,:)
integer(c_int):: x,y,z_ext
end subroutine
end interface
integer nx,ny,nz
parameter (nx=10,ny=10,nz=10)
real ,dimension (:,:,:),allocatable:: x1
integer:: iy1,iy2,iy3,iy4
integer i,j,k
allocate(x1(nx,ny,nz))
do k=1,nz
do j=1,ny
do i=1,nx
x1(i,j,k)=k*1000+j*100+i
enddo
enddo
enddo
iy1=nx
iy2=ny
iy3=nz
call deb_cc(x1,iy1,iy2,iy3)
end
I compiled them by pgf90 -c test-cc.f90 and pgcpp -c deb_cc.cc
Finally, I linked them by pgf90 -pgcpplibs test-cc.o deb_cc.o.
The output is:
thinktest in test- x1 (8,2,2) is 2208.000
thinkdeb 1
thinkdeb 1
thinktest i=8,j=4,k=1 (*x) 10
Segmentation fault (core dumped)
You use the iso_c_binding module, but your procedure interface is not C interoperable.
The iso_c_binding module is not the most important thing. The bind(C) attribute is the key. (I ranted several times about the unfortunate name of the tag here)
You use an assumed shape allocatable array argument
real(c_float),allocatable::rh(:,:,:)
these are not allowed in interoperable procedures in Fortran 2008, because C or C++ have no idea what to do with them. They are not just addresses. If you used the bind(C) attribute in the interface, the compiler should tell you it is wrong.
There is a possibility to pass them in the next Fortran standard (in an existing TS actually) using a special C header, but some compilers (notably gfortran) are still not compatible.
As you do not do any reallocation on the C side (at least in your example), you can just pass the array as an assumed size (array(*)) argument. I also changed the C++ name, no need for the underscore.
interface
subroutine deb_cc(rh,x,y,z_ext) bind(C, name="deb_cc")
use ISO_C_BINDING
real(c_float) :: rh(*)
integer(c_int):: x,y,z_ext
end subroutine
end interface
On the C side, you cannot use the C arrays which are pointers to pointers ([i][j][k]). What you receive from Fortran is a single block of memory. You also have to pass the array shape. At least in the first two Fortan dimensions.
I would just use a macro to index the array in C.
// adjust as needed, many variants possible
#define IND(i,j,k) = i + (j-1) * nx + (k-1) * nx * ny
// adjust as needed, many variants possible
extern "C" void deb_cc(float *rh, int *nx, int *ny, int *nz) {
cout <<"thinktest i=8,j=4,k=1"<< " x3/rh " << rh(IND(8,4,1))<<endl;
}
We are trying to take over the memory allocation of a legacy Fortran code (+100,000 lines of code) in C++, because we are using a C library for partitioning and allocating distributed memory on a cluster. The allocatable variables are defined in modules. When we call subroutines that use these modules the index seems to be wrong (shifted by one). However, if we pass the same argument to another subroutine we get what we expect. The following simple example illustrates the issue:
hello.f95:
MODULE MYMOD
IMPLICIT NONE
INTEGER, ALLOCATABLE, DIMENSION(:) :: A
SAVE
END MODULE
SUBROUTINE TEST(A)
IMPLICIT NONE
INTEGER A(*)
PRINT *,"A(1): ",A(1)
PRINT *,"A(2): ",A(2)
END
SUBROUTINE HELLO()
USE MYMOD
IMPLICIT NONE
PRINT *,"A(1): ",A(1)
PRINT *,"A(2): ",A(2)
CALL TEST(A)
end SUBROUTINE HELLO
main.cpp
extern "C" int* __mymod_MOD_a; // Name depends on compiler
extern "C" void hello_(); // Name depends on compiler
int main(int args, char** argv)
{
__mymod_MOD_a = new int[10];
for(int i=0; i<10; ++i) __mymod_MOD_a[i] = i;
hello_();
return 0;
}
We are compiling with:
gfortran -c hello.f95; c++ -c main.cpp; c++ main.o hello.o -o main -lgfortran;
Output from running ./main is
A(1): 1
A(2): 2
A(1): 0
A(2): 1
As you can see the output of A is different, though both subroutines printed A(1) and A(2). Thus, it seems that HELLO starts from A(0) and not A(1). This is probably due to that ALLOCATE has never been called directly in Fortran so that it is not aware of the bounds of A. Any work arounds?
The ISO_C_BINDING "equivalent" code:
c++ code:
extern "C" int size;
extern "C" int* c_a;
extern "C" void hello();
int main(int args, char** argv)
{
size = 10;
c_a = new int[size];
for(int i=0; i<size; ++i) c_a[i] = i;
hello();
return 0;
}
fortran code:
MODULE MYMOD
USE, INTRINSIC :: ISO_C_BINDING
IMPLICIT NONE
INTEGER, BIND(C) :: SIZE
TYPE (C_PTR), BIND(C) :: C_A
INTEGER(C_INT), POINTER :: A(:)
SAVE
END MODULE
SUBROUTINE TEST(A)
IMPLICIT NONE
INTEGER A(*)
PRINT *,"A(1): ",A(1)
PRINT *,"A(2): ",A(2)
END
SUBROUTINE HELLO() BIND(C)
USE, INTRINSIC :: ISO_C_BINDING
USE MYMOD
IMPLICIT NONE
CALL C_F_POINTER(C_A,A,(/SIZE/))
PRINT *,"A(1): ",A(1)
PRINT *,"A(2): ",A(2)
CALL TEST(A)
END SUBROUTINE
Output:
A(1): 0
A(2): 1
A(1): 0
A(2): 1
Fortran array dummy arguments always start at the lower bound defined in the subroutine. Their lower bound is not retained during the call. Therefore the argument A in TEST() will always start at one. If you wish it to start from 42, you must do:
INTEGER A(42:*)
Regarding the allocation, you are playing with fire. It is much better to use Fortran pointers for this.
integer, pointer :: A(:)
You can then set the array to point to a C buffer by
use iso_c_binding
call c_f_pointer(c_ptr, a, [the dimensions of the array])
where c_ptr is of type(c_ptr), interoperable with void *, which also comes from iso_c_binding.
---Edit---
Once I see that #Max la Cour Christensen implemented what I sketched above, I see I misunderstood the output of your code. The descriptor was indeed wrong, though I didn't write anything plain wrong. The solution above still applies.
The internal representation of fortran arrays is very different than the one used in C/C++.
Fortran uses descriptors that start with a pointer to the array data, and followed by element type size, number of dimensions, some padding bytes, an internal 32/64 bit byte sequence indicating various flags such as pointer, target, allocatable, can be deallocated, etc. Most of these flags are not documented (at least in ifort that I have worked with), and at the end is a sequence of records, each describing the number of elements in the corresponding dimension, distance between elements, etc.
To 'see' an externally created array from fortran, you'd need to create such descriptors in C/C++, but, it does not end there because fortran also makes copies of them in the startup code of each subroutine before it gets to the first one of your statements, depending on indicators like 'in', 'out, 'inout', and other indicators used in the fortran array declaration.
Arrays within a type declared with specific sizes map well (again in ifort) to corresponding C struct members of the same type and number of elements, but pointer and allocatable type members are really descriptors in the type that need to be initialized to the correct values in all their fields so fortran can 'see' the allocatable value. This is at best tricky and dangerous, since the fortran compiler may generate copy code for arrays in undocumented ways for optimization purposes, but it needs to 'see' all the involved fortran code to do so. Anything coming outise of the fortran domain, is not known and can result in unexpected behavior.
Your best bet is to see if gfortran supports something like iso_c_binding and define such interfaces for your fortran code, and then use iso_c_binding intrinsics to map the C_PTR pointers to fortran pointers to types, arrays, etc.
You can also pass a pointer to a one-dimensional array of char, and its size, and this works for strings mostly as long as the size is passed by value as last argument (again, compiler and compiler-flag dependent).
Hope this helps.
EDIT: changed 'ifort's iso_c_binding' to 'iso_c_binding after Vladimir's comment - thanks!
I have a main program in Fortran. I am using Intel Visual Fortran XE 2011 on Visual Studio 2010. I would like to use a function which is coded in C++. The function I'm using is getting several arrays (input - set from the main fortran program) and use them to form an output array (to be returned to the main fortran program).
I've taken the following steps:
1)I created a Fortran project with the Fortran main program and module and I set it as "startup project".
2)I created a C++ project of type "static library".
3)I added $(IFORT_COMPILERvv)\compiler\lib\ia32 as explained here http://software.intel.com/en-us/articles/configuring-visual-studio-for-mixed-language-applications
The C++ static library is build with no problem.
The errors I get is about the declaration of the real(8) variables in the fortran program.
I get the following two errors for all real(8) declarations, i.e. 6 errors in total:
error #5082: Syntax error, found '(' when expecting one of: :: %FILL , TYPE BYTE CHARACTER CLASS DOUBLE DOUBLECOMPLEX DOUBLEPRECISION ...
error #5082: Syntax error, found '::' when expecting one of: ( * , ; [ / = =>
Here is the code I used:
Main Fortran Program:
Program Fort_call_C
use iso_c_binding
implicit none
interface
subroutine vec_sum_c(a,b,c) bind (C, name = "vec_sum_c")
use iso_c_binding
implicit none
real(8) (c_double), intent (in), dimension (*) :: a,b
real(8) (c_double), intent (out), dimension (*) :: c
end subroutine get_filled_ar
end interface
integer:: i
integer (c_int)::m
real(8)(c_double),dimension(:):: a, b, c
open(unit=10, file="input_arrays.txt",status="unknown")
read(10,*) m
allocate(a(m),b(m),c(m))
do i=1,m
read(10,*)a(i),b(i)
end do
close(10)
call vec_sum_c(m,a,b,c)
do i=1,m
print*, c(i)
end do
pause
end program
And the C++ function is:
extern"C" void vec_sum_c(int *m, double *a, double *b, double *c){
int mm = *m;
for(int i=0;i<=m-1;i++){
c[i]=a[i]+b[i];
}
}
Could anybody please help me with this issue?
And would you please let me know if the idea of sending a whole array from a fortran program to a c++ routine is a safe or problematic (better-to-be-avoided) attempt?
Your Fortran syntax is out. You have the real kind twice. Try
REAL(C_DOUBLE), INTENT(IN), DIMENSION(*) :: a, b
etc.
C_DOUBLE is a named constant. It happens to have the value 8 with that processor.
Also:
you are missing the argument m in the Fortran interface body for the C function.
you change your mind about the name of the subroutine in the Fortran interface body between the opening and closing statement!
Your C++ for loop less than equal compares against m, that should probably be mm.
There are no inherent problem sending whole arrays in this manner.
I had only managed to pass the value of a varible from C
function to fortran function.
I have here pasted the two source files namely main.c and fortran.f
You can use these two files in microsoft visual studio 10. After
doing all the settings in visual studio as suggested in page http://software.intel.com/en-us/articles/configuring-visual-studio-for-mixed-language-applications, you need to make another change as;
go to the project property of C/C++ static library ;
go to C/C++
go to Code Generation
set the Runtime Library to Multi-threaded Debug (/MTd)
now you can build the program....
main.c:
#include <stdio.h>
#include <malloc.h>
void testc(double **pa, double **p)
{
double b;
double *a, *c;
int m;
c = (double*) malloc(sizeof(double));
*c = 10;
*p = c;
a = (double*) malloc(sizeof(double)*5);
a[0]=1.23;
a[1]=2.46;
a[2]=3.69;
a[3]=4.11;
a[4]=7.21;
*pa=a;
for (m=0;m<5;m++)
{
b=a[m];
b=b+1.0;
a[m]=b;
}
}
fortran.f:
program test
use iso_c_binding
implicit none
interface
subroutine testc(pa, m) bind(c)
use iso_c_binding
type(c_ptr):: m
type(c_ptr):: pa
end subroutine testc
end interface
type(c_ptr) :: pa
type(c_ptr) :: m
real(c_double),pointer::fpa(:)
real(c_double),pointer::fm(:)
call testc(pa,m)
call c_f_pointer(pa, fpa, [5])
call c_f_pointer(m, fm, [1])
print *, fm(1)
print*, fpa(1)
pause
end program test
I am trying to call a C++ function in a Fortran subroutine. This C++ function is supposed to update an integer array. Here is a non-working code I wrote. What is the issue?
! Fortran function that calls a C++ function.
subroutine my_function()
integer(4) ar(*)
integer(4) get_filled_ar
! Need correct syntax here.
ar = get_filled_ar()
end
// C++ function:
extern "C" {
void get_filled_ar(int *ar){
ar[0] = 1;
ar[1] = 10;
ar[3] = 100;
}
}
With Fortran 2003 there is a standard and thus platform and compiler independent way to call C from Fortran, and also any language that uses the C calling interface. Also to call Fortran from C. While the various compiler writers are gradually adding Fortran 2003 features and there are few complete 2003 compilers, the ISO C Binding has been available for some time in many compilers. The ISO C Binding works better than the previous ad hoc techniques, which were sometimes poorly documented, and varied between compilers and platforms. To call C from Fortran, you write an "interface" that tells the Fortran compiler that it should use the C calling conventions, and C types.
Here is an example. As Mike wrote, since the C++ function returns void, treat it in the Fortran as a subroutine and call it. Thus it doesn't need to be given a type. Also, somewhere in the Fortran you have to reserve storage for the array -- the easiest way is with a declaration with numeric value for the dimension. And you need a main program in some language.
program test_call_C
use iso_c_binding
implicit none
interface c_interface
subroutine get_filled_ar (ar) bind (C, name = "get_filled_ar")
use iso_c_binding
implicit none
integer (c_int), intent (out), dimension (*) :: ar
end subroutine get_filled_ar
end interface c_interface
integer (c_int), dimension (0:3) :: ar
call get_filled_ar (ar)
write (*, *) "Fortran: ar:", ar
stop
end program test_call_C
and in C:
void get_filled_ar (
int ar []
) {
ar [0] = 1;
ar [1] = 10;
ar [2] = 100;
return;
}
Example commands:
gcc -c get_filled_ar.c
gfortran get_filled_ar.o test_call_C.f90 -o test_call_C.exe
./test_call_C.exe
To call your C++ code, use the following commands. The name specified in the "bind" obviates the need for a trailing underscore so your C++ code works directly.
g++ -c cplusplus.cc
gfortran cplusplus.o test_call_C.f90 -o test_call_Cplusplus.exe
./test_call_Cplusplus.exe
Here's a full program with your code in it that should work (at least on linux with gcc/gfortran).
In the fortran file fortran.f, put:
IMPLICIT NONE
CALL my_function()
END
! FORTRAN function that calls a C++ function
subroutine my_function()
! Tell fortran about the C++ function:
external get_filled_ar
integer ar(4)
! As the C++ function returns void, call it as a subroutine in
! fortran:
call get_filled_ar(ar)
! To check it worked, print out the numbers:
do i = 1,4
write(*,*) i,"->",ar(i)
enddo
end
In cplusplus.cc put:
extern "C"
{
void get_filled_ar_(int* ar) // note extra underscore to keep linker happy
{
ar[0] = 1;
ar[1] = 10;
ar[3] = 100;
}
}
Then build (on linux with gcc/gfortran, might be different on other systems) with:
gfortran -c fortran.f
gcc -c cplusplus.cc
gfortran -o program-name fortran.o cplusplus.o