Best way to call a C++ function that returns a double complex

This topic is on the same line as this one:

https://fortran-lang.discourse.group/t/issues-interfacing-between-c-and-fortran/

I have access to a C++ function that returns a complex class object and I want to call it from Fortran.

The problem is that, apparently, the C++ complex class is incompatible with the C double complex type defined in the header file complex.h.

Browsing the web, the solution I found was to create a couple of wrapper functions which employ a C - C++ compatible structure that stores the data. As a minimal working example I created 3 files:

part1.cpp:

#include <complex>
using namespace std;

// Define a C-compatible struct for complex numbers
struct C_ComplexDouble {
double real;
double imag;
};

complex<double> fccp (const complex<double> &a)
{
   return sqrt(a);
}

extern "C" C_ComplexDouble call_fccp(C_ComplexDouble a_c) {
   complex<double> a_cpp(a_c.real, a_c.imag);
   complex<double> result_cpp = fccp(a_cpp);
   C_ComplexDouble result_c = {result_cpp.real(), result_cpp.imag()};
   return result_c;
}


part2.c:

#include<complex.h>

// Define the C-compatible struct for complex numbers
typedef struct {
   double real;
   double imag;
} C_ComplexDouble;

extern C_ComplexDouble call_fccp(C_ComplexDouble a_c);

double complex fccp_ccpp(const double complex a)
{
   C_ComplexDouble a_c= {creal(a), cimag(a)};
   C_ComplexDouble res= call_fccp(a_c);
   return CMPLX(res.real, res.imag);
}

parts12.f90:

program parts12
use, intrinsic :: iso_c_binding, only: dpc => c_double_complex
implicit none
complex(dpc) :: a, res
INTERFACE
   complex(dpc) function fccp_ccpp(a) bind(c)
   import :: dpc
   complex(dpc), intent(in), value :: a
   end function fccp_ccpp
END INTERFACE

write(*, '(a)', advance= 'no')'a= ' ; read(*,*)a
res= fccp_ccpp(a)
print'(2(a,g0),a)', 'fccp(a)= (', res%re, ',', res%im, ')'
end program parts12

The compilation sequence is:

$> g++/icpx -c part1.cpp

$> gcc/icx -c part2.c

$> gfortran/ifx parts12.f90 part1.o part2.o

The executable successfully evaluates the square root of the complex number with both gfortran and ifx compilers.
Anyone has a better solution for this interoperability issue?

This looks reasonable. If you need to pass large arrays of COMPLEX data from C++ to Fortran, it may be more efficient to pass separate array of real data and separate array of imaginary data, rather than deal with converting each element individually to C Complex and then to Fortran COMPLEX.

If you have control over C++ implementation of function returning std::complex, maybe you could change it to work with complex type friendly to Fortran.

1 Like

Just out of curiosity, what is the incompatibility between C and C++? Is it just the name of the structure, or something more extensive?

The problem was that the original C++ function employs the std::complex class:

which is not the simpler C double complex type, whose real and imaginary parts are simply data components of the complex struct. It also contains member functions and methods for the operators.

If I simply declare the C++ function passing complex<double> as extern “C”, such as:

extern “C” complex<double> fccp (const complex<double> &a)
{
return sqrt(a);
}

Gnu’s C++ compiler (g++) will create the object without complains, but Intel’s icpx will issue the warning

warning: ‘fccp’ has C-linkage specified, but returns
user-defined type ‘complex’ which is incompatible with C [-Wreturn-type-c-linkage]
16 | extern “C” complex<double> fccp (const complex<double> &a)


It still creates the object, but some post on the internet warn against this procedure and recommend the creation of a wrapper function that passes a C-compatible structure, as in the example.

This is indeed a little complicated, but std::complex<double> is in fact binary compatible with C’s double _Complex (and Fortran’s complex), the trick is you need to explicitly cast the type to convince the compiler. You can get away without needing your own type at all.

Here’s the stripped down preamble from my nc-complex library:

// This is a workaround for MSVC's frustratingly incomplete support
// for complex numbers. When compiling with C++17, the complex.h
// header is broken unless the following escape hatch is used. See
// https://github.com/microsoft/STL/issues/3280 for more details

// NOLINTBEGIN(bugprone-reserved-identifier)
#ifdef _MSC_VER
#define _CRT_USE_C_COMPLEX_H
#endif
#include <complex.h>
#ifdef _MSC_VER
#undef _CRT_USE_C_COMPLEX_H
#endif
// NOLINTEND(bugprone-reserved-identifier)

#ifdef __cplusplus
#include <complex>
#endif

//@{
/// Portable typedefs for complex numbers
///
/// These become aliases for `std::complex` with C++.
#ifdef _MSC_VER
typedef _Dcomplex double_complex;
typedef _Fcomplex float_complex;
#else
#if defined(__cplusplus) && defined(__clang__)
using double_complex = std::complex<double>;
using float_complex = std::complex<float>;
#else
typedef double _Complex double_complex;
typedef float _Complex float_complex;
#endif
#endif
//@}

#ifdef __cplusplus
/// @name Helper functions
///@{
/// Helper functions for converting between (pointers to) C++ and C complex types
inline double_complex* cpp_to_c_complex(std::complex<double>* data) {
    return reinterpret_cast<double_complex*>(data);
}

inline std::complex<double>* c_to_cpp_complex(double_complex* data) {
    return reinterpret_cast<std::complex<double>*>(data);
}

inline const double_complex* cpp_to_c_complex(const std::complex<double>* data) {
    return reinterpret_cast<const double_complex*>(data);
}

inline const std::complex<double>* c_to_cpp_complex(const double_complex* data) {
    return reinterpret_cast<const std::complex<double>*>(data);
}
///@}
#endif

Now you can use these helper functions to wrap your C++ function:

extern "C" {
double_complex fcpp_from_c(const double_complex* a) {
    // Intermediate variable that we can take the address of to cast it
    const auto temp = fccp(*c_to_cpp_complex(a));
    return *cpp_to_c_complex(&temp);
}
}

Here’s a working Compiler Explorer link to demonstrate

2 Likes