All the code in this article is available as a gist, if you want to use it.
The number of available languages nowadays is very big, and growing every day.
As a library writer, I would like to make sure that all the cool code I write
today will still be runnable in 10 or 20 years, if it is still useful. Ideally,
this very good library (let’s call it libfoo
) would be language agnostic,
such that any programmer can use it, whatever his favorite language be.
Picking a language to write a library
In real world, I will always need to write some glue code between languages. In order to make it easier to write this glue code, I can use C to write my library. Any existing language does has a C interface, and can call C code in one way or another.
But I don’t really like C. I find that for my particular library, an object-oriented (OO) language will help me, and will allow me to be more efficient. So, I need an OO language with a very straightforward C interface. This interface will then be called by all the bindings. And here comes the C++ !
As usual, when writing a C++ library, we should start by writing down our main
interface specification, in an header file. This library will be the Foo
library,
built around the following Foo.hpp
file:
#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);
This is the most boring library ever, with only one class, two useless function
member and a global function. The library implementation can be found in the
corresponding Foo.cpp
file:
#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;
}
Creating the C API for our code
The good thing with C++, is its very straightforward C interface. All we have to
do is use an extern "C"
statement around the functions we need to wrap. So let’s
create a mixed C/C++ header containing the exported C interface to libfoo
.
We will wrap all the public functions, together with the constructor and the
destructor. The foo.h
file contains the C interface, and can be parsed by either
a C compiler or a C++ compiler.
#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
This C wrapper will be implemented in C++. All we need is to use a pointer to
our Foo
class, creating it dynamically with new
. So let’s put this code to
the foo_capi.cpp
file.
#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));
}
Wrapping the C API to Fortran
Why fortran ? Why this old and dying language ? You may ask. Well, three reasons here: first, Fortran is still very used in the high performance computing (HPC) world. You know, these guys running calculations for days on thousand of processor. And this is not a metaphor: super-computers frequently have at least 1024 processor per computing node. Second, the last standard is from 2008, and the previous one in 2003 introduced object-oriented programming. And third, I like this language, because it is as simple as C for simple tasks, but a lot more safer and easier to use.
I’ll split the fortran interface in two file, because wrapping C can become very
verbose. The first one is foo_cdef.f90
, and contains the interface declaration
for the external C function. This is the equivalent of the foo.h
file.
! 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
What is interesting here ? We use the iso_c_binding
module from fortran 2003,
and bind the function to their C name. Another point is the value
attribute
for all the parameter: in Fortran, all the arguments are passed by reference,
so we have to precise that we will use the pass-by-value convention.
And that’s all we need to have something functional ! But let’s make this
interface feels a bit more like fortran code. For this, we will use the
object-oriented (OO) capacities of the Fortran 2003 standard. Their is
something funny in wrapping C++ OO code to plain old C to build and OO module
in fortran … All this code goes to the foo_mod.f90
file:
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.
final :: delete_foo ! Destructor
procedure :: delete => delete_foo_polymorph ! Destructor for gfortran
! 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
This library can now be used in a fortran program! As you can see, the library interface is really easy to use, and looks a lot like the C++ interface.
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
Theoretically, the destructor should be automatically called when a variable goes out of scope, enabling the RAII idiom in Fortran, but the gfortran implementation (as of 4.9.2) is not complete for this point.
We can now compile the code, using this Makefile:
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
You should note that as gfortran is the program which will call the linker, we have to explicitly link to the C++ standard library. And now we can run our program!
$ make
$ ./test.x
C API, create_foo
C++ side, constructor
63 should be 63
14.000000000000000 should be 14.000000000000000
C++ side, constructor
From Fortran! Foo(4, 2).bar(3) is: 7
C++ side, destructor
C API, delete_foo
C++ side, destructor
So, what is missing for this interface? We should add a couple of try/cacth
statements in the CAPI implementation, and replace it by a status code, in order
to play nicely with the C++ exceptions. Another amelioration point is for the
foo_cdef.f90
and foo_mod.f90
files, which are rather repetitive, boring and
error prone to write. It should be possible to use a C parser to generate these files from the
foo.h
file. This would make another article!