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.