FortranからC++のコードを呼び出す

最近のFortranではオブジェクト指向プログラミングができますが（[参考URL](https://qiita.com/cometscome_phys/items/f87080286c6fdf72e49a)）、これを用いるとC++のクラスとのやりとりができます。
こちらの[URL](https://gist.github.com/Luthaf/4df78ca52b3caf7fbe0e) を参考にしました。コンパイラはgfortranとg++を使っています。

まだ調査中ですが、この方法を使えば、c++で書かれたライブラリをFortranから呼ぶことが簡単にできるようになるはずです。

# Fortranコード
以下のFortranコードを見てください。

```fortran:main.f90
program main
    use libtest
    implicit none
    type(system_class) :: f
    character(len=100) :: inputfile
    inputfile = "test.txt"

    f = system_class(trim(inputfile))
    write(*,*) "hello world in fortran90!"

    call f%print_inputfile()
end program
```
これを見ると、Fortranのクラス（構造体）fを作って、コンストラクタを通じてinputfileというファイル名を保存しておき、```f%print_inputfile()```でそのファイル名を表示する形に見えます。
実行結果は
```
./test.x
C API, create_system
C++ side, constructor
 hello world in fortran90!
Input file is test.txt
```
となります。実は、このクラスの具体的な中身はc++で書かれています。

# Fortranモジュール
まず、モジュールは

```fortran:mod_libtest.f90
module libtest
    use iso_c_binding
    private
    public::system_class

    include "libtest_cdef.f90"

    ! We'll use a Fortan type to represent a C++ class here, in an opaque maner
    type system_class
        private
        type(c_ptr) :: ptr ! pointer to the System_class class
    contains
        ! We can bind some functions to this type, allowing for a cleaner syntax.
#ifdef __GNUC__
        procedure :: delete => delete_system_polymorph ! Destructor for gfortran
#else
        final :: delete_system ! Destructor
#endif

        ! Function member
        procedure :: print_inputfile => system_print_inputfile
    end type

    ! This function will act as the constructor for foo type
    interface system_class
        procedure create_system
    end interface

contains ! Implementation of the functions. We just wrap the C function here.
    function create_system(filename)
        implicit none
        type(system_class) :: create_system
        character(len=*),intent(in)::filename
        create_system%ptr = create_system_c(filename)
    end function

    subroutine delete_system(this)
        implicit none
        type(system_class) :: this
        call delete_system_c(this%ptr)
    end subroutine

    ! Bounds procedure needs to take a polymorphic (class) argument
    subroutine delete_system_polymorph(this)
        implicit none
        class(system_class) :: this
        call delete_system_c(this%ptr)
    end subroutine


    subroutine  system_print_inputfile(this)
        implicit none
        class(system_class), intent(in) :: this
        call system_print_inputfile_c(this%ptr)
    end subroutine
end module libtest
```
です。コメントは元のURLに書かれたものそのままになっています。ポイントは

```fortran
    type system_class
        private
        type(c_ptr) :: ptr ! pointer to the Sytem_class class
    contains
        ! We can bind some functions to this type, allowing for a cleaner syntax.
#ifdef __GNUC__
        procedure :: delete => delete_system_polymorph ! Destructor for gfortran
#else
        final :: delete_system ! Destructor
#endif

        ! Function member
        procedure :: print_inputfile => system_print_inputfile
    end type
```
の部分でして、```system_class```という構造体にはメソッド``` print_inputfile```がついていますが、```inputfile```を格納するような文字列変数は全く定義されていません。文字列はc++側のクラスに定義されています。

includeしているのは
```fortran:libtest_cdef.f90
! C functions declaration
interface
function create_system_c(filename) bind(C, name="create_system")
    use iso_c_binding
    implicit none
    type(c_ptr) :: create_system_c
    character(len=1, kind=C_CHAR), intent(in) :: filename(*)
end function

subroutine delete_system_c(system) bind(C, name="delete_system")
    use iso_c_binding
    implicit none
    type(c_ptr), value :: system
end subroutine

subroutine system_print_inputfile_c(system) bind(C, name="print_inputfile")
    use iso_c_binding
    implicit none
    type(c_ptr), value :: system
end subroutine
end interface 
```
です。これは、c言語の関数をFortranから呼ぶために引数の情報を記述しています。

# C++のクラス
呼び出すC++のクラスは
```c++:System_class.cpp
#include "System_class.hpp"
#include <iostream>
using namespace std;


System_class::System_class(const char* s) {
    cout << "C++ side, constructor" << endl;
    filename = string(s);
}

System_class::~System_class(){
    cout << "C++ side, destructor" << endl;
}

void System_class::print_inputfile() const{
    cout << "Input file is " << this -> filename << endl;
}

```

```c++:System_class.hpp
#include <string>
class System_class {
public:
    System_class(const char* s);
    ~System_class();

    void print_inputfile() const;
private:
    std::string filename;
};

```
です。こちらに```filename```というstring型の変数が格納されています。あとは、Fortranからこの値を代入できれば良いわけです。

# C言語
上のFortranとc++を繋げるためには、C言語のコードを書く必要があります。直接Fortranとc++がやり取りするわけではなく、より扱いやすいc言語を挟みます。c言語のコードですが、コンパイラはc++を使います。

```c:system_capi.cpp
#include "system_class.h"
#include "System_class.hpp"

#include <iostream>
using namespace std;

SYSTEM_class* create_system(const char* s){
    cout << "C API, create_system" << endl;
    return new SYSTEM_class(s);
}

void delete_system(SYSTEM_class* system){
    cout << "C API, delete_system" << endl;
    delete system;
}

void print_inputfile(const SYSTEM_class* system){
    system->print_inputfile();
}
```
と
```c:system_class.h
#ifdef __cplusplus // Are we compiling this with a C++ compiler ?
extern "C" {
    class System_class;
    typedef System_class SYSTEM_class;
#else
    // From the C side, we use an opaque pointer.
    typedef struct SYSTEM_class SYSTEM_class;
#endif

// Constructor
SYSTEM_class* create_system(const char* s);

// Destructor
void delete_system(SYSTEM_class* foo);

void print_inputfile(const SYSTEM_class* foo);

#ifdef __cplusplus
}
#endif
```
を用意します。このコードでは、c++のメソッドではなく、通常の関数として定義されています。この関数をFortranから呼んでいるわけです。

# Makefile
以上のコードを

```makefile: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

OBJS = mod_libtest.o system_capi.o System_class.o #foo_mod.o foo_capi.o Foo.o

all: test.x
test.o : mod_libtest.o #foo_mod.o

%.x : %.o ${OBJS}
	${FC} $^ -o $@ ${LDFLAGS}

%.o : %.f90
	${FC} ${FCFLAGS} -c $< -o $@

%.o : %.cpp
	${CXX} ${CCFLAGS} -c $^ -o $@

.PHONY : clean

clean :
	${RM} -rf *.o *.mod test.x
```
というmakefileでコンパイルすれば、
```
./test.x
```
で実行できるわけです。

# 追記
コメントで@cure_honeyさんに指摘していただいたように、このままだとデストラクタが働いていないですね。よくわかりませんが、以下のように
```fortran
subroutine test()
    use libtest
    implicit none
    type(system_class) :: f
    character(len=100) :: inputfile
    inputfile = "test.txt"

    ! Create an object of type foo
    f = system_class(trim(inputfile))
    write(*,*) "hello world in fortran90!"

    call f%print_inputfile()
end subroutine 


program main
    call test()
end program
```
subroutine内で実行すると、
```
./test.x 
C API, create_system
C++ side, constructor
 hello world in fortran90!
Input file is test.txt
C API, delete_system
C++ side, destructor
```
となってデストラクタが呼ばれるようです。