Instantly share code, notes, and snippets.

@Luthaf /Foo.cpp
Last active Sep 20, 2018

Embed
What would you like to do?
Calling C++ from Fortran
#include "Foo.hpp"
#include <iostream>
using namespace std;
Foo::Foo(int _a, int _b): a(_a), b(_b){
cout << "C++ side, constructor" << endl;
}
Foo::~Foo(){
cout << "C++ side, destructor" << endl;
}
int Foo::bar(int c) const{
return a + c;
}
double Foo::baz(double d) const{
return d + b;
}
void foo_speaker(string s){
Foo f(4, 2);
cout << s << " Foo(4, 2).bar(3) is: " << f.bar(3) << endl;
}
#ifdef __cplusplus // Are we compiling this with a C++ compiler ?
extern "C" {
class Foo;
typedef Foo FOO;
#else
// From the C side, we use an opaque pointer.
typedef struct FOO FOO;
#endif
// Constructor
FOO* create_foo(int a, int b);
// Destructor
void delete_foo(FOO* foo);
// The const qualificators maps from the member function to pointers to the
// class instances.
int foo_bar(const FOO* foo, int c);
double foo_baz(const FOO* foo, double d);
void foo_speaker(const char* s);
#ifdef __cplusplus
}
#endif
#include <string>
class Foo {
public:
Foo(int a, int b);
~Foo();
int bar(int c) const;
double baz(double d) const;
private:
int a;
int b;
};
void foo_speaker(std::string s);
#include "foo.h"
#include "Foo.hpp"
#include <iostream>
using namespace std;
FOO* create_foo(int a, int b){
cout << "C API, create_foo" << endl;
return new Foo(a, b);
}
void delete_foo(FOO* foo){
cout << "C API, delete_foo" << endl;
delete foo;
}
int foo_bar(const FOO* foo, int c){
return foo->bar(c);
}
double foo_baz(const FOO* foo, double d){
return foo->baz(d);
}
void foo_speaker(const char* s) {
foo_speaker(string(s));
}
! C functions declaration
interface
function create_foo_c(a, b) bind(C, name="create_foo")
use iso_c_binding
implicit none
type(c_ptr) :: create_foo_c
integer(c_int), value :: a
integer(c_int), value :: b
end function
subroutine delete_foo_c(foo) bind(C, name="delete_foo")
use iso_c_binding
implicit none
type(c_ptr), value :: foo
end subroutine
function foo_bar_c(foo, c) bind(C, name="foo_bar")
use iso_c_binding
implicit none
integer(c_int) :: foo_bar_c
! The const qualification is translated into an intent(in)
type(c_ptr), intent(in), value :: foo
integer(c_int), value :: c
end function
function foo_baz_c(foo, c) bind(C, name="foo_baz")
use iso_c_binding
implicit none
real(c_double) :: foo_baz_c
type(c_ptr), intent(in), value :: foo
real(c_double), value :: c
end function
! void functions maps to subroutines
subroutine foo_speaker_c(str) bind(C, name="foo_speaker")
use iso_c_binding
implicit none
character(len=1, kind=C_CHAR), intent(in) :: str(*)
end subroutine
end interface
module libfoo
use iso_c_binding
private
public :: foo, foo_speaker
! Yes, include is a keyword in Fortran !
include "foo_cdef.f90"
! We'll use a Fortan type to represent a C++ class here, in an opaque maner
type foo
private
type(c_ptr) :: ptr ! pointer to the Foo class
contains
! We can bind some functions to this type, allowing for a cleaner syntax.
#ifdef __GNUC__
procedure :: delete => delete_foo_polymorph ! Destructor for gfortran
#else
final :: delete_foo ! Destructor
#endif
! Function member
procedure :: bar => foo_bar
procedure :: baz => foo_baz
end type
! This function will act as the constructor for foo type
interface foo
procedure create_foo
end interface
contains ! Implementation of the functions. We just wrap the C function here.
function create_foo(a, b)
implicit none
type(foo) :: create_foo
integer, intent(in) :: a, b
create_foo%ptr = create_foo_c(a, b)
end function
subroutine delete_foo(this)
implicit none
type(foo) :: this
call delete_foo_c(this%ptr)
end subroutine
! Bounds procedure needs to take a polymorphic (class) argument
subroutine delete_foo_polymorph(this)
implicit none
class(foo) :: this
call delete_foo_c(this%ptr)
end subroutine
integer function foo_bar(this, c)
implicit none
class(foo), intent(in) :: this
integer, intent(in) :: c
foo_bar = foo_bar_c(this%ptr, c)
end function
double precision function foo_baz(this, c)
implicit none
class(foo), intent(in) :: this
double precision, intent(in) :: c
foo_baz = foo_baz_c(this%ptr, c)
end function
subroutine foo_speaker(str)
implicit none
character(len=*), intent(in) :: str
character(len=1, kind=C_CHAR) :: c_str(len_trim(str) + 1)
integer :: N, i
! Converting Fortran string to C string
N = len_trim(str)
do i = 1, N
c_str(i) = str(i:i)
end do
c_str(N + 1) = C_NULL_CHAR
call foo_speaker_c(c_str)
end subroutine
end module
FC = gfortran
CXX = g++
UNAME := $(shell uname -s)
FCFLAGS = -Wall -Wextra
CCFLAGS = -Wall -Wextra
ifeq ($(UNAME_S),Darwin)
LDFLAGS = -lstdc++
else
LDFLAGS = -lc++
endif
all: test.x
test.o : foo_mod.o
%.x : %.o foo_mod.o foo_capi.o Foo.o
${FC} $^ -o $@ ${LDFLAGS}
%.o : %.f90
${FC} ${FCFLAGS} -c $< -o $@
%.o : %.cpp
${CXX} ${CCFLAGS} -c $^ -o $@
.PHONY : clean
clean :
${RM} -rf *.o *.mod test.x
program test
use libfoo
implicit none
type(foo) :: f
! Create an object of type foo
f = foo(3, 4)
! Call bound procedures (member functions)
write(*,*) f%bar(60), " should be ", 63
write(*,*) f%baz(10d0), " should be ", 14.0d0
call foo_speaker("From Fortran!")
! The destructor should be called automatically here, but this is not yet
! implemented in gfortran. So let's do it manually.
#ifdef __GNUC__
call f%delete
#endif
end program
@baptistelabat

