gpavanb
gpavanb

Reputation: 262

Pass pointer-to-member-function for class without copy constructor into Fortran

I have a call to a Fortran function, the first argument of which is a function, for which I would like to use a C++ member-function of the form,

void Obj::memberF(double* x,double* res)

with Obj having the copy-constructor disabled (and this becomes important as shown below).

The Fortran subroutine is of the form

subroutine solve(f,len,x)

with f taking two arguments, x and rhs, both of which are real arrays. len stands for the length of these arrays.

(Essentially, need to call the residue function in a non-linear solver library)

For this purpose, irrespective of the eventual C++ approach, we will need a binder and is as follows

binder.f90

SUBROUTINE cpp_solve(cpp_f,len,x) BIND(C, NAME='cpp_solve')
  USE, INTRINSIC :: iso_c_binding, ONLY : c_int, c_float
  INTERFACE
    SUBROUTINE cpp_f(x,rhs) BIND(C)
      USE, INTRINSIC :: iso_c_binding, ONLY : c_float
      REAL(c_float) :: x(:)
      REAL(c_float) :: rhs(:)
    END SUBROUTINE cpp_f
  END INTERFACE
  INTEGER(c_int) :: len
  REAL(c_float) :: x(len)
  CALL solve(cpp_f,len,x)
END SUBROUTINE cpp_solve

For the C++ program, first, a wrapper is needed for these functions

wrap.h

extern "C" {
  void cpp_f(double*,double*);
  void cpp_solve(void (Obj::*cpp_f)(double*,double*),int*,double*);
}

The main program is as follows

main.cpp

#include "wrap.h"

int main {
  int len = 2;
  std::vector<double> x(len);
  // Multiple lines to initialize foo
  // Creating a global Obj is not feasible
  // ...
  Obj current_object(foo);

  // WHAT GOES HERE? 

  // cpp_solve(...,len,x);
}  

Below are a few approaches I've considered, starting with recent C++ features,

1) Note that the copy-constructor of Obj has been disabled for other reasons and is the constraint. This prevents me from using std::bind to attach the current instance and obtain a function pointer.

2) Also, defining another function extra_function(Obj& obj, double*,...) and then using a lambda to just return obj.memberF is not an option either, as that would require me to specify a capture, given the custom object and I need a function pointer only.

3) A simpler option would be to just obtain the member function pointer and then, pass the current instance as follows

typedef void (Obj::*fptr)(double*,double*);

fptr current_fPointer = &Obj::memberF;
cpp_solve(current_object.*current_fPointer,len,x);

This gives me the 'invalid use of non-static member function' error.

Is there an alternative approach I could use to obtain the function pointer?

TBH, I'm actually trying to call a Fortran 77 routine using these C++11 features, so that's some retrofuturism.

Upvotes: 1

Views: 301

Answers (1)

Olaf Dietsche
Olaf Dietsche

Reputation: 74088

If this is a single threaded program, a pragmatic approach could use a global pointer variable with an extra function

static Obj *g_obj;

static void extra_func(double *x1, double *x2)
{
    g_obj->memberF(x1, x2);
}

void call_f77(Obj *o1, int *len, double *x)
{
    g_obj = o1;
    cpp_solve(extra_func, len, x);
}

Another approach, if you can modify the Fortran code (and Fortran transparently can pass object pointers somehow), you may tunnel the relevant object through Fortran to the extra_func, e.g.

static void extra_func(void *p, double *x1, double *x2)
{
    Obj *obj = static_cast<Obj*>(p);
    obj->memberF(x1, x2);
}

void call_f77(Obj *o1, int *len, double *x)
{
    cpp_solve(extra_func, o1, len, x);
}

and in Fortran (please forgive me, I've never seen any Fortran code):

SUBROUTINE solve(cpp_f, obj, len, x)
...
CALL cpp_f(obj, x, rhs)
...

Upvotes: 2

Related Questions