Cを使ってRubyのメソッドを書く(その1)

###目次
Cを使ってRubyのメソッドを書く(その1) <- ここ
[Cを使ってRubyのメソッドを書く(その2) Numo::NArray](https://qiita.com/ki073/items/a9a06ed5f505e763893b)
###はじめに
C++でRubyの拡張ライブラリを書く方法は、「[C++を使ってRubyのメソッドを書く(その1)](https://qiita.com/ki073/items/b35e1e0ecc4862ea3c14)」にありますので参考にしてください。C++さえ書ければほとんど苦労せずにライブラリが作成できますので、自分で一から書かれるのでしたらそちらをお勧めします。

ここでは、Cで書かれた関数を拡張ライブラリにする方法を紹介します。C++でもCと同様の書き方ができますので、すでにCで書かれたライブラリがある場合にも使うことができます。

1. 2. はdouble, float, intなどの単純な変数に受け渡し、3.で文字列に受け渡し、4.で既存のライブラリを拡張ライブラリにする方法を紹介します。
配列を受け渡す方法は(その2)を参考にしてください。

###1. コンパイラが提供する関数を呼び出す
SWIGインターフェース定義ファイルを書きます。
上の%{..%}の中にはヘッダファイルを読み込むか、ヘッダファイルに相当することを書きます。

```test.i
%module test  // モジュール名 最初の文字は大文字に変換される
%{
#include <math.h>
%}

%include "typemaps.i"
extern double floor(double x);
// double modf(double value, double *iptr); ポインタで値を受け取る時は*OUTPUTに変更
extern double modf(double value, double *OUTPUT);
```

下の方にはRubyから呼び出す関数を書いています。math.hには多くの関数のヘッダが書かれていますが、その中からfloorとmodfをRubyから利用できるようにします。
modfに2番目の引数はポインタになっておりdouble型の値を返します。ポインタでは入力、出力、入出力が可能で、*INPUT, *OUTPUT, *INOUTと記述することでそれぞれの機能を持たせられます。

```ruby:extconf.rb
require 'mkmf'
create_makefile("test") # モジュール名と同じに
```

Makefikeを作成するためのRubyプログラムです。

以上のファイルを同じフォルダの中に入れ、次のコマンドを実行します。

```
swig -ruby test.i
ruby extconf.rb
make
```

以上で、test.bundle（拡張子はOSにより異なります）ができあがります。
テスト用プログラムです。

```ruby:test1.rb
require "./test"
p Test.floor(2.3)
p Test. modf(2.3)
```
###2. 自作の関数を呼び出す

単純な変数を受け渡しする関数を書いてみました。ごく普通のCの関数です。2倍の値を返す関数です。ポインタは使っていません。

```c:test2.c
double twicefold_d(double x){
  return(x*2.0);
}
```

```test.i
%module test
%{
double twicefold_d(double x);
%}

double twicefold_d(double x);
```
ヘッダファイルは作っていませんので、%{..%}の中にヘッダファイルに相当すること書きます。
extconf.rbファイルとコマンドは上と同じものを使ってください。
フォルダの中に.cや.cppなどのプログラムファイルがありましたら、全部コンパイルてリンクしようとしますので、不必要なファイルは入れておかないようにしましょう。

テスト用プログラムです。

```ruby:test2.rb
require "./test"
p Test.twicefold_d(3.4)
```

###3. 自作の関数を呼び出す 文字列を渡す
文字列も関数に簡単に渡すことができます。

```c:test3.c
#include "test3.h"
int string_length(char* str){
  return(strlen(str));
}
```

```c:test3.h
#include <string.h>
int string_length(char* str);
```

ここではヘッダファイルを作っています。そう手間はかかりませんので、ヘッダファイルを作っておいた方が何かと便利なように思います。

```test.i
%module test
%{
#include "test3.h"
%}

%include test3.h
```

同じくextconf.rbファイルとコマンドは上と同じものを使ってください。

```ruby:test1.rb
require "./test"
p Test.string_length("abcd")
```

###4. 既存ライブラリを利用できるようにする

GSLの関数gsl_hypotとgsl_hypot3を使えるようにします。GSLのヘッダファイルを読み込んで2つの関数のインターフェースを定義しています。2つの関数はヘッダファイルからそのままコピーしているだけで変更はしておりません。

```test.i
%module test
%{
#include "gsl/gsl_math.h"
%}

extern double gsl_hypot(const double x, const double y);
extern double gsl_hypot3(const double x, const double y, const double z);
```

```ruby:extconf.rb
require 'mkmf'
dir_config("gsl")
have_header("gsl/gsl_math.h")
have_library("gsl") # libgsl.soをリンクする
create_makefile("test")
```

GSLのヘッダファイル読み込みとlibgsl.soをリンクする必要がありますので、そのための3行を追加しています。コマンドは上と同じで大丈夫なことが多いのですが、ヘッダファイルとlibgsl.soの場所がわからわからない時には次のようにruby extconf.rbに後にパスを書きます。

```
ruby extconf.rb -- --with-gsl-dir=/path
--with-gsl-include=/path/include
--with-gsl-lib=/path/lib
```
/path/include/gsl/gsl_math.hと/path/lib/libgsl.soのような場所にファイルがある時には
--with-gsl-dir=/pathで両方いっぺんに指定できます。下2つは片方ずつ指定できます。

```ruby:test4.rb
require "./test"
p Test.gsl_hypot(3, 4)
p Test.gsl_hypot3(3, 4, 5)
```

CentOS 8.2 / Ruby 2.6.6 / Swig 3.0.12
macOS 10.13.6 / Ruby 2.7.1 / Swig 4.0.2