Can Fortran module with operator overloading be used in C++?

Dear all,

I have a question. Simply speaking, I have a Fortran module, in it I define a type (dual number for example, for automatic differentiation), and create the computation rules among this type of variable. Of course, operators like ± * / , etc are all overloaded to support this type’s computations. Now, in a C++ code, can I still use this Fortran module?

I know I can define a similar struct in C++, and Fortran can recognize this struct as the type defined in the Fortran module. But, in the C++ code, can I use the overloaded operator (defined in this Fortran module) such as ± * / ? ChatGpt said yes we can.

Just curious, has anyone successfully did something like this? Many thanks!

PS. For example, the Fortran module I am talking about is the dnad module from joddlehod, link is below. I am just try to use it directly in a C++ code. If I cannot directly use it, looks like I need to write its C++ version for it to be used in C++ code.

A newer module by fpenunuri which can calculate higher order derivatives can be found here,
fpenunuri/DNAOAD: Fortran implementation of dual numbers for arbitrary order automatic differentiation

2 Likes

I don’t think you can use the overloaded operator from C++ without defining an interface or a way to actually use it (?)

The overloaded operator will be in the Fortran and what gets exposed to C/C++ is the C interface via the bind(C, name=“my_func_c_name”) and I don’t think you can expose to overloaded operator since it does not have a named interface, per se.

You would need to create a class that wraps the C interface exposed functionality and overloads the operators there to then call the C bound functions. Something like:

extern "C" {
  vec2_handle vec2_create(double x, double y);
  void        vec2_destroy(vec2_handle v);

  vec2_handle vec2_add(vec2_handle a, vec2_handle b);
  vec2_handle vec2_sub(vec2_handle a, vec2_handle b);
  vec2_handle vec2_scale(vec2_handle a, double s);

  double      vec2_dot(vec2_handle a, vec2_handle b);
  void        vec2_get(vec2_handle a, double* x, double* y);
}

from:


  public :: vec2
  public :: operator(+), operator(-), operator(*)


  public :: vec2_create
  public :: vec2_destroy
  public :: vec2_add
  public :: vec2_sub
  public :: vec2_scale
  public :: vec2_dot
  public :: vec2_get

for example:


  type :: vec2
     real(real64) :: x = 0.0_real64
     real(real64) :: y = 0.0_real64
  end type vec2

  interface operator(+)
     module procedure vec2_plus
  end interface

  interface operator(-)
     module procedure vec2_minus
  end interface

I know this is a bad example because we could do this with native arrays but this is easy. Now vec plus:

  pure function vec2_plus(a, b) result(c)
    type(vec2), intent(in) :: a, b
    type(vec2) :: c
    c%x = a%x + b%x
    c%y = a%y + b%y
  end function vec2_plus

and the C function:

  function vec2_add(a, b) result(out) bind(C, name="vec2_add")
    type(c_ptr), value :: a, b
    type(c_ptr) :: out
    type(vec2), pointer :: pa, pb, pc

    call unwrap_vec2(a, pa)
    call unwrap_vec2(b, pb)

    allocate(pc)
    pc = pa + pb          ! operator(+) used here (Fortran-side)
    out = c_loc(pc)
  end function vec2_add

which uses the Fortran overloaded operator. And you can do this (ish)?

  friend Vec2 operator+(const Vec2& a, const Vec2& b) {
    return Vec2(vec2_add(a.h_, b.h_));
  }

inside a Vec2 class that binds the overloaded+ to the Fortran function (???) you could probably write a script that generates this for your operators. But at this point, would you be better just porting the thing to C++? Not sure.

1 Like

The QD package from David Bailey does this, just in the other direction. The C++ operators are annotated with inline hints, and then wrapped into Fortran-compatible C functions:

#define TO_DOUBLE_PTR(a, ptr) ptr[0] = a.x[0]; ptr[1] = a.x[1];

extern "C" {

/* add */
void f_dd_add(const double *a, const double *b, double *c) {
  dd_real cc;
  cc = dd_real(a) + dd_real(b);
  TO_DOUBLE_PTR(cc, c);
}

// ...

}

You can find under high-precision software of Bailey here: Software Directory


You’ll probably want to avoid pointer on the result here, as it’s easy to create memory leaks. Instead a subroutine could be used, and the result passed by reference in C++:

extern "C" void f_vec2_add(const Vec2 *a, const Vec2 *b); // Fortran wrapper

inline Vec2 operator+(const Vec2 &a, const Vec2 &b) {
  Vec2 result;;

  f_vec2_add(a, b, result);

  return result;
}

Experimenting with link-time optimization may be a good idea, to avoid some of the penalty of many small function calls.

1 Like