RokkoFortranBindingTips - t-sakashita/rokko GitHub Wiki
Fortranバインディング開発のメモ
offset(添字が0か1から始まるか)の問題
- 内部にoffset_変数を持たせる必要はあるか?
Fortranの文字列
- Cのように、一文字(char)か2文字(char*)以上かという区別はない。
- Fortran2003からサポートされているdynamic allocatedを使って、文字列を宣言する。
- これを使うと、trimをしなくても済む。
- 文字列変数の使い回しも容易?
character(len=:), allocatable :: str
- Intelコンパイラでは特別な処理をしているらしい。http://wwweic.eri.u-tokyo.ac.jp/computer/manual/altix/compile/Fortran/Intel_Fdoc91/main_for/mergedProjects/bldaps_for/pgwchars.htm
通常のFortran文字列をC言語に渡す場合
trim
した後にc_null_charを追加する必要がある。
character(*), intent(in) :: key
character(*), intent(in) :: val
call rokko_parameters_set_string_c (params, trim(key)//c_null_char, trim(val)//c_null_char)
Fortranの関数の引数
- 入力変数:valueを付けて渡す
- 出力変数:valueを付けることはできない。
CとFortran間の文字列の関数の引数
- 入力変数:ポインタ。Fortran側ではvalueを付ける。
- 出力変数:2重ポインタにする。2重ポインタも
iso_c_binding
のtype(c_ptr)
で受け取れる。それを受け取れるようなCラッパーを書く。
void rokko_split_solver_name_f(char* str, char** library_ptr, char** routine_ptr) {
std::string tmp_library, tmp_routine;
rokko::split_solver_name(str, tmp_library, tmp_routine);
*library_ptr = copy_string(tmp_library);
*routine_ptr = copy_string(tmp_routine);
}
戻り値が文字列のC関数をFortranから呼び出す
subroutine rokko_split_solver_name_f (str, library, routine) &
bind(c,name='rokko_split_solver_name_f')
use iso_c_binding
implicit none
character(c_char), intent(in) :: str(*)
type(c_ptr), intent(out) :: library
type(c_ptr), intent(out) :: routine
end subroutine rokko_split_solver_name_f
char* rokko_parameters_get_string(struct rokko_parameters* params, const char* key) {
std::string tmp = static_cast<rokko::parameters*>(params->ptr)->get_string(key);
char* p = copy_string(tmp);
return p;
}
type(c_ptr) function rokko_parameters_get_string_c (params, key) &
bind(c,name='rokko_parameters_get_string')
use iso_c_binding
import rokko_parameters
implicit none
type(rokko_parameters), intent(in) :: params
character(c_char) :: key(*)
end function rokko_parameters_get_string_c
subroutine rokko_parameters_get_string (params, key, val)
use iso_c_binding
implicit none
type(rokko_parameters), intent(in) :: params
character(*), intent(in) :: key
character(len=:), allocatable, intent(out) :: val
type(c_ptr) :: ptr
character, pointer, dimension(:) :: tmp_array
character*255 :: tmp
integer :: i
integer(c_int) :: n
n = rokko_parameters_get_key_size_c (params, trim(key)//c_null_char)
ptr = rokko_parameters_get_string_c (params, trim(key)//c_null_char)
call c_f_pointer(ptr, tmp_array, (/n/) )
do i=1, n
tmp(i:i) = tmp_array(i)
enddo
call free_c(ptr)
val = trim(tmp(1:n)) ! automatically allocating suitable size
end subroutine rokko_parameters_get_string
- Rokko bindingで文字列をコピーし、最後にc_null_charを追加して、C bindingに渡す。
- コピーし終わった後は、C側の文字列をfreeする。
文字列からなる配列の定義
type(string)
をstring.F90で定義。
type(string)
の割り付け配列を用いれば、配列のサイズを動的に指定できる。(使用例:parameters
のkeys
や、登録されたソルバ名)
type(string), allocatable :: names(:)
以下、全ての要素の文字列長が同じ配列。メモリ使用が非効率であらかじめ文字列長を決めておく必要があるが、古いFortranのバージョンでも使用可能であるため、対応したい。
character(n), dimension(m) :: array
以下の方式は検討したが、使用していない。
type array_strings
type(string), allocatable :: string(:)
integer :: size
end type array_strings
diagonalize関数のオーバーロード
Fortranでは、function(戻り値ありの関数)において、戻り値を受け取らないことはできない。 params_outは、ユーザが必要なかったら、読み捨てたい。 そこで、params_outは受け渡しを省略できる引数とする。
ただし、実装には、optionalキーワードを使わない。 optionalキーワードを使うと、if(present(~))で分岐するルーチンを書かなければならないし、わずかだがオーバーヘッドがある。 引数と関数名の違うものを用意して、総称名で参照できるようにする。
C++において、引数をオプションにする代わりに、オーバーロードするのと似ている。
params_outが引数にないときに、ダミー変数を与えるのも気持ち悪い。 generic interfaceは、全部がsubroutineか全部がfunctionでなければならない:
Error: In generic interface 'kaijo' at (1) procedures must be either all SUBROUTINEs or all FUNCTIONs
params_outを指定するときは、params(入力パラメータ)は省略できない事に注意。
Fortran binding用のC wrapper関数は、最後に_f
を付けて、C binding用のC wrapper関数と住み分ける。
void rokko_serial_dense_ev_diagonalize_f
_f付きの関数もC bindingから呼び出せる。(params_outを返すC bindingがあるので、その必要はないが。)
Fortranから呼ばれるためのものなので、このC wrapperの入力引数は、ポインタ渡しで良い。
Fortran関数での行列引数
rokko_distributed_matrix
やrokko_mapping_bc
やrokko_localized_matrix
は構造体で、Fortran bindingでは以下のように定義されている。
type, bind(c) :: rokko_mapping_bc
type(c_ptr) ptr
integer(c_int) major
end type rokko_mapping_bc
type, bind(c) :: rokko_distributed_matrix
type(c_ptr) ptr
integer(c_int) major
end type rokko_distributed_matrix
- 構造体ごとコピーする必要があるため、Fortranでの入力変数としては
value, intent(in)
属性を指定する必要あり。 - 行列の中身をいじる場合も、書き込む場合も、
value, intent(in)
とする。
interface
subroutine rokko_frank_matrix_generate_distributed_matrix(matrix) bind(c)
use iso_c_binding
import rokko_distributed_matrix
implicit none
type(rokko_distributed_matrix), value, intent(in) :: matrix
end subroutine rokko_frank_matrix_generate_distributed_matrix
end interface
Fortran bindingで、wrapしたクラスの解放
各destruct関数では、deleteした後に、ヌルポインタ(C++11以降ではnullptr
)を代入する。
Fortranの予約語
- Fortranには予約語という概念はないので、
dim
やvalue
は元々あるキーワード名をユーザ変数として使っても問題ない。→見やすくするために、n
やval
を使う。
C言語とFortranの関数名の衝突防止
C言語とFortranで同名の関数がある場合、シンボル名がぶつかるのを防止するために、関数宣言でbind(c)を外す。(シンボル名のアンダースコアの数が異なるため、ぶつからなくなる。)
subroutine rokko_distributed_matrix_generate_function(matrix, func_in) !bind(c)
use iso_c_binding