I have a bunch of old F77 source code (usually compiled on a x86_64
with gfortran -std=legacy
).
It contains quite a few functions in form:
double complex function f(x, y, i)
double precision x, y
integer i
f = cmplx(x, y) * i
return
end
I need to call these functions from some C++ code (usually compiled on a x86_64
with g++
).
It works with the default Fortran
KIND=8
:extern "C" { std::complex<double> f_(double *x, double *y, int *i); }
It works when I enforce the default Fortran
KIND=4
using the-freal-8-real-4
option:extern "C" { std::complex<float> f_(float *x, float *y, int *i); }
It works when I enforce the default Fortran
KIND=16
using the-freal-8-real-16
option (and in C++#include <quadmath.h>
):extern "C" { __complex128 f_(__float128 *x, __float128 *y, int *i); }
To my surprise, in this case, it also seems to work with (the returned value is in
*z
):extern "C" { void f_(__complex128 *z, __float128 *x, __float128 *y, int *i); }
Which of these two above prototypes is the (more?) proper one?
My problem is that I cannot get it working with my desired default Fortran
KIND=10
using the-freal-8-real-10
option. Inside of Fortran, thekind
,precision
,range
andsizeof
return values which directly correspond to the C++long double
. So, I tried:extern "C" { std::complex<long double> f_(long double *x, long double *y, int *i); } extern "C" { void f_(std::complex<long double> *z, long double *x, long double *y, int *i); } extern "C" { void f_(long double *x, long double *y, int *i, std::complex<long double> *z); }
But I cannot get it working at all.
Maybe I need to add some special flags to
gfortran
and / org++
calls in order to let C++ retrieve FortranKIND=10
complex values? Note: I don't think I can use-ff2c
.
Update (2020.08.04): I have been able to fool the C++ compiler so that it seems to generate proper code for any Fortran KIND=4,8,10
. The trick is to use the ISO C99 _Complex
in C++ (note: this trick is required only for KIND=10
, but it actually works for KIND=4,8
, too):
#include <complex.h>
#define C99KIND long double /* it can be "float", "double" or "long double" */
extern "C" { C99KIND _Complex f_(C99KIND *x, C99KIND *y, int *i); }
Note that, in C++, you cannot use e.g. long double complex
but fortunately long double _Complex
is still fine.
The usability of the ISO C99 _Complex
in C++ is rather limited. For example, with -std=c++11
(or newer) even the most basic creal*
and cimag*
functions disappear.
So, the best idea is to immediately copy the returned value into some standard C++ templated complex variable, e.g using something like (note: f_
returns C99KIND _Complex
):
std::complex<C99KIND> z = f_(&x, &y, &i);