This comment has been minimized.

Show comment
Hide comment
@baptistelabat

baptistelabat Jun 15, 2016

Thanks for sharing this code.
I have this issue on windows when trying to compile.

gfortran -Wall -Wextra -c foo_mod.f90 -o foo_mod.o
foo_mod.f90:11.12:

type foo
        1
Error: Finalization at (1) is not yet implemented

This seems to be related to the version of GCC/gfortran in last mingw (version 4.8)
Any idea of workaround?

baptistelabat commented Jun 15, 2016

Thanks for sharing this code.
I have this issue on windows when trying to compile.

gfortran -Wall -Wextra -c foo_mod.f90 -o foo_mod.o
foo_mod.f90:11.12:

type foo
        1
Error: Finalization at (1) is not yet implemented

This seems to be related to the version of GCC/gfortran in last mingw (version 4.8)
Any idea of workaround?

@Luthaf

This comment has been minimized.

Show comment
Hide comment
@Luthaf

Luthaf Sep 23, 2016

Hi! Sorry for the late answer, I did not get any notification from your comment ...

Yes, the issue here is that your version of gfortran does not support yet finalizers and object-oriented pattern. I got around this in my code by explicitly adding a foo_free function, calling delete from C++, and then calling this function by hand in Fortran.

You may want to remove (or ifdef) the final procedure in the foo type to get this to compile:

   ! We'll use a Fortan type to represent a C++ class here, in an opaque maner
    type foo
        private
        type(c_ptr) :: ptr ! pointer to the Foo class
    contains
        ! We can bind some functions to this type, allowing for a cleaner syntax.
#ifdef __GNUC__
        procedure :: delete => delete_foo_polymorph ! Destructor for gfortran
#else
        final :: delete_foo ! Destructor
#endif
        ! Function member
        procedure :: bar => foo_bar
        procedure :: baz => foo_baz
    end type

I will edit these files with this workaround!

Owner

Luthaf commented Sep 23, 2016

Hi! Sorry for the late answer, I did not get any notification from your comment ...

Yes, the issue here is that your version of gfortran does not support yet finalizers and object-oriented pattern. I got around this in my code by explicitly adding a foo_free function, calling delete from C++, and then calling this function by hand in Fortran.

You may want to remove (or ifdef) the final procedure in the foo type to get this to compile:

   ! We'll use a Fortan type to represent a C++ class here, in an opaque maner
    type foo
        private
        type(c_ptr) :: ptr ! pointer to the Foo class
    contains
        ! We can bind some functions to this type, allowing for a cleaner syntax.
#ifdef __GNUC__
        procedure :: delete => delete_foo_polymorph ! Destructor for gfortran
#else
        final :: delete_foo ! Destructor
#endif
        ! Function member
        procedure :: bar => foo_bar
        procedure :: baz => foo_baz
    end type

I will edit these files with this workaround!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment