Clicky

Fortran Wiki
Fortran and Cpp objects

HosseinTalebi:

Hello Everyone,

Here I would like to discuss on the question of “How to deal with C++ objects in Fortran?”

As there are many C++ libraries and codes out there and I’m using many, sometimes it is required to save the C++ object when exiting from the C++ code. In other words, I have a Fortran code which calls a C++ umbrella type code which creates a C++ object. After some manipulation on this C++ object, the C++ subroutine will return to Fortran caller. However, the C++ object is lost. Therefore, I need to find a way to create and keep the C++ object in the Fortran subroutine. Maybe this is possible as a pointer?

I would really appreciate if anyone has some information on this.

Thank you
Hossein

Jason Blevins (17 Aug 2010):

In Fortran 2003, using the iso_c_binding module, as long as the object still exists in memory, that is, as long as you don’t call free or delete, there should be no problem in passing a pointer back to Fortran. You can modify your routine to include an argument with type(c_ptr). On the C or C++ side, before the procedure returns, assign to this pointer the address of the object in question. Then it can be passed to other routines for further manipulation. Just make sure to eventually free the memory when it’s no longer needed, so you don’t have a memory leak. (By the way, I had to change the page name as, unfortunately, the wiki converts plus signs to spaces as is done with CGI parameters.)

Joe Krahn (18 Aug 2010): I have worked on generating Fortran interfaces to C++ procedures. An example of the end result is in the page Generating C Interfaces. I have not yet posted the underlying interface code. My goal is to auto-generate Fortran/C++ interfaces. I put it off for a while due to various F2003 compiler bugs, which has become less of a problem.

Instead of simply using F2003’s generic C pointer TYPE(C_PTR), C++ objects are defined as typed pointers, using a derived type as a container for the actual C++ object. The object pointer is then used as the first argument in all calls to C++ functions, which allows for unambiguous generic-procedure mapping to overloaded C++ functions. The only major exception is that Fortran does not allow subroutines and functions to share the same generic name; there is really no valid rationale for this design flaw.

My overall design is to present a user-friendly API to a C++ class object, which requires a Fortran wrapper to support generic mapping to overloaded C++ functions, and to automatically convert string arguments. There also must be a C-to-C++ wrapper for every function, because the C++ binary interface is platform-dependent.

The procedures NEW and DELETE are defined for each C++ object, which map to the class constructor and destructor procedures. The Fortran code must take care to call DELETE when a C++ object pointer will go out of scope or be otherwise lost.

With F2008, it will be possible to automatically call C++ destructors using a type-bound FINAL procedure.

Here is a complete example for a simple C++ class. First, the C++ class code, including C-accessible proxy/wrapper routines.

// crectangle_class.cc
#include <iostream>
using namespace std;
class CRectangle {
    int width, height;
  public:
    CRectangle (int,int);
    int area () {return (width*height);}
};
CRectangle::CRectangle (int a, int b) {
  width = a;
  height = b;
}
/* C wrapper interfaces to C++ routines */
extern "C" {
  CRectangle *CRectangle__new (int a, int b) {
    return new CRectangle(a, b);
  }
  int CRectangle__area (CRectangle *This) {
    return This->area();
  }
  void CRectangle__delete (CRectangle *This) {
    delete This;
  }
}
/* example main() written in C++:
int main () {
  CRectangle rect;
  rect.set_values (3,4);
  cout << "area: " << rect.area();
  return 0;
}
*/

Here is the Fortran source implementing a C++ class interface. The wrapper routines allow for using standard Fortran types, rather than complicating user code with ISO_C_BINDING and “integer(C_int)”.

// crectangle_module.f90
module CRectangle_module
  use, intrinsic :: ISO_C_Binding, only: C_int, C_ptr, C_NULL_ptr
  implicit none
  private
  type CRectangle_type
    private
    type(C_ptr) :: object = C_NULL_ptr
  end type CRectangle_type
  interface
    function C_CRectangle__new (a, b) result(this) bind(C,name="CRectangle__new")
      import
      type(C_ptr) :: this
      integer(C_int), value :: a, b
    end function C_CRectangle__new
    subroutine C_CRectangle__delete (this) bind(C,name="CRectangle__delete")
      import
      type(C_ptr), value :: this
    end subroutine C_CRectangle__delete
    function C_CRectangle__area (this) result(area) bind(C,name="CRectangle__area")
      import
      integer(C_int) :: area
      type(C_ptr), value :: this
    end function C_CRectangle__area
  end interface
  interface new
    module procedure CRectangle__new
  end interface new
  interface delete
    module procedure CRectangle__delete
  end interface delete
  interface area
    module procedure CRectangle__area
  end interface area
  public :: new, delete, area, CRectangle_type
contains
! Fortran wrapper routines to interface C wrappers
  subroutine CRectangle__new(this,a,b)
    type(CRectangle_type), intent(out) :: this
    integer :: a,b
    this%object = C_CRectangle__new(int(a,C_int),int(b,C_int))
  end subroutine CRectangle__new
  subroutine CRectangle__delete(this)
    type(CRectangle_type), intent(inout) :: this
    call C_CRectangle__delete(this%object)
    this%object = C_NULL_ptr
  end subroutine CRectangle__delete
  function CRectangle__area(this) result(area)
    type(CRectangle_type), intent(in) :: this
    integer :: area
    area = C_CRectangle__area(this%object)
  end function CRectangle__area
end module CRectangle_module
program main
  use CRectangle_module
  type(CRectangle_type) :: rect
  call new(rect,3,4)
  write(*,*) 'rect area: ',area(rect)
  call delete(rect)
end program main

Compiling:

# g++ -c crectangle_class.cc
# gfortran -c crectangle_module.f90
# gfortran -o crectangle crectangle_class.o crectangle_module.o -lstdc++

In general, it is linked with gfortran because the main program is in Fortran, but requires explicit linking to C++ libraries which g++ normally handles for you. The actual set of libraries depends on the compiler used.

I follow the convention that class and member names are joined by a double underscore to minimize possible name collisions with other C interfaces that might be exported by a C++ library. I also prefix all C procedure names with “C_” in the Fortran namespace. The Fortran code is a bit verbose, but that is a “feature” of modern Fortran programming. The end result is a very simple Fortran user API.