1

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.

gpavanb
  • 288
  • 2
  • 12
  • If you want `cpp_f` to be C interoperable by the current Fortran standard, the dummy arguments cannot be assumed shape. – francescalus Feb 07 '18 at 07:47
  • Yes, sorry about that. – gpavanb Feb 07 '18 at 07:56
  • Have you considered using `std::function` initialized by lambda e.g.: `std::function foo = [&current_object](double* len, double* x) { current_object.memberF(len, x); };` ? – W.F. Feb 07 '18 at 08:24
  • @francescalus do you have suggestions for how the length of arrays can be conveyed to the arguments in cpp_f? The prototype cannot be changed as it is part of the library. – gpavanb Feb 07 '18 at 09:04
  • @W.F. Are you sure this will not give a `std::function` object but a function pointer, given that there is a non-default capture – gpavanb Feb 07 '18 at 09:08
  • 1
    If that's an accurate interface for `cpp_f` then you can find examples of how such a function can be interoperable in [this question](https://stackoverflow.com/q/34561835/3157076). – francescalus Feb 07 '18 at 09:47

1 Answers1

2

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)
...
Olaf Dietsche
  • 72,253
  • 8
  • 102
  • 198
  • Thanks for the approach and I had considered it actually. Unfortunately, I cannot create a global `Obj` as it will need `foo`, which involves multiple objects to be initialized, which make up the bulk of the program. I've edited my question to clarify this. – gpavanb Feb 07 '18 at 08:00
  • 1
    Not a global object, but a global pointer to the currently used/active object. – Olaf Dietsche Feb 07 '18 at 08:01
  • Worth a shot. I will wait for other answers though, given the presence of a global variable. – gpavanb Feb 07 '18 at 08:06