Passing an array from fortran to a C++ function - c++

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

Related

Binding C++ and Fortran

I want to combine C++ and Fortran together. My Fortran code will use a C++ function and C++ function changes variables of Fortran and sends them back. The C++ function is built with other C++ codes, e.g. the C++ function will use some sub-function in other .cpp file. I make the Fortran code with ifort and I added that C++ function as one object file, test.o in my Fortran makefile. I also put every needed C++ .o file(support test.o) in makefile. It shows the error
#6633, "The type of the actual argument differs from the type of the dummy argument".
Here is the code.
Fortran code
use, intrinsic :: ISO_C_BINDING, only: C_INT, C_DOUBLE
implicit double precision(a-h,o-z),integer(i-n)
Interface
integer (C_INT) function SolveBIE_(x, y, aa, m) BIND(C, NAME='SolveBIE_')
use, intrinsic :: ISO_C_BINDING
implicit none
type (C_PTR), value :: x
type (C_PTR), value :: y
type (C_PTR), value :: aa
integer (C_INT), value :: m
end function SolveBIE_
end Interface
integer (C_INT) :: m
real (C_DOUBLE), ALLOCATABLE, DIMENSION(:,:), target :: x
real (C_DOUBLE), ALLOCATABLE, DIMENSION(:,:), target :: y
real (C_DOUBLE), ALLOCATABLE, DIMENSION(:,:), target :: aa
ALLOCATE(x(0:MAXLEN,MAXINTERFACES))
ALLOCATE(y(0:MAXLEN,MAXINTERFACES))
ALLOCATE(aa(0:MAXLEN,MAXINTERFACES))
My Fortran code run
mm = SolveBIE_(x(1,1),y(1,1),aa(1,1),m)
Using the C++ code and where the error is from, on x, y, aa
I use x(1,1) instead of x, because if using x, then there is another error
#6634,"the shape matching rules of actual arguments and dummy arguments have been violated"`
I don't understand why it should be x(1,1). Why is this working, not x?
My C++ code
#ifdef __cplusplus
extern "C" {
#endif
int solveBIE_(double *ini_bdry_x, double *ini_bdry_y, double *ini_bdry_um, int *fM)
{
double(*bdry_node)[2] = new double[M1][2];
for (int k = 0; k < M; k++) {
bdry_node[k+1][0] = ini_bdry_x[k+1];
bdry_node[k+1][1] = ini_bdry_y[k+1];
bdry_theta[k+1] = Atan(ini_bdry_x[k+1], ini_bdry_y[k+1]);}
... some functions in other .cpp file
The way your interface is written, you have to construct a C_PTR to array x and pass that as the first argument:
use, intrinsic :: ISO_C_BINDING, only: C_INT, C_DOUBLE, C_PTR, C_LOC
! ...
type(C_PTR) PTRx
! ...
PTRx = C_LOC(x(LBOUND(x,1),LBOUND(x,2)))
! ...
mm = solveBIE_(PTRx, PTRy, PTRaa, m)
As shown above, you would have to fix the next two arguments as well. But you need to rewrite the interface for argument fM because as matters stand, Fortran will pass an integer by value whereas C++ is expecting a pointer. Given that, I would rewrite the interface completely, using the names given for the arguments in the C++ function and passing everything by reference. Names for dummy arguments are potentially visible in Fortran, so it's useful for them to be meaningful. In the following I assume that fM points to a scalar in the callee:
Interface
function SolveBIE_(ini_bdry_x, ini_bdry_y, ini_bdry_um, fM) &
BIND(C, NAME='SolveBIE_')
import
implicit none
integer(C_INT) SolveBIE_
real(C_DOUBLE) :: ini_bdry_x(*)
real(C_DOUBLE) :: ini_bdry_y(*)
real(C_DOUBLE) :: ini_bdry_um(*)
integer (C_INT) :: fM
end function SolveBIE_
end Interface
Then later on you can invoke it more or less normally as
mm = SolveBIE_(x,y,aa,m)
Note that x(1,1) was wrong because LBOUND(x,1) = 0, not 1!

Compiling fortran and C++ program and linking from intel compiler

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);

Pass arrays from C/C++ to Fortran and return a calculated array

I am trying to pass an array from C/C++ into a Fortran 2003 module and get the calculated values back into C/C++. I've been able to pass and return single values (scalars) just fine, but getting an array back and forth is proving difficult. I've found many threads on scalar values and I've been successful at making those work.
I've modeled my array based functions after my working scalar functions.
I am using gcc/gfortran.
Here's the Fortran module (ConvertUnitsLib.f03).
module ConvertUnitsLib
use :: iso_c_binding ! for C/C++ interop
real(c_double), bind(c) :: degF, degC
public DegCtoF
contains
!
! Convert temperature degrees Celsius Fahrenheit
!
real(kind = c_double) function DegCtoF(degC) result(degF) &
& bind(c, name = "DegCtoF")
real(c_double), intent(in), dimension(:) :: degC
real(c_double), dimension(size(degC)) :: degF
do i = 1, size(degC)
degF(i) = ( degC(i) * 1.8 ) + 32
end do
end function DegCtoF
! End of module
end module ConvertUnitsLib
And the C/C++, (CFort.cpp)
#include <stdio.h>
#ifdef __cplusplus
extern"C" {
#endif
double DegCtoF(double *[]);
#ifdef __cplusplus
}
#endif
/**********************************************************************/
int main(int argc, char *argv[])
{
printf("C/C++ and Fortran together!\n");
double DegreesC[2] = {32, 64};
double DegreesF[2];
DegreesF = DegCtoF(&DegreesC);
printf("%3.1f [C] = %3.1f [F]\n", DegreesC, DegreesF );
return 0;
}
And last but not least, the Makefile
# C++ directives
CC=g++
CFLAGS=-std=c++11
# Fortran directives
FC=gfortran
FFLAGS=-std=f2003
all: clean
$(FC) $(FFLAGS) -c -fcheck=all ConvertUnitsLib.f03
$(CC) $(CFLAGS) -c CFort.cpp
$(FC) $(FFLAGS) ConvertUnitsLib.o CFort.o -o convert
clean:
rm -f *.o
rm -f *.mod
Under the rules of current Fortran (Fortran 2008, but this is the same for when C interoperability was introduced in Fortran 2003), a Fortran procedure is not interoperable with C if it has an assumed shape dummy argument (other restrictions also apply). In your code degC, the dummy argument in the function DegCtoF, declared as
real(c_double), intent(in), dimension(:) :: degC
is such a thing.
So, under F2003 you cannot have such an interoperable function. Which is where things get tricky.
In the proposed draft for F2015 (based on the ISO TS29113 Further Interoperability of Fortran with C) such a thing is interoperable. And this syntax is (I think) supported by recent versions of gcc which is why the code is not rejected by gfortran.
(TS) Standardized interoperation with such a procedure with an assumed shape argument, however, requires using the C descriptor described in ISO_Fortran_binding.h on the C side which is not implemented in gcc. To do such interaction instead requires understanding the gcc array descriptor directly.
But you're in luck. In your case you don't really need to use an assumed shape dummy argument: you can use an explicit shape dummy argument and such interoperation is part of F2003. All you need to do is pass the size of the array.
Either way, an interoperable function must return a scalar result, so you'll also want to move to a subroutine, as given in the answer by innoSPG.
Finally, I'll mention your use of
real(c_double), bind(c) :: degF, degC
in the module.
These are interoperable global variables (through linkage association). You don't reference these variables in the Fortran code: the dummy and the function result are not these things.
In this simple case from the above, and the other answer, one will happily have a subroutine like
subroutine DegCtoF(n, degC, degF) bind(c,name='DegCtoF')
...
end subroutine
but this is perhaps a good opportunity to describe the use of the C descriptor from ISO_Fortran_binding.h. Note, though, that in the immediate term gfortran does not support this approach.
Consider the Fortran source
subroutine DegCtoF(degC, degF) bind(c,name='DegCtoF')
use, intrinsic :: iso_c_binding, only : c_double
implicit none
real(c_double), intent(in), dimension(:) :: degC
real(c_double), intent(out), dimension(*) :: degF
degF(1:SIZE(degC)) = degC*1.8+32
end subroutine DegCtoF
(for simplicity I'm going to assume that the memory management of degF is done all on the C side - naturally one could extend beyond the assumed size array). For this subroutine to be interoperable the argument corresponding to degC must be a pointer to CFI_cdesc_t.
Take the C code (with size magic numbers)
#include "ISO_Fortran_binding.h"
#include <stdio.h>
void DegCtoF(CFI_cdesc_t*, double*);
int main(int argc, char *argv[])
{
printf("C and Fortran together!\n");
CFI_CDESC_T(1) DegreesC_Fdesc;
CFI_index_t extent[1] = {2};
CFI_rank_t rank = 1;
double DegreesC[2] = {32, 64};
double DegreesF[2];
CFI_establish((CFI_cdesc_t*)&DegreesC_Fdesc, &DegreesC, CFI_attribute_other,
CFI_type_double, 2*sizeof(double), rank, extent);
DegCtoF((CFI_cdesc_t*)&DegreesC_Fdesc, DegreesF);
printf("%3.1f [C] = %3.1f [F]\n", DegreesC[0], DegreesF[0] );
printf("%3.1f [C] = %3.1f [F]\n", DegreesC[1], DegreesF[1] );
return 0;
}
Here CFI_establish establishes a suitable C descriptor DegreesC_Fdesc which can correspond to the assumed shape Fortran dummy argument. Inside the Fortran subroutine there is no problem at all assessing the size of the incoming array.
Before francescalus confirms it, I was going to say that from what I know that was a little bit old, the interoperability does not permit what you are trying to do with arrays.
In addition, some good habits are always critical when coding. For example using implicit none in fortran to force the declaration of all variables before they are used. The use of named constant when the language permits it, for example the 2 that you are using as array size in fortran.
Below is a modified version of your code that should do something like what you want to achieve.
//Fortran
module ConvertUnitsLib
use :: iso_c_binding ! for C/C++ interop
!real(c_double), bind(c) :: degF, degC
implicit none
public DegCtoF
contains
!
! Convert temperature degrees Celsius Fahrenheit
!
subroutine DegCtoF(degC, degF, n)&
bind(c, name = "DegCtoF")
integer, intent(in) :: n
real(c_double), intent(in), dimension(n) :: degC
real(c_double), intent(out), dimension(n) :: degF
integer :: i
do i = 1, n
degF(i) = ( degC(i) * 1.8 ) + 32
end do
end subroutine DegCtoF
// C++
#include <stdio.h>
#ifdef __cplusplus
extern"C" {
#endif
double DegCtoF(double [], double [], const int *);
#ifdef __cplusplus
}
#endif
/**********************************************************************/
int main(int argc, char *argv[])
{
const int N = 2;
printf("C/C++ and Fortran together!\n");
double DegreesC[N] = {32, 64};
double DegreesF[N];
DegCtoF(DegreesC, DegreesF, &N);
for(int i = 0; i<N; i++){
printf("%d : %3.1f [C] = %3.1f [F]\n", i, DegreesC[i], DegreesF[i] );
}
return 0;
}

the run time aborting when calling c++ sub from fortran

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;
}

fortran dummy argument doesn't match actual argument. casting available?

I've recently inherited Fortran code that used to be built with an older version of the Intel Visual Fortran compiler. There's a section of code that used to compile, but now throws an error #6633 'The type of the actual argument differs from the type of the dummy argument.'
The problem is when a function called READ_AND_CONVERT is called with REAL*4 DATA_ARRAY(*), but in READ_AND_CONVERT that parameter is declared as INT*2. I think it really just wants the address of the DATA_ARRAY.
Is there a way to pass the address of the DATA_ARRAY, even though they're of different types?
Here is READ_AND_CONVERT:
SUBROUTINE READ_AND_CONVERT (MX, N)
C=======================================================================
C Reads Integer*2 Data Array and Converts it to Real*4.
C
C This is a service routine called by subroutines
C READ_XYZ_2, READ_XYZ_4, READ_XYZ_ALL and READ_XYZ_FULL
C=======================================================================
C
IMPLICIT NONE
C
INCLUDE 'XYZ.FOR'
INCLUDE 'COMMON_XYZIO.FOR'
INCLUDE 'COMMON_HDR.FOR'
C
C-----------------------------------------------------------------------
C Local Parameters
C-----------------------------------------------------------------------
C
LOGICAL BB_FOUND
INTEGER*2 MX, MY
INTEGER*4 N, J
REAL*4 YJ, BB
C
DIMENSION MX(*), MY(2)
EQUIVALENCE (YJ, MY(1))
C
C-----------------------------------------------------------------------
C
CALL GET_REAL_PARAMETER ('XYZ$_OFFSET', BB, BB_FOUND)
C
READ (LUGIN) (MX(J), J = 1,N)
C
IF (BB_FOUND) THEN
DO J = N, 1, -1
YJ = (SCALE_FACTOR * MX(J)) + BB
MX(2*J) = MY(2)
MX(2*J-1) = MY(1)
END DO
ELSE
DO J = N, 1, -1
YJ = SCALE_FACTOR * MX(J)
MX(2*J) = MY(2)
MX(2*J-1) = MY(1)
END DO
END IF
C
RETURN
END
Found a solution here:
Basically disable the warning... by setting Properties | Fortran | Diagnostics | Check Routine Interfaces [change from Yes to No]
The article also shows how to do casting, in their example of a complex array to a real array:
use ISO_C_BINDING
complex(8), allocatable :: c(:)
real(8), pointer:: p(:)
allocate(c(N))
call C_F_POINTER(C_LOC(c), p, [2*N])
call donothing(N, p)
There are directives in Intel Fortran which disable the argument type check for a given routine and for a given argument. To disable the checks for all your code is dangerous!
!DEC$ ATTRIBUTES NO_ARG_CHECK :: ARGUMENT_NAME
source: https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/288896