RubyでC++のバインディングを作成するRiceのドキュメントをChatGPTで日本語に翻訳しただけの記事

::: note
RubyでC++のバインディングを作成するために、Riceの公式ドキュメントをChatGPTでにほんごに翻訳しました。
:::

https://github.com/ruby-rice/rice

https://ruby-rice.github.io

## はじめに

Riceは、C++ 17対応のヘッダオンリーライブラリで、2つの目的を持っています。まず、既存のC++ライブラリに対するRubyのバインディングを簡単に作成することができます。次に、RubyのC APIに対してオブジェクト指向インターフェースを提供し、Rubyを埋め込んだり、C++でRubyの拡張機能を書くことを簡単にします。

Riceは、[Boost.Python](https://github.com/boostorg/python) や [pybind11](https://github.com/pybind/pybind11) と似ており、C++とインターフェースを作る際に必要な定型コードを最小限に抑えます。これにより、RubyオブジェクトをC++に、またその逆に変換するための型情報を自動で判定します。

Riceが提供するもの：

- クラスのラップや定義のためのシンプルなC++ベースの構文
- C++とRuby間の自動型変換
- C++とRuby間の自動例外処理
- ガベージコレクションを扱うためのスマートポインタ
- ほとんどの組み込み型に対するサポート

### バージョンの違い（3.xと4.x以降）

このドキュメントと`master`ブランチは、ヘッダオンリー版のRice 4.x以降用です。3.x系のリリースについては`3.x`ブランチのドキュメントとコードを使用してください。

ライブラリをRice 3から4にアップグレードするには、[Migrating](https://ruby-rice.github.io/4.x/)を参照してください。

3.x系のRiceのドキュメントは、[こちら](https://ruby-rice.github.io/3.x)で閲覧できます。

### プロジェクトの詳細

- ソースコードはGitHubでホストされています: [http://github.com/ruby-rice/rice](http://github.com/ruby-rice/rice)
- バグ追跡: [http://github.com/ruby-rice/rice/issues](http://github.com/ruby-rice/rice/issues)
- APIドキュメント: [http://ruby-rice.github.io/4.x](http://ruby-rice.github.io/4.x)

### インストール方法

```shell
gem install rice
```

Riceはヘッダオンリーのライブラリなので、別途ビルドする必要はありません。代わりに、C++プロジェクトで`#include`してください。RiceはC++17以降を必要とし、Windows（MSVCとMingw64）、MacOS（Xcode/clang）、Linux（g++）でテストされています。

## チュートリアル

### はじめに

Riceを使った拡張の作成は、C APIを使った拡張の作成と非常に似ています。

まず最初に、Riceのgemをインストールします：

```bash
gem install rice
```

次に、`extconf.rb`ファイルを作成します：

```ruby
require 'mkmf-rice'
create_makefile('test')
```

ここで、`mkmf`の代わりに`mkmf-rice`を使用している点に注意してください。これにより、拡張機能が標準のC++ライブラリとリンクされ、Riceのヘッダーファイルにアクセスできるようになります。

> **Note:** 上級者向け - `mkmf-rice`の代わりにCMakeなどの独自のビルドシステムを使用することもできます。その場合、[rice.hpp](https://github.com/ruby-rice/rice/blob/master/include/rice/rice.hpp)をGitHubからダウンロードして、ソースツリーに直接含めることをお勧めします。

次に、拡張を作成し、それを`test.cpp`に保存します：

```cpp
extern "C"
void Init_test()
{
}
```

上記の`extern "C"`行に注意してください。これにより、コンパイラは`Init_test`関数がCリンクと呼び出し規約を持つべきであることを認識します。これにより、名前のマングリングがオフになり、Rubyインタプリタが関数を見つけられるようになります（RubyはCで書かれているためです）。

今のところ、拡張には何も入っていないので、特に役には立ちません。次のステップは、メソッドを追加できるようにクラスを定義することです。

### クラスの定義

Riceでクラスを定義するのは簡単です：

```cpp
#include <rice/rice.hpp>

using namespace Rice;

extern "C"
void Init_test()
{
  Class rb_cTest = define_class("Test");
}
```

これにより、`Object`を継承した`Test`というクラスが作成されます。別のクラスを継承したい場合は、2番目のパラメータを使用します：

```cpp
#include <rice/rice.hpp>

using namespace Rice;

extern "C"
void Init_test()
{
  Class rb_cMySocket = define_class("MySocket", rb_cIO);
}
```

クラス名に`rb_c`というプレフィックスが付いている点に注意してください。これは、Rubyインタプリタおよび多くの拡張で使用される命名規則であり、これはクラスであり他のタイプのオブジェクトではないことを示しています。よく使用されるその他の命名規則は次のとおりです：

- `rb_c`：クラスの変数名のプレフィックス
- `rb_m`：モジュールの変数名のプレフィックス
- `rb_e`：例外タイプの変数名のプレフィックス
- `rb_`：Ruby C APIの関数のプレフィックス
- `rb_f_`：Rubyオブジェクトを引数として取るAPI関数とC引数タイプを取る関数を区別するための関数プレフィックス
- `rb_*_s_`：関数がシングルトン関数であることを示すプレフィックス
- `*_m`：関数が可変数の引数を取ることを示すサフィックス

また、「ruby.h」を直接インクルードしないことにも注意してください。Riceには「ruby.h」をラップするヘッダーがあり、プラットフォームやRubyバージョン間の互換性の問題を処理します。必ず「ruby.h」をインクルードする前にRiceのヘッダーをインクルードしてください。

### メソッドの定義

次に、クラスにメソッドを追加しましょう：

```cpp
#include <rice/rice.hpp>

using namespace Rice;

Object test_hello(Object /* self */)
{
  String str("hello, world");
  return str;
}

extern "C"
void Init_test()
{
  Class rb_cTest =
    define_class("Test")
    .define_method("hello", &test_hello);
}
```

ここでは、文字列「Hello, World」を返すメソッド`Test#hello`を追加しています。このメソッドはselfを暗黙のパラメータとして取りますが、使用しないため、コンパイラの警告を防ぐためにコメントアウトしています。

クラスに`#initialize`メソッドを追加することもできます：

```cpp
#include <rice/rice.hpp>
#include <rice/stl.hpp>

using namespace Rice;

Object test_initialize(Object self)
{
  self.iv_set("@foo", 42);
}

Object test_hello(Object /* self */)
{
  String str("hello, world");
  return str;
}

extern "C"
void Init_test()
{
  Class rb_cTest =
    define_class("Test")
    .define_method("initialize", &test_initialize)
    .define_method("hello", &test_hello);
}
```

`initialize`メソッドはインスタンス変数`@foo`に値42を設定します。この数値は代入前に自動的に`Fixnum`に変換されます。

`Class`オブジェクトに対するメソッド呼び出しをチェインしている点に注意してください。`Module`と`Class`のほとんどのメンバー関数は`self`への参照を返すので、定義したいメソッドの数だけ呼び出しをチェインできます。

> **Note:** コンパイラが「一致するオーバーロードされた関数が見つかりません」とエラーを出し、その後に「テンプレート引数'Function_T'を推測できません」と表示された場合、それはオーバーロードされたC++関数またはメソッドを使用していることを意味します。この場合、:ref:`overloaded_methods`セクションに記載されているようにRiceに少し助けを提供する必要があります。

### ラムダを使ったメソッドの定義

C++ラムダを使ってメソッドを定義することも可能です。`define_method`と同様に、ラムダはselfを暗黙のパラメータとして取ります：

```cpp
Class rb_cTest =
  define_class("Test")
  .define_method("hello", [](Object& object) {
    return test_hello;
  });
```

selfをコピーしたくないので、参照としてselfを渡している点に注意してください！

### 関数の定義

Rubyクラスにメソッドを追加するには、`define_function`を使用することも可能です。違いは、暗黙のselfパラメータが渡されないことです。再び、関数ポインタやラムダを使用することができます：

```cpp
void some_function()
{
  // do something
}

extern "C"
void Init_test()
{
  Class rb_cTest =
    define_class("Test")
    .define_function("some_function", &some_function)
    .define_function("some_function_lambda", []() {
      return some_function();
    });
}
```

### C++タイプのラッピング

RubyクラスをC++スタイルで定義できるのは便利ですが、Riceの本当の強みは既に定義されたC++タイプをラップすることにあります。

次のC++クラスをラップしたいと仮定しましょう：

```cpp
class Test
{
public:
  static std::string static_hello();
public:
  Test();
  std::string hello();
};
```

これをラップするために：

```cpp
#include <rice/rice.hpp>
#include <rice/stl.hpp>

using namespace Rice;

extern "C"
void Init_test()
{
  Data_Type<Test> rb_cTest =
    define_class<Test>("Test")
    .define_constructor(Constructor<Test>())
    .define_function("static_hello", &Test::static_hello)
    .define_method("hello", &Test::hello);
}
```

この例では、`Class`の代わりに`Data_Type<>`を使用し、非テンプレートバージョンの`define_class()`の代わりにテンプレートバージョンを使用しています。これにより、Riceライブラリ内でRubyクラス`Test`とC++クラスTestの間にバインディングが作成されます。

次に、C++静的メンバ関数によって実装される関数`static_hello`を定義します。静的関数は特定のオブジェクトに結びついていないため、selfパラメータはありません。そのため、`define_method`の代わりに

`define_function`を使用します。

最後に、C++メンバ関数によって実装されるメソッド`hello`を定義します。Rubyがこの関数を呼び出すと、Riceは呼び出しを正しいC++ Testインスタンスに指示するため、暗黙のselfパラメータは渡されません。

### 属性の定義

C++構造体やクラスには、データを格納するためのパブリックメンバ変数がしばしばあります。Riceは、これらのメンバ変数を`define_attr`を使用して簡単にラップできます：

```cpp
struct MyStruct
{
  int readOnly = 0;
  int writeOnly = 0;
  int readWrite = 0;
};

Data_Type<MyStruct> rb_cMyStruct =
  define_class<MyStruct>("MyStruct")
  .define_constructor(Constructor<MyStruct>())
  .define_attr("read_only", &MyStruct::readOnly, Rice::AttrAccess::Read)
  .define_attr("write_only", &MyStruct::writeOnly, Rice::AttrAccess::Write)
  .define_attr("read_write", &MyStruct::readWrite);
}
```

読み取り専用属性を定義するための`Rice::AttrAccess::Read`および書き込み専用属性を定義するための`Rice::AttrAccess::Write`の使用に注目してください。AttrAccessの値を指定しない場合、Riceは属性を読み取りおよび書き込み可能にします。

これらの属性は、Rubyで次のようにしてアクセスできます：

```ruby
my_struct = MyStruct.new
a = my_struct.read_only
my_struct.write_only = 5
my_struct.read_write = 10
b = my_struct.read_write
```

同様に、静的メンバを`define_singleton_attr`を使用してラップすることができます：

```cpp
struct MyStruct
{
  static int readOnly = 0;
  static int writeOnly = 0;
  static int readWrite = 0;
};

Data_Type<MyStruct> rb_cMyStruct =
  define_class<MyStruct>("MyStruct")
  .define_constructor(Constructor<MyStruct>())
  .define_singleton_attr("read_only", &MyStruct::readOnly, Rice::AttrAccess::Read)
  .define_singleton_attr("write_only", &MyStruct::writeOnly, Rice::AttrAccess::Write)
  .define_singleton_attr("read_write", &MyStruct::readWrite);
}
```

これらの属性は、Rubyで次のようにしてアクセスできます：

```ruby
a = MyStruct.read_only
MyStruct.write_only = 5
MyStruct.read_write = 10
b = MyStruct.read_write
```

### 型変換

RiceはほとんどのRubyおよびC++オブジェクト間の変換を行うことができます。例として、再び以下のクラスを見てみましょう：

```cpp
class Test
{
public:
  Test();
  std::string hello();
};
```

クラスを記述するときに、`hello()`によって返される`std::string`をRubyタイプに変換するコードを一行も書いていません。それでも変換は機能し、以下のコードを記述すると：

```ruby
test = Test.new
puts test.hello
```

期待どおりの結果が得られます。

Riceには多くのC++タイプのデフォルトの特殊化が含まれています。独自の変換を定義するには、[Type Conversions](bindings/type_conversions)のセクションを参照してください。

### ラップされたC++タイプの変換

再び`Test`クラスのラッパーを見てみましょう：

```cpp
extern "C"
void Init_test()
{
  Data_Type<Test> rb_cTest =
    define_class<Test>("Test")
    .define_constructor(Constructor<Test>())
    .define_method("hello", &Test::hello);
}
```

`define_class<Test>`を呼び出したとき、Riceは新しいクラスを自動的に型システムに登録したので、以下の呼び出し：

```cpp
Data_Object<Foo> obj(new Foo);
Foo * f = detail::From_Ruby<Foo *>::convert(obj);
Foo const * f = detail::From_Ruby<Foo const *>::convert(obj);
```

が期待通りに動作します。

`Data_Object`クラスはC拡張の`TypedData_Wrap_Struct`および`TypedData_Get_Struct`マクロのラッパーです。これは`Data_Type`に割り当てられた任意のクラスをラップまたはアンラップするために使用できます。これは`Object`から継承しているため、`Object`上で呼び出せる任意のメンバー関数を`Data_Object`上でも呼び出すことができます：

```cpp
Object object_id = obj.call("object_id");
std::cout << object_id << std::endl;
```

`Data_Object`クラスは新しく作成されたオブジェクトをラップするために使用できます：

```cpp
Data_Object<Foo> foo(new Foo);
```

または既に作成されたオブジェクトをアンラップするために使用できます：

```cpp
VALUE obj = ...;
Data_Object<Foo> foo(obj);
```

`Data_Object`はスマートポインタのように機能します：

```cpp
Data_Object<Foo> foo(obj);
foo->foo();
std::cout << *foo << std::endl;
```

`VALUE`や`Object`と同様に、`Data_Object`に格納されたデータは`Data_Object`がスタックにある限り、ガベージコレクタによってマークされます。

### 例外

一般的にRiceは例外を自動的に処理します。たとえば、例のクラスのメンバ関数が例外をスローする可能性があるとしましょう：

```cpp
class MyException
  : public std::exception
{
};

class Test
{
public:
  Test();
  std::string hello();
  void error();
};

extern "C"
void Init_test()
{
  Data_Type<Test> rb_cTest =
    define_class<Test>("Test")
    .define_constructor(Constructor<Test>())
    .define_method("hello", &Test::hello)
    .define_method("error", &Test::error);
}
```

この関数をRubyから呼び出すと、C++は例外を発生させます。Riceは自動的にそれをキャッチして、Rubyの例外に変換します：

```ruby
test = Test.new
begin
  test.error()
rescue => e
  ..
end
```

例外についての詳細は、:ref:`Exceptions`セクションを参照してください。

## 関数とメソッド

チュートリアルでは、C++の関数、静的メンバ関数、およびメンバ関数のラップについて触れました。ここでは、さらに詳しく説明します。

### デフォルト引数

最初のC++クラスの例に戻り、`hello()`メソッドに追加の引数を追加し、そのうちの1つにデフォルト値を設定してみましょう：

```cpp
class Test
{
public:
  Test();
  std::string hello(std::string first, std::string second = "world");
};
```

テンプレートを通してデフォルトのパラメータ値を利用することはできないため、Riceにデフォルト値を知らせるためには`Rice::Arg`を使用する必要があります：

```cpp
#include <rice/rice.hpp>

using namespace Rice;

extern "C"
void Init_test()
{
  Data_Type<Test> rb_cTest =
    define_class<Test>("Test")
    .define_constructor(Constructor<Test>())
    .define_method("hello",
       &Test::hello,
       Arg("hello"), Arg("second") = "world"
    );
}
```

構文は`Arg(nameOfParameter)[ = defaultValue]`です。ここでパラメータの名前は重要ではありません（可読性のためです）が、`operator=`を使用して設定される値はパラメータの型と一致する必要があります。そのため、デフォルト値を明示的にキャストする必要があるかもしれません。

```cpp
.define_method("hello",
   &Test::hello,
   Arg("hello"), Arg("second") = (std::string)"world"
);
```

これらの`Rice::Arg`オブジェクトは正しい位置に配置する必要があります。したがって、2番目の引数にデフォルト値がある場合、2つのArgオブジェクトが必要です。

これでRubyはデフォルトの引数について認識し、このラッパーは期待通りに使用できます：

```ruby
t = Test.new
t.hello("hello")
t.hello("goodnight", "moon")
```

これはコンストラクタにも適用されます：

```cpp
.define_constructor(Constructor<SomeClass, int, int>(),
    Arg("arg1") = 1, Arg("otherArg") = 12);
```

### Return

`Arg`クラスと同様に、RiceにはC++から返される値をどのように処理するかを指示するための`Return`クラスもあります。これは特にメモリ管理を正しく行うために重要です（詳細は:ref:`cpp_to_ruby`を参照）。

RubyのVALUE型（Rubyオブジェクトを表す）を処理する際にも役立ちます。ほとんどの場合、RiceはVALUEインスタンスを自動的に処理しますが、ネイティブメソッドがVALUE引数を取る場合やVALUEインスタンスを返す場合はRiceにその旨を伝える必要があります。

これは、VALUEが`unsigned long long`のtypedefであるためです。実際にはRubyオブジェクトへのポインタですが、RiceにとってはRubyの数値として変換すべき整数に過ぎません。そのため、メソッドがVALUEパラメータを取る場合、RiceはそれをC++の`unsigned long long`値に変換してしまいます。同様に、メソッドがVALUEを返す場合、Riceは単にそれをRubyオブジェクトとして返すのではなく、数値Rubyオブジェクトに変換してしまいます。

この不正な変換を避けるために、`Arg`および`Return`クラスの`setValue()`メソッドを使用します。例：

```cpp
VALUE some_function(VALUE ary)
{
  VALUE new_ary = rb_ary_dup(ary);
  rb_ary_push(new_ary, Qtrue);
  return new_ary;
}

define_global_function("some_function", &some_function, Arg("ary").setValue(), Return.setValue());
```

`Arg`と`Return`オブジェクトを任意の順序で混在させることができます。例えば、次のコードも動作します：

```cpp
define_global_function("some_function", &some_function, Return.setValue(), Arg("ary").setValue());
```

### selfを返す

メソッドが`self`を返す場合、つまり関数呼び出しのレシーバであった同じC++オブジェクトを返す場合、Riceは同じRubyオブジェクトが返されることを保証します。`self`を返すことはRubyで一般的なパターンです。

例：

```ruby
a = Array.new
a << 1 << 2 << 3 << 4
```

上記のコードは、`<<`メソッドがArray `a`を返すために機能します。この動作を模倣するために、C++クラスをラップするときにラムダを使用できます。例えば、Riceは`std::vector`を次のようにラップします：

```cpp
define_vector<std::vector<int32_t>>().
define_method("<<", [](std::vector<int32_t>& self, int32_t value) -> std::vector<int32_t>&
{
  self.push_back(value);
  return self;  // 呼び出しのチェインを可能にする
});
```

ラムダの戻り値の型`std::vector<int32_t>&`に注意してください。戻り値の型が指定されていない場合、デフォルトでラムダは値で返します。それにより、`std::vector`のコピーコンストラクタが呼び出され、*2つ*の`std::vector<int32_t>`インスタンスと2つのRubyオブジェクトが生成されます。これは望ましくありません。

## オーバーロードされたメソッド

C++はオーバーロードされたメソッドや関数をサポートしています。オーバーロードされた関数をラップしようとすると、C++コンパイラは「no matching overloaded function found」（一致するオーバーロードされた関数が見つかりません）というエラーメッセージを出します。

例えば、次のC++クラスを考えてみましょう：

```cpp
class Container
{
public:
  size_t capacity()
  {
    return this->capacity_;
  }

  void capacity(size_t value)
  {
    this->capacity_ = value;
  }

private:
  size_t capacity_;
};
```

このクラスを次のようにラップしようとすると、コンパイラエラーが発生します：

```cpp
Class c = define_class<Container>("Container")
  .define_constructor(Constructor<Container>())
  .define_method("capacity", &Container::capacity)
  .define_method("capacity=", &Container::capacity);
```

代わりに、どのオーバーロードされたメソッドを使用するかをC++コンパイラに伝える必要があります。以下に説明するいくつかの方法があります。

### テンプレートパラメータ

`define_method`はテンプレート関数です。したがって、一つの解決策は次のように呼び出そうとしているメソッドを指定することです：

```cpp
Class c = define_class<Container>("Container")
  .define_constructor(Constructor<Container>())
  .define_method<size_t(Container::*)()>("capacity", &Container::capacity)
  .define_method<void(Container::*)(size_t)>("capacity=", &Container::capacity);
```

`size_t(Container::*)()` と `void(Container::*)(size_t)` はC++のメンバ関数へのポインタです。

### usingの使用

もう一つの解決策は、C++の`using`機能を使用する方法です：

```cpp
using Getter_T = size_t(Container::*)();
using Setter_T = void(Container::*)(size_t);

Class c = define_class<Container>("Container")
  .define_constructor(Constructor<Container>())
  .define_method<Getter_T>("capacity", &Container::capacity)
  .define_method<Setter_T>("capacity=", &Container::capacity);
```

または次のように書くこともできます：

```cpp
using Getter_T = size_t(Container::*)();
using Setter_T = void(Container::*)(size_t);

Class c = define_class<Container>("Container")
  .define_constructor(Constructor<Container>())
  .define_method("capacity", (Getter_T)&Container::capacity)
  .define_method("capacity=", (Setter_T)&Container::capacity);
```

### typedefの使用

古いスタイルを好み、やや難解な構文を使いたい場合は、`typedef`を使うこともできます：

```cpp
extern "C"
void Init_Container()
{
    typedef size_t(Container::* Getter_T)();
    typedef void (Container::* Setter_T)(size_t);

    Class c = define_class<Container>("Container")
      .define_constructor(Constructor<Container>())
      .define_method("capacity", (Getter_T)&Container::capacity)
      .define_method("capacity=", (Setter_T)&Container::capacity);
}
```

### Rubyでの例

このクラスをラップした後は、Rubyで簡単に使用できます：

```ruby
container = Container.new
container.capacity = 6
puts container.capacity
```

表示される結果は7になります。

## イテレータ

C++のイテレータは、コンテナに格納されている要素を順に操作するために使用されます。C++のイテレータは外部イテレータであり、開始イテレータと終了イテレータのペアで機能します。例えば、`std::vector`には`begin`/`end`、`cbegin`/`cend`、`rbegin`/`rend`などがあります。

### Enumerableのサポート（内部イテレータ）

Riceを使用すると、C++クラスに簡単に[`Enumerable`](https://ruby-doc.org/3.2.2/Enumerable.html)モジュールのサポートを追加できます。Enumerableモジュールは、Rubyクラスが`each`インスタンスメソッドを定義している限り、そのクラスに内部イテレータのサポートを追加します。

Riceは`define_iterator`メソッドを使ってこれを簡単にします。`define_iterator`は`each`メソッドを作成し、さらに`Enumerable`モジュールをミックスインします。

例えば、`std::vector`の簡単なラッパーを作成してみましょう（完全なサポートについては、:ref:`std_vector`を参照してください）。

```cpp
#include <vector>
#include <rice/rice.hpp>

using namespace Rice;

extern "C"
void Init_IntVector()
{
  using IntVector = std::vector<int>;
  define_class<IntVector>("IntVector")
    .define_constructor(Constructor<IntVector>())
    .define_method<void(IntVector::*)(const IntVector::value_type&)>("push_back", &IntVector::push_back)
    .define_iterator<IntVector::iterator(IntVector::*)()>(&IntVector::begin, &IntVector::end);
}
```

ここでは、`push_back`、`begin`、`end`のどのオーバーロードバージョンを公開するかをRiceに伝える必要があります。詳細については、:ref:`overloaded_methods`を参照してください。

イテレータが定義されると、次のような標準のRubyコードを書くことができます：

```ruby
intVector = IntVector.new
intVector.push_back(1)
intVector.push_back(2)
intVector.push_back(3)

result = intVector.map do |value|
  value * 2
end
```

ここで、`result`は`[2, 4, 6]`になります。

また、`std::vector`の逆方向イテレータをRubyに`reach`というメソッド名で公開したいとします。これは、`define_iterator`呼び出しに3番目のパラメータを追加することで行います。この場合、`reach`に設定します：

```cpp
extern "C"
void Init_IntVector()
{
  define_class<IntVector>("IntVector")
    .define_iterator<IntVector::reverse_iterator(IntVector::*)()>(&IntVector::rbegin, &IntVector::rend, "reach");
}
```

Rubyの例は次のようになります：

```ruby
intVector = IntVector.new
intVector.push_back(1)
intVector.push_back(2)
intVector.push_back(3)

result = Array.new
intVector.reach do |value|
  result << value * 2
end
```

ここで、`result`は`[6, 4, 2]`になります。

### Enumeratorのサポート（外部イテレータ）

Rubyは`Enumerator`クラスを使用して外部イテレータをサポートしています。`define_iterator`メソッドは、Enumeratorのサポートを自動的に追加します。

Enumeratorは、イテレータメソッドをブロックなしで呼び出すことによって作成できます。これは、Array#eachなどのメソッドをブロックなしで呼び出す方法と同じです。例：

```ruby
intVector = IntVector.new
intVector.push_back(1)
intVector.push_back(2)
intVector.push_back(3)

# Enumeratorを取得
enumerator = intVector.each

# 使用例
enumerator.map do |i|
  i * 2
end
```

## Enumerators

Rubyはあまり頻繁には使用されませんが、内部および外部の反復処理を可能にする列挙子（Enumerator）をサポートしています。列挙子を作成する最も簡単な方法は、ブロックを渡さずに列挙可能なメソッドを呼び出すことです。例えば：

```ruby
a = [1, 2, 3, 4, 5]

# 一般的な反復処理
a.each do |i|
  puts i
end

# 列挙子を取得
enumerator = a.each

# 使用方法
enumerator.map { |i| i * 2 }
```

Riceは、:ref:`std_vector`, :ref:`std_map`, および :ref:`std_unordered_map`のようなSTLコンテナのための列挙子を返すためのビルトインサポートを提供しています。

列挙子を実装するのは難しく、実際には多くのRiceの機能が必要です。ここでは、`std::vector`に対する列挙子のサポートがどのように実装されているかを見てみましょう。

### Implementation

まず、コードを見てみましょう：

```cpp
define_method("each", [](T& vector) -> const std::variant<std::reference_wrapper<T>, Object>
{
  if (!rb_block_given_p())
  {
    auto rb_size_function = [](VALUE recv, VALUE argv, VALUE eobj) -> VALUE
    {
      // 上記のベクターをキャプチャできないので（そうするとこのラムダをrb_enumeratorize_with_sizeに送ることができなくなる）
      // recvから抽出します
      T* receiver = Data_Object<T>::from_ruby(recv);
      return detail::To_Ruby<size_t>().convert(receiver->size());
    };

    return rb_enumeratorize_with_size(detail::selfThread, Identifier("each").to_sym(), 0, nullptr, rb_size_function);
  }

  for (Value_T& item : vector)
  {
    VALUE element = detail::To_Ruby<Value_T>().convert(item);
    detail::protect(rb_yield, element);
  }

  return std::ref(vector);
});
```

以下の各セクションで詳細を見ていきます。

### Method Signature

まず、RiceはRubyの`enumerable`モジュールをサポートするために`each`メソッドを定義します。そのシグネチャは次のようになります：

```cpp
define_method("each", [](T& vector) -> const std::variant<std::reference_wrapper<T>, Object>
```

`std::vector`には`each`メソッドがないため、Riceは代わりにベクターと対話するラムダ関数を作成します。ベクターはコピーを避けるために参照`T&`で渡されます。

さらに興味深いのは、戻り値の型が`std::variant`であることです。これは、メソッドがRubyの列挙子またはベクターのいずれかを返す必要があるためです。

最初のケースでは、ベクターを返すことはC++のメンバ関数から`this`を返すのと同じです。またはRuby関数から`self`を返すのと同様です。これにより、例えば`vector.a.b`のようにメソッドを連鎖して使用することが可能になります。

ベクターの参照を返さなければならず、コピーを返してはいけません。コピーを返すと、新しいRubyオブジェクトが作成されてしまい、メモリが無駄になるだけでなく、`self`がもはや`self`でなくなり、非常に予想外の結果になります。しかし、`std::variant`は参照を含むことができないため、返す必要があるのは`std::reference_wrapper<T>`です。

2番目のケースでは、新しいRubyの列挙子を返したいので、その型は`VALUE`です。しかし、`VALUE`を直接返すことはできません。なぜならRiceはそれを`unsigned long long`（実際それが本質です）として解釈してしまうからです。代わりに、`Rice::Object`を返します。詳細については:ref:`return`クラスを参照してください。

### Creating an Enumerator

次に、列挙子を返すコードを見てみましょう：

```cpp
if (!rb_block_given_p())
{
  auto rb_size_function = [](VALUE recv, VALUE argv, VALUE eobj) -> VALUE
  {
    // 上記のベクターをキャプチャできないので（そうするとこのラムダをrb_enumeratorize_with_sizeに送ることができなくなる）
    // recvから抽出します
    T* receiver = Data_Object<T>::from_ruby(recv);
    return detail::To_Ruby<size_t>().convert(receiver->size());
  };

  return rb_enumeratorize_with_size(detail::selfThread, Identifier("each").to_sym(), 0, nullptr, rb_size_function);
}
```

ユーザーがブロックを提供しない場合、メソッドは列挙子を返すべきです。列挙子は次のように作成されます：

```cpp
return rb_enumeratorize_with_size(detail::selfThread, Identifier("each").to_sym(), 0, nullptr, rb_size_function);
```

`rb_enumeratorize_with_size`の最初のパラメータはC++インスタンスではなくRubyインスタンスを必要とすることに注意してください。正しいRubyインスタンスは、C++インスタンスをラップしているもので、スレッドローカル変数`selfThread`に保存されています。

### Supporting Enumerator Size

`rb_enumeratorize_with_size`呼び出しには、列挙されるオブジェクトのサイズを返す関数へのオプションのポインタが含まれています。この場合、ベクターのサイズです。それは別のラムダ関数として実装されています：

```cpp
auto rb_size_function = [](VALUE recv, VALUE argv, VALUE eobj) -> VALUE
{
  // 上記のベクターをキャプチャできないので（そうするとこのラムダをrb_enumeratorize_with_sizeに送ることができなくなる）
  // recvから抽出します
  T* receiver = Data_Object<T>::from_ruby(recv);
  return detail::To_Ruby<size_t>().convert(receiver->size());
};
```

このラムダはCコードに送られているため、ローカル変数をキャプチャできません。そのため、`T& vector`パラメータに直接アクセスできません。代わりに、ベクターをラップしているRubyオブジェクトから抽出する必要があります：

```cpp
T* receiver = Data_Object<T>::from_ruby(recv);
```

次に、ベクターのサイズを決定し、それをRubyオブジェクトとして返す必要があります：

```cpp
return detail::To_Ruby<size_t>().convert(receiver->size());
```

### Yielding to a Block

最後に、はるかに一般的なユースケースである、ブロックに値を引き渡すことについて説明します：

```cpp
for (Value_T& item : vector)
{
  VALUE element = detail::To_Ruby<Value_T>().convert(item);
  detail::protect(rb_yield, element);
}
```

このコードは非常にシンプルです。ベクター内の各アイテムを参照によって反復処理し（コピーなし！）、それをRubyオブジェクトでラップし、ブロックに返します。`rb_yield`の呼び出しは、Rubyが例外を発生させる場合に備えて`detail::protect`を介して行われます。

### Returning Self

最後に、メソッドチェーンを可能にするために`self`を返すことがRubyでは一般的な慣行です。この場合の`self`はベクターをラップしているRubyオブジェクトです。ベクターへの参照を返すことによって、Riceはそれを元のRubyオブジェクトにマッピングするのに十分賢いです。

```cpp
return std::ref(vector);
```

上述したように、戻り値のバリアントに含めるために、ベクターを`std::reference_wrapper`に入れる必要があります。

## メモリ管理

C++ APIをラップする際に最も難しい部分は、C++とRuby間で共有されるメモリを正しく管理することです。これを正しく行わないと、プログラムは**必ず**クラッシュします。正しく行うための鍵は、各メモリ片の所有者が誰であるかを明確にすることです。

Riceはネイティブタイプをビルトインタイプと外部タイプに分けます。ビルトインタイプはC++とRubyの間でコピーされるのに対し、外部タイプはラップされます。ビルトインタイプに関する追加情報については、:doc:`Type Conversions <type_conversions>`セクションを参照してください。

このセクションの残りでは、外部タイプのメモリ管理について説明します。

### C++からRubyへの移行

デフォルトでは、RiceはRubyに渡されたC++インスタンスがC++によって引き続き所有されると仮定します。したがって、所有権の移転はありません。この場合、所有権の移転は次のルールに従います：

| メソッドの戻り値の型 (T) | C++からRubyへ | クリーンアップ                        |
|-------------------------|---------------|--------------------------------------|
| 値 (T)                   | コピーコンストラクタ | Rubyがコピーを解放し、C++が元のオブジェクトを解放 |
| 参照 (T&)               | コピーなし    | C++がC++インスタンスを解放            |
| ポインタ (T*)            | コピーなし    | C++がC++インスタンスを解放            |

しかし、多くのAPIでは、返された値の所有権を呼び出し元に移転します。この場合、Rubyがその値の所有権を引き継ぎます。この場合の所有権の移転は次のルールに従います：

| メソッドの戻り値の型 (T) | C++からRubyへ | クリーンアップ                        |
|-------------------------|---------------|--------------------------------------|
| 値 (T)                   | コピーコンストラクタ | Rubyがコピーを解放し、C++が元のオブジェクトを解放 |
| 参照 (T&)               | ムーブコンストラクタ | RubyがC++インスタンスを解放          |
| ポインタ (T*)            | コピーなし    | RubyがC++インスタンスを解放          |

### 所有権の移転

Rubyが返された値の所有権を取得すべきことをRiceに伝えるには、:ref:`return`を使用します。例を見てみましょう：

```cpp
class MyClass
{
}

class Factory
{
public:
  static MyClass* create()
  {
    return new MyClass();
  }
}

extern "C"
void Init_test()
{
  Data_Type<MyClass> rb_cMyClass = define_class<MyClass>("MyClass");

  Data_Type<Factory> rb_cFactory = define_class<Factory>("Factory")
      .define_function("create", &Factory::create); // 間違い、メモリリークを引き起こす
}
```

Rubyから`Factory#create`が呼び出されるたびに、`MyClass`の新しいC++インスタンスが作成されます。Riceのデフォルトのルールを使用すると、これによりメモリリークが発生します。なぜなら、それらのインスタンスは解放されないからです。

```ruby
1_000.times do
  my_class = Factory.create
end
```

これを修正するには、返されたインスタンスの所有権を取得するようにRiceに指示する必要があります：

```cpp
define_function("create", &Factory::create, Return().takeOwnership());
```

ここで`Return().takeOwnership()`が追加されていることに注意してください。これにより、Returnクラスのインスタンスが作成され、C++から返されたインスタンスの所有権を取得するように指示されます。

### RubyからC++への移行

時には、あるRubyオブジェクトのライフタイムを別のオブジェクトに結びつける必要があります。これはコンテナでよく発生します。例えば、`Listener`と`ListenerContainer`クラスを考えてみましょう。

```cpp
class Listener
{
};

class ListenerContainer
{
  public:
    void addListener(Listener* listener)
    {
      mListeners.push_back(listener);
    }

    int process()
    {
      for(const Listener& listener : mListeners)
      {
      }
    }

  private:
    std::vector<Listener*> mListeners;
};
```

これらのクラスがRiceでラップされていると仮定すると、次のコードはクラッシュします：

```ruby
@handler = ListenerContainer.new
@handler.add_listener(Listener.new)
GC.start
@handler.process # クラッシュ！！！
```

Rubyのガベージコレクタは`Listener.new`オブジェクトが孤立していることに気付き、それを解放します。結果として、基盤となるC++のListenerオブジェクトも解放され、`process`が呼び出されるとクラッシュします。

これを防ぐために、Rubyのリスナーインスタンスのライフタイムをコンテナに結びつけたいと思います。これは引数リストで`keepAlive()`を呼び出すことで行います：

```cpp
define_class<ListenerContainer>("ListenerContainer")
  .define_method("add_listener", &ListenerContainer::addListener, Arg("listener").keepAlive())
```

この変更により、リスナーがコンテナに追加されると、コンテナはそれへの参照を保持し、`rb_gc_mark`を呼び出してそれを保持します。これはArrayやHashなどのRubyのコレクションクラスが行うのと全く同じことです。`Listener`オブジェクトはコンテナ自体がスコープ外になるまで解放されません。

別の例として、返されたオブジェクトが元のオブジェクトに依存している場合があります。例えば：

```cpp
class Column;

class Database
{
public:
  Database()
  {
    // データベースに接続
  }

  ~Database()
  {
    // データベースから切断
  }

  Column getColumn(uint32_t index)
  {
     return Column(*this, index);
  }

  std::string lookupName(uint32_t index)
  {
    return some_name;
  }
};

class Column
{
public:
  Column(Database& database, uint32_t index): database_(database), index_(index)
  {
  }

  std::string getName()
  {
    return this->database_.lookupName(this->index_);
  }

private:
  Database& database_;
  uint32_t index_;
};
```

これらのクラスがRiceでラップされていると仮定すると、次のRubyコードはクラッシュします：

```ruby
def get_column(column_index)
  database = Database.new(...)
  column = database.get_column(column_index)
end

column = get_column(0)
puts column.name
```

問題は、`get_column`で作成された`Database`クラスのインスタンスが、メソッドの戻り時にガベージコレクションされる可能性が高いことです。その結果、`Column#name`が呼び出されると、もはや有効でないデータベースオブジェクトへの参照が残っていることになります。

このコードは、プログラム全体でデータベースオブジェクトが存続するように書き直すこともできますが、代わりにRiceにDatabaseオブジェクトのライフタイムをColumnオブジェクトに結びつけるように指示することもできます。これにより、Columnが解放されるまでDatabaseも解放されません：

```cpp
define_class<Database>("Database")
  .define_method("get_column", &Database::getColumn, Return().keepAlive())
```

`Return().keepAlive()`は外部タイプでのみ動作します。ビルトインタイプでこれを使用しようとすると、実行時に例外が発生します。

### C++からRubyオブジェクトを参照する

C++からRubyオブジェクトを参照する場合、それらが早期にガベージコレクションされないようにRubyに知らせる必要があります。

これを行う方法はいくつかあります：

#### スタック

スタックに格納されているVALUEやObjectを操作する場合、Rubyのガベージコレクタはそれらを自動的に見つけようとします。しかし、最適化コンパイラがそれを妨げる可能性があります。そのため、Rubyの[`RB_GC_GUARD`](https://docs.ruby-lang.org/en/3.2

/extension_rdoc.html#label-Appendix+E.+RB_GC_GUARD+to+protect+from+premature+GC)マクロを使用する必要があるかもしれません。

#### ヒープ

ヒープ上にオブジェクトを割り当てる場合、またはそれがヒープ上に割り当てられるオブジェクトのメンバである場合、`Rice::Address_Registration_Guard`を使用してそのオブジェクトをガベージコレクタに登録してください。

#### メンバ変数

Rubyオブジェクトを参照するクラスや構造体を作成する場合、カスタムの`ruby_mark`関数を実装する必要があります：

```cpp
class MyClass
{
  VALUE value_;
};

namespace Rice
{
  template<>
  ruby_mark(const MyClass* myClass)
  {
    rb_gc_mark(myClass->value_);
  }
}

Data_Type<MyClass> class = define_class<MyClass>("MyClass")
          .define_constructor(Constructor<MyClass>());
```

## 例外処理

Riceは自動的に例外を処理します。これにより、C++の例外がRubyコードに伝播せず、Rubyの例外がC++に伝播しないようにします。もしこれが起こると、プログラムはクラッシュします。

ただし、C++コードがRubyコードを呼び出し、そのRubyコードがさらにC++コードを呼び出す場合もあります。Riceはそのような状況に対処するのを簡単にしてくれます。以下でその方法を説明します。

### 例外の変換

RubyコードがC++の関数やメソッドを呼び出すか、C++の属性を読み書きする場合、Riceは例外ハンドラをインストールして、発生したC++の例外をキャッチします。そのハンドラはC++の例外をRubyの例外に変換し、再度発生させることで、呼び出し元のRubyコードで処理できるようにします。

C++の例外をRubyの例外にマッピングする方法を以下の表にまとめます：

| C++ 例外                                     | Ruby 例外                       |
|---------------------------------------------|---------------------------------|
| `std::bad_alloc`                            | `NoMemoryError`                 |
| `std::domain_error`                         | `FloatDomainError`              |
| `std::exception`                            | `RuntimeError`                  |
| `std::invalid_argument`                     | `ArgumentError`                 |
| `std::filesystem::filesystem_error`         | `IOError`                       |
| `std::length_error`                         | `RuntimeError`                  |
| `std::out_of_range`                         | `IndexError`                    |
| `std::overflow_error`                       | `RangeError`                    |
| `std::range_error`                          | `RangeError`                    |
| `std::regex_error`                          | `RegexpError`                   |
| `std::system_error`                         | `SystemCallError`               |
| `std::underflow_error`                      | `RangeError`                    |
| `Rice::Exception`                           | `RuntimeError`                  |
| その他の例外                                | `RuntimeError`                  |

`Rice::Exception`クラスは、Rice自体が例外を発生させる必要がある場合に使用するカスタム例外タイプです。

### カスタムハンドラ

Riceでは、カスタム例外ハンドラを登録することもできます。次のように行います：

```cpp
extern "C"
void Init_test()
{
  register_handler<MyException>(handle_my_exception);

  Data_Type<Test> rb_cTest =
    define_class<Test>("Test")
    .define_constructor(Constructor<Test>())
    .define_method("hello", &Test::hello)
    .define_method("error", &Test::error);
}
```

`handle_my_exception`は任意の型の例外を処理できます。例えば、C++の例外を投げることも可能です：

```cpp
void handle_my_exception(const MyException& ex)
{
  throw std::runtime_error(ex.what());
}
```

より便利な例として、C++の例外をRubyの例外に変換することがあります。これは`Rice::Exception`クラスを使用して行います：

```cpp
void handle_my_exception(const MyException& ex)
{
  throw Rice::Exception(rb_eRuntimeError, ex.what_without_backtrace());
}
```

### ハンドラの順序

例外ハンドラは登録された順に適用されます。したがって、ハンドラA、B、Cを登録すると、最初にAがチェックされ、次にB、最後にCがチェックされます。

例外ハンドラはグローバルであり、RubyがC++関数を呼び出したり、属性を読み書きするときに使用されます。また、`cpp_protect`を使用する場合にも適用されます（詳細は:ref:`c++_exceptions`を参照してください）。

### Rubyの例外

C++コードがRuby APIを呼び出す場合、Rubyの例外をキャッチするためにその呼び出しを保護する必要があります。Riceはこれを行うための`protect`メソッドを提供しています。例えば、カスタムC++クラスにEnumerableのサポートを追加するために`each`メソッドを実装したとします。`each`メソッドは、ユーザー指定のブロックに値を渡すために`rb_yield`を使用すべきです。しかし、`rb_yield`を直接呼び出してRubyコードが例外を発生させた場合、プログラムはクラッシュします。代わりに、`protect`関数を使用します：

```cpp
.define_method("each", [](T& vector) -> Object
{
  for (Value_T& item : vector)
  {
    VALUE element = detail::To_Ruby<Value_T>().convert(item);
    detail::protect(rb_yield, element);
  }

  return vector;
}
```

ほとんどのケースでは、`protect`メソッドは関数パラメータを呼び出されるRuby APIに正しくマップします。しかし、まれに、正しいパラメータタイプを推論するのを手助けする必要があります。詳細な例については、以下のC++例外セクションを参照してください。

RiceはRubyの`throw`/`catch`やRuby VM内部の他の非ローカルジャンプから投げられるシンボルを処理するために、`Jump_Tag`と呼ばれる同様のクラスを使用します。

### C++例外

C++コードがRuby APIを呼び出し、その後RubyコードがC++コードを呼び出す場合、潜在的なC++例外をキャッチする必要があります。これはあまり一般的ではありませんが、C++からRubyコレクションを繰り返すときに発生する可能性があります。例えば：

```cpp
static int convertPair(VALUE key, VALUE value, VALUE user_data)
{
  // マップを取得
  std::map<T, U>* result = (std::map<T, U>*)(user_data);

  // このメソッドはRubyから呼び出されているので、C++例外をRubyに伝播させてはいけません
  return cpp_protect([&]
  {
    result->operator[](From_Ruby<T>().convert(key)) = From_Ruby<U>().convert(value);
    return ST_CONTINUE;
  });
}

static std::map<T, U> createFromHash(VALUE value)
{
  std::map<T, U> result;
  VALUE user_data = (VALUE)(&result);

  // MSVCはここで助けが必要だが、g++は不要
  using Rb_Hash_ForEach_T = void(*)(VALUE, int(*)(VALUE, VALUE, VALUE), VALUE);
  detail::protect<Rb_Hash_ForEach_T>(rb_hash_foreach, value, convertPair, user_data);

  return result;
}
```

このコードは、Rubyのハッシュから新しい:ref:`std_map`を作成します。これを行うために、`rb_hash_foreach`を使用してハッシュを反復処理します。`rb_hash_foreach`関数はC++関数へのポインタを取ります。この場合、`convertPair`と呼ばれる関数です。これは、`protect`呼び出しが呼び出すメソッドの型を理解するために助けが必要なまれなケースです。この場合、`rb_hash_foreach`の関数シグネチャは`void(*)(VALUE, int(*)(VALUE, VALUE, VALUE), VALUE)`です。

ハッシュ内の各アイテムに対して、Rubyは`convertPair`関数を呼び出します。つまり、C++からRuby、そして再びC++に戻っています。`convertPair`関数は発生したC++例外をキャッチしなければなりません。それは、関数のコードを`cpp_protect`ラムダ内にラップすることで行われます：

```cpp
// このメソッドはRubyから呼び出されているので、C++例外をRubyに伝播させてはいけません
return cpp_protect([&]
{
  result->operator[](From_Ruby<T>().convert(key)) = From_Ruby<U>().convert(value);
  return ST_CONTINUE;
});
```

## タイプの概要

Riceの目的は、ネイティブC++コードとRubyコードが一緒に動作できるようにすることです。これには、両方の言語間でタイプを簡単に変換できるようにすることが必要です。

主に3つのユースケースがあります：

1. 両言語間でタイプを変換（コピー）すること（:doc:`Type conversion <type_conversions>`を参照）
2. RubyがC++コードにアクセスできるように、Rubyラッパーを介して提供すること（`define_class`、`define_enum`など）
3. C++がC++ラッパーを介してRubyコードにアクセスできるようにすること（:ref:`api`を参照）

:doc:`Type conversion <type_conversions>`は、ブール型や数値型などのプリミティブ型に対してはうまく機能します。例えば、C++の符号なし32ビット整数はRubyの`Fixnum`インスタンスにコピーされます（その逆も同様）。プリミティブ型の変換は、プログラマーにとってなじみがあるため理解しやすいです。ブール型や整数をメソッドに渡すとき、そのメソッドがそれを変更するとは考えないで、単にコピーを受け取ると考えます。

しかし、より複雑な型については、通常、型変換は意味を成しません。簡単なC++構造体のインスタンスをRubyにコピーしたいとはあまり思わないでしょうし、C++クラスのインスタンスをコピーしたいことはほとんどありません。その理由は多くありますが、いくつか挙げると：

- C++オブジェクトは、データベース接続や開いたファイルハンドルのようにコピーできない内部状態を持っているかもしれません。
- C++には、オブジェクトの生成、コピー、破棄の方法を制御する複雑なオブジェクトライフタイムルールがあり、これらはRubyには翻訳できません。
- C++オブジェクトは、例えば100万要素のベクターのように大量のメモリを使用する可能性があり、それをRubyにコピーするのは現実的ではありません。
- データをコピーすることは、定義により2つの別々のバージョンを作成することであり、2つの言語間でデータを共有することができなくなります。

その結果、より実用的なアプローチは、RubyがC++オブジェクトにアクセスし、C++がRubyオブジェクトにアクセスできるようにするための薄いラッパーを提供することです。Rubyラッパーは、他のドキュメントで説明されているように、`define_enum`、`define_class`などを介して作成されます。

最後に、C++からRubyオブジェクトを操作したい場合、RubyのC APIではなく、Riceのオブジェクト指向の:ref:`api`を使用することをお勧めします。

## タイプ変換

Riceでは、RubyとC++の間で変換（コピー）すべきタイプをビルトインタイプと呼びます。ビルトインタイプはC++からRubyに直接マップされるタイプです。例として、`nullptr`、`bool`、数値型（整数、浮動小数点、倍精度、複素数）、`char`型、文字列などがあります。

ビルトインタイプはコピーされるため、そのインスタンスは切り離されます。したがって、Rubyの文字列が`std::string`に変換される場合、2つの文字列は独立しており、一方の変更が他方に**反映されることはありません**。また、C++で新しい`char*`を割り当ててRubyに渡すと、Rubyは`char*`の内容をコピーしますが、元のバッファを解放しないため、メモリリークが発生します。

Riceはすべての一般的なビルトインタイプを標準でサポートしています。一般的に、新しいC++タイプをRubyに追加するには、`define_class`や`define_enum`などを使ってラップする必要があります。新しいビルトインタイプを追加するのはかなり珍しいケースです。

### ビルトインタイプの追加

例として、`std::deque<int>`をRubyに公開したいとしましょう。RiceのSTL（標準テンプレートライブラリ）サポートを使用せずにこれを行います。また、ラッパーを提供するのではなく、データを2つの言語間でコピーしたいとします。これを行うには、次のステップが必要です：

1. `Type`テンプレートを特殊化する
2. `To_Ruby`テンプレートを特殊化する
3. `From_Ruby`テンプレートを特殊化する

#### ステップ1 - Typeの特殊化

まず、`std::deque<int>`が既知のタイプであることをRiceに伝える必要があります。これにより、:doc:`タイプ検証 <type_verification>`を通過します。これは`Type`テンプレートを特殊化することで行います：

```cpp
namespace Rice::detail
{
  template<>
  struct Type<std::deque<int>>
  {
    static bool verify()
    {
      return true;
    }
  };
}
```

特殊化は**必ず**`Rice::detail`名前空間で行う必要があります。タイプにサブタイプが含まれている場合は、それらも検証することを忘れないでください。例として、`std::optional`の`verify`メソッドを示します：

```cpp
namespace Rice::detail
{
  template<typename T>
  struct Type<std::optional<T>>
  {
    static bool verify()
    {
      return Type<T>::verify();
    }
  };
}
```

`std::optional`は格納するタイプが有効な場合にのみ有効であることに注意してください。

#### ステップ2 - To_Rubyの特殊化

次に、`std::deque<int>`をRubyオブジェクトに変換するC++コードを書く必要があります。最も明らかなRubyオブジェクトのマッピング先は配列です。

```cpp
namespace Rice::detail
{
  template<>
  class To_Ruby<std::deque<int>>
  {
  public:
    VALUE convert(const std::deque<int>& deque)
    {
      // Rubyが例外を投げた場合に備えて、Ruby APIの呼び出しをprotectでラップしている点に注意
      // protectを使用せずにRubyが例外を投げると、プログラムは**クラッシュします**
      VALUE result = protect(rb_ary_new2, deque.size());

      for (int element : deque)
      {
        // C++のintをRubyの整数に変換
        VALUE value = To_Ruby<int>::convert(element);
        // Ruby配列に追加
        detail::protect(rb_ary_push, result, value);
      }
      return result;
    }
  };
}
```

定義は**必ず**`Rice::detail`名前空間で行う必要があります。

上記のような生のRuby C APIを使用する代わりに、Ruby配列のためのC++ラッパーを提供する`Rice::Array`を使用する方が良いでしょう。

#### ステップ3 - From_Rubyの特殊化

最後に、Ruby配列を`std::deque<int>`に変換するためのC++コードも記述する必要があります。

```cpp
namespace Rice::detail
{
  template<>
  class From_Ruby<std::deque<int>>
  {
  public:
    std::deque<int> convert(VALUE ary)
    {
      // 配列が本当に配列であることを確認します。そうでない場合、この呼び出しはRuby例外を投げるので、保護する必要があります
      detail::protect(rb_check_type, ary, (int)T_ARRAY);

      long size = protect(rb_array_len, ary);
      std::deque<int> result(size);

      for (long i = 0; i < size; i++)
      {
        // 配列要素を取得
        VALUE value = protect(rb_ary_entry, ary, i);

        // RubyのintをC++のintに変換
        int element = From_Ruby<int>::convert(value);

        // デックに追加
        result[i] = element;
      }

      return result;
    }
  };
}
```

通常通り、定義は**必ず**`Rice::detail`名前空間で行う必要があります。

### デフォルト引数のサポート

RiceはC++の:ref:`default_arguments`をサポートしています。このサポートをカスタムタイプで有効にするには、`From_Ruby`の特殊化に次の変更を加える必要があります：

- `detail::Arg`ポインタを受け取り、それをメンバ変数に格納する追加のコンストラクタを追加する
- デフォルトコンストラクタを追加する
- `convert`メソッドで、Rubyの値が`nil`（つまり`Qnil`）であり、`arg`が設定されている場合、デフォルト値を返す

上記の例を拡張すると：

```cpp
namespace Rice::detail
{
  template<>
  class From_Ruby<std::deque<int>>
  {
  public:
    From_Ruby() = default;

    explicit From_Ruby(Arg* arg) : arg_(arg)
    {
    }

    std::deque<int> convert(VALUE ary)
    {
      if (ary == Qnil && this->arg_ && this->arg_->hasDefaultValue())
      {
        return this->arg_->defaultValue<std::deque<int>>();
      }
      else
      {
        // .... 上記の例のコードと同じ
      }
    }

  private:
    Arg* arg_ = nullptr;
  };
}
```

## タイプ検証

Riceはタイプに非常に厳密です。定義されていないタイプに遭遇すると、起動時に例外を投げます。これはほとんどのC++開発者にとって驚くべきことではないでしょう。

例えば、次のような例を考えてみましょう：

```cpp
class MyClass
{
};

using Fancy_T = std::vector<std::unique_ptr<std::pair<std::string, MyClass>>>;

class Factory
{
  Fancy_T& make_fancy_type();
};

void setupRice()
{
  define_class<Factory>("Factory").
    define_method("make_fancy_type", &Factory::make_fancy_type);
}
```

上記のRice拡張がRubyにロードされると、例外が発生します。その理由は、`MyClass`がまだRiceに定義されていないからです。

`define_method`が呼び出されると、Riceは`&Factory::make_fancy_type`の戻り値のタイプが`Fancy_T`であることを確認します。次に、ベクターからユニークポインタ、ペア、そして`MyClass`へと掘り下げていきます。`MyClass`に到達したとき、Riceは内部の`TypeRegistry`をチェックし、まだ定義されていないことに気づいて`std::runtime_exception`を投げます。

これを修正するには、まず`MyClass`を次のように定義する必要があります：

```cpp
void setupRice()
{
  define_class<MyClass>("MyClass");

  define_class<Factory>("Factory").
    define_method("make_fancy_type", &Factory::make_fancy_type);
}
```

`MyClass`を定義することで、Riceは`TypeRegistry`にこのクラスが存在することを認識し、例外を避けることができます。

## 継承

Riceは、`define_class`メソッドを使用して子クラスを作成することをサポートしています：

```cpp
class Base
{
public:
  virtual void foo();
};

class Derived : public Base
{
};

extern "C"
void Init_test()
{
  Data_Type<Base> rb_cBase =
    define_class<Base>("Base")
    .define_method("foo", &Base::foo);

  Data_Type<Derived> rb_cDerived =
    define_class<Derived, Base>("Derived");
}
```

これは、`define_class`に2番目のテンプレートパラメータを追加することで行われます。このパラメータは、`Derived`が`Base`を継承していることを指定します。

Riceは多重継承をサポートしていません。

### ディレクター

ラップされたC++クラスから継承するRubyクラスを作成したい場合、継承はさらに複雑になります。これにはいくつかの問題があります：

- RubyクラスはC++の仮想メソッドをオーバーライドできる必要があります。
- オーバーライドされた仮想メソッドは`super`を呼び出して、オーバーライドされたC++メソッドを呼び出せる必要があります。
- C++コードが仮想メソッドを呼び出すとき、Rubyでオーバーライドされたバージョンを呼び出す必要があります。

Riceはこれらのユースケースを「ディレクター」クラスを使ってサポートします。ディレクターは、クラス階層を上下に正しくメソッド呼び出しをディスパッチできるプロキシです。

> **Note:** ディレクターという名前はSWIGに由来します。詳細は[SWIGのドキュメント](https://www.swig.org/Doc4.0/SWIGPlus.html#SWIGPlus_director_classes_introduction)を参照してください。

次のクラスを考えてみましょう：

```cpp
class VirtualBase
{
public:
  VirtualBase();
  virtual int doWork();
  virtual int processWorker() = 0;
};
```

このクラスの抽象的な性質のため、C++コンパイラは仮想クラスをインスタンス化しようとしてエラーを出すため、直接Riceでラップすることはできません。純粋仮想関数がなくても、`VirtualBase::doWork`の呼び出しはC++レベルで停止し、実行はRubyのサブクラスには渡りません。

これらのメソッドを適切にラップするには、プロキシとして`Rice::Director`サブクラスを使用し、この新しいプロキシクラスを`define_class`でラップするタイプとして使用します：

```cpp
#include <rice/rice.hpp>

class VirtualBaseProxy : public VirtualBase, public Rice::Director
{
public:
  VirtualBaseProxy(Object self) : Rice::Director(self)
  { }

  virtual int doWork()
  {
    int result = getSelf().call("do_work");
    return detail::From_Ruby<int>().convert(result);
  }

  int default_doWork()
  {
    return VirtualBase::doWork();
  }

  virtual int processWorker()
  {
    int result = getSelf().call("process_worker");
    return detail::From_Ruby<int>().convert(result);
  }

  int default_processWorker()
  {
    raisePureVirtual();
  }
};
```

ここでは多くのことが行われているので、それぞれの部分について説明します。

```cpp
class VirtualBaseProxy : public VirtualBase, public Rice::Director { }
```

まず、このクラスは対象の仮想クラスと`Rice::Director`の両方をサブクラス化する必要があります。

```cpp
public:
  VirtualBaseProxy(Object self) : Rice::Director(self) { }
```

`Rice::Director`がその魔法を発揮するには、このクラスの各インスタンスが対応するRubyインスタンスへのハンドルを持っている必要があります。コンストラクタは最初の引数として`Rice::Object`を受け取り、それを`Rice::Director`に渡す必要があります。

次に、`doWork`を実装します。ディレクタークラスはメソッド呼び出しをRubyインスタンスに転送することでオーバーライドします。

```cpp
virtual int doWork()
{
  int result = getSelf().call("do_work");
  return detail::From_Ruby<int>().convert(result);
}

int default_doWork()
{
  return VirtualBase::doWork();
}
```

また、`default_doWork`も実装しており、これによりRubyがオーバーライドされた仮想C++メソッドを呼び出すことができます。`default_`プレフィックスは、どのメソッドがどの機能を実行するかを明確にするための命名規則です。

RubyがC++メソッドを呼び出してはならない場合、`default_`の実装は`raisePureVirtual()`を呼び出す必要があります：

```cpp
int default_processWorker()
{
  raisePureVirtual();
}
```

メソッド`raisePureVirtual()`は、純粋仮想メソッドをRubyにラップできるようにするため（そしてコンパイルが可能であることを確認するため）に存在しますが、同時にこの拡張のユーザーに、C++側で呼び出せるものがない場合はすぐに通知します。

ディレクタークラスが構築されたら、それをRubyにラップします：

```cpp
extern "C"
void Init_virtual() {
  define_class<VirtualBase>("VirtualBase")
    .define_director<VirtualBaseProxy>()
    .define_constructor(Constructor<VirtualBaseProxy, Rice::Object>())
    .define_method("do_work", &VirtualBaseProxy::default_doWork)
    .define_method("process_worker", &VirtualBaseProxy::default_processWorker);
}
```

このコードには新しい点がいくつかあります。

1. `define_director`呼び出しが追加され、`VirtualBaseProxy`をテンプレートパラメータとして取ります。
2. `Constructor`テンプレートパラメータも`VirtualBaseProxy`である必要があり、派生オブジェクトの適切なオブジェクトの生成と破棄を可能にします。
3. `define_method`呼び出しは`default_*`実装を指す必要があります。


## インスタンスレジストリ

Rice 4.1では、RubyオブジェクトでラップされたC++オブジェクトを追跡するインスタンスレジストリが追加されました。これは、Riceによって維持されるグローバルな`std::map`を介して行われます。

### 有効化

インスタンスレジストリが有効な場合、RiceはC++インスタンスがRubyインスタンスによってラップされているかどうかをチェックします。ラップされている場合は、既存のRubyインスタンスが返されます。

デフォルトでは、インスタンストラッキングは無効になっています。これを有効にするには、次のように設定します：

```cpp
detail::Internal::instance_registry.isEnabled = true;
```

### 無効化

インスタンスレジストリが無効になっている場合、RiceはC++インスタンスがすでにRubyインスタンスによってラップされているかどうかに関係なく、新しいRubyインスタンスでC++インスタンスをラップします。したがって、参照またはポインタを介して毎回同じC++オブジェクトを返すC++メソッドに複数回呼び出すと、複数のラッピングされたRubyオブジェクトが作成されます。デフォルトでは、複数のRubyオブジェクトがC++オブジェクトをラップすることは問題ありません。なぜなら、RubyオブジェクトはC++オブジェクトを所有していないからです。詳細については、:ref:`cpp_to_ruby`のトピックをよくお読みください。

このルールには1つの例外があります。それは、C++メソッドが自分自身を返す場合です。Riceは、C++オブジェクトが呼び出しを行っているRubyオブジェクトによってラップされていることを認識し、したがって`self`を返しています（詳細は:ref:`return_self`を参照してください）。

### トラッキングが無効な理由

インスタンスレジストリを有効にすると、パフォーマンスが大幅に向上する可能性があります。トラッキングはわずかなオーバーヘッドを追加しますが、重複するRubyオブジェクトとC++ラッパーオブジェクトの作成を避けることができます。

しかし、トラッキングが完全に信頼できるかどうかは不明です。いくつかの潜在的な問題があります。

まず、実装はスレッドセーフではありません。RubyのGIL（グローバルインタープリタロック）のおかげで、これは問題と見なされていません。

次に、ペアはRubyオブジェクトがガベージコレクタによって解放されるときにグローバルマップから削除されます。Rubyオブジェクトが削除のためにマークされているが、基礎となるC++オブジェクトが再びRubyに返される可能性があるウィンドウが存在する可能性があります。その場合、Rubyオブジェクトは解放され、クラッシュが発生します。これは実際に発生するかどうかは不明ですが、観察されたことはありません。

第三に、RubyインスタンスによってラップされたC++インスタンスがC++側で解放される可能性があります。所有権のルールが正しく設定されている限り、これは問題ありません。しかし、削除されたC++オブジェクトと同じアドレスを持つ新しいC++インスタンスが作成され、それがRubyに渡されると、インスタンストラッカーは古い削除されたオブジェクトを返します。これはRiceのテストで観察されています。これがテストの書き方によるものなのか、より一般的な問題なのかは不明です。

## STL

RiceはC++の標準テンプレートライブラリ（STL）を部分的にサポートしています。このサポートを有効にするには、`rice/rice.hpp`の後に`rice/stl.hpp`ヘッダーファイルをインクルードする必要があります。

### 自動生成されるRubyクラス

STLクラスはテンプレートクラスであることを覚えておいてください。そのため、実際のC++クラスを定義するには具体的な型でインスタンス化する必要があります。例えば、`std::vector<int>`は`std::vector<std::string>`とは異なるクラスです。これにより、C++コードベースには多くのSTLインスタンス化クラスが存在する可能性があります。

これらのクラスを手動で定義する手間を避けるために、RiceはRubyラッパークラスを自動的に生成します。これらのRubyクラスは`Rice::Std`モジュールに追加されます。自動クラスは、RubyコードがC++で作成されたラップされたオブジェクトにアクセスしたり、それを変更したりする場合によく機能します。

ただし、Rubyがこれらのクラスの新しいインスタンスを作成する必要がある場合、自動クラス作成はあまりうまく機能しません。その理由は、生成されたクラス名がC++のランタイム型情報から生成されるため、非常に長くて見た目が悪くなるためです。例えば、型`std::pair<std::string, double>`の名前は次のようになります：

```ruby
Rice::Std::Pair__basic_string__char_char_traits__char___allocator__char_____double__
```

うわぁ！したがって、ルールオブサムは、Rubyからこれらのクラスのインスタンスを作成する必要がない場合は生成されたクラスを使用し、そうでない場合は手動で作成されたクラスを使用することです。

クラス名を手動で作成するのは簡単です。これを行う方法はSTLタイプごとに異なりますが、単純な命名規則に従います—`define_pair`、`define_vector`などです。各サポートされているSTLタイプのドキュメントを参照してください。

手動で作成されたクラス名は、自動生成されたクラスの*後に*定義することもできます。何が起こるかというと、クラスはまだ1つしかないが、それが複数の定数によって参照されることです。例えば、ペアが登録された後に`define_pair<std::pair<std::string, double>>(StringDoublePair)`を呼び出すと、Rubyではクラスを指す2つの定数があります：

```ruby
Rice::Std::Pair__basic_string__char_char_traits__char___allocator__char_____double__
Object::StringDoublePair
```

### std::complex

`std::string`と同様に、Riceは`std::complex`をビルトインタイプと見なします。つまり、`std::complex`の値はRubyの`Complex`インスタンスに変換され、その逆も同様です。

### std::map

`std::vector`の外では、`std::map`はC++で最も一般的に使用されるコンテナの1つです。`std::map`とRubyの`Hash`の間には直接的な概念的マッピングがありますが、Riceはマップをハッシュにコピーしません。代わりに、`std::map`をラップします。

これにはいくつかの理由があります：

- `std::map`のインスタンスは1つのキーと値のタイプしか含められませんが、Rubyのハッシュは異なるタイプのキーと値を含むことができます。
- `std::map`のインスタンスは非常に大きくなることがあります。
- `std::map`のインスタンスには、コピーやムーブセマンティクスが複雑なC++クラスが一般的に含まれます。
- C++とRubyで切り離されたデータのコピーが2つ存在するのは通常望ましくありません。

Riceは、見つかった`std::map`の各インスタンス化に対して、自動的にRubyクラスを定義します。また、`define_map`や`define_map_under`メソッドを使用してRubyクラスを手動で定義することもできます。ただし、Riceが自動的にそれらを作成する*前に*定義する必要があります。

例：

```cpp
std::map<std::string, int> makeStringIntMap()
{
   return std::map {{"one", 1}, {"two", 2}, {"three", 3}};
}

define_map<std::map<std::string, int>>("StringIntMap");
define_global_function("make_string_int_map", &makeStringIntMap);
```

このRubyクラスを定義したら、次のようにして新しいインスタンスを作成できます：

```ruby
map = StringIntMap.new
map["value 1"] = 1
map["value 2"] = 2
```

#### ハッシュからマップへ

マップ引数を取るC++メソッドの場合、Rubyから新しいマップをインスタンス化できます（詳細は:ref:`stl_class_names`を参照してください）。

例えば、次のC++コードを考えてみましょう：

```cpp
void passMap(std::map<std::string, int> stringIntMap)
{
}

define_map<std::map<std::string, int>>("StringIntMap");
define_global_function("pass_map", &passMap);
```

これをRubyから呼び出す1つの方法は次のとおりです：

```ruby
map = StringIntMap.new
map["thirty seven"] = 37
pass_map(map)
```

この場合、RubyはC++のマップをラップしています。したがって、C++でマップに行った変更はRubyに反映されます。

しかし、Rubyのハッシュを渡す方が便利な場合もあります。これは、特にRiceの:ref:`automatic <stl_class_names>` STLクラスを使用している場合に当てはまります。

したがって、Riceはこの使い方もサポートしています：

```ruby
hash = {"three" => 3, "five" => 5, "nine" => 9}
pass_map(hash)
```

この場合、RiceはRubyのハッシュを*コピー*します（ラップするのではなく）。したがって、C++で行った変更はRubyには反映されません。

#### Ruby API

Riceは、`std::map`をRubyのハッシュのように見せるために、`Hash`のサブセットとなるAPIを提供しようとしています。ただし、いくつかの違いがありますので、注意が必要です。

まず、以下のメソッドはマップのタイプがコピー可能である場合にのみ動作します（コピーはC++で行われます）：

- `Map#copy(other)`

次に、以下のメソッドはマップのタイプがC++の等号演算子 `operator==` を実装している場合にのみ動作します：

- `Map#value?`

最後に、マップのタイプがC++ストリームをサポートしている場合、次のメソッドは動作します。サポートしていない場合は「Not Printable」を返します：

- `Map#to_s`

### std::optional

C++17で導入された`std::optional`は、関数から設定されていない可能性のある値を返すためのC++コードのもう一つの方法を提供します（ポインタ以外に）。

Rubyにはこれに相当するタイプがないため、Riceは`std::optional`インスタンスをアンラップします。オプショナルが空である、つまり`std::nullopt`の値を持つ場合、RiceはこれをRubyの`nil`に変換します。オプショナルに値があり、その値がビルトインタイプである場合、それは適切なRubyタイプに変換されます。ビルトインタイプでない場合、その値はRubyによってラップされます。

Rubyインスタンスを`std::optional`に渡す際、Riceは`nil`の値を`std::nullopt`に、`nil`でない値を適切なC++タイプに変換します。

### std::pair

`std::pair`は、2つの値を関連付けるための簡単なコンテナを提供するC++のコードです。`std::map`や`std::unordered_map`は、キーとそれに関連する値を保持するために`std::pair`を使用します。

Rubyにはペアの概念がないため、Riceは`std::pair`をラップします。これにより、データがC++とRubyの間でコピーされることはありません。

`std::pair`は2つのタイプのテンプレートであるため、各`std::pair`のインスタンス化はそれぞれ独自のC++クラスであり、したがって独自のRubyクラスでもあります。ペアクラスを手動で定義することも、Riceに自動的に定義させることもできます。Rubyクラスを手動で定義するには、`define_pair`または`define_pair_under`メソッドを使用します。

例：

```cpp
std::pair<std::string, uint32_t> makeStringIntPair(std::string key, uint32_t value)
{
   return std::make_pair(key, value);
}

define_pair<std::pair<std::string, uint32_t>>("StringIntPair");
define_global_function("make_string_int_pair", &makeStringIntPair);
```

このRubyクラスを定義した後、新しいインスタンスを次のように作成できます：

```ruby
pair = StringIntPair.new("key 2", 33)
```

#### Ruby API

`std::pair`用に公開されているRuby APIは非常にわかりやすく、以下のメソッドで構成されています（ここでは、`Pair`という名前のRubyクラスを作成したと仮定しています）：

- `Pair#new(value1, value2)`
- `Pair#first`
- `Pair#first=(value)`
- `Pair#second`
- `Pair#second=(value)`

基になる`std::pair`がコピー可能なタイプを持っている場合（コピーはC++で行われます）、次のメソッドは動作します。そうでない場合は例外が発生します：

- `PairClass#copy(other)`

基になる`std::pair`がC++のストリームによってサポートされているタイプを持っている場合、次のメソッドは動作します。そうでない場合は「Not Printable」を返します：

- `PairClass#to_s`

### std::reference_wrapper

C++11で導入された`std::reference_wrapper`は、C++の参照をコピー可能で代入可能なオブジェクトにラップします。これにより、`std::vector`のようなコンテナや、`std::variant`のような他のタイプに格納することが可能になります。

Rubyにはこれに相当するタイプがないため、Riceは`std::reference_wrapper`インスタンスをアンラップします。`std::reference_wrapper`がビルトインタイプを指している場合、それは適切なRubyタイプに変換されます。ビルトインタイプではない場合、その値はRubyによってラップされます。

Rubyインスタンスを`std::reference_wrapper`に渡す際、RiceはRubyタイプを適切なC++タイプに変換し、それをラッパー内に格納します。これは危険である場合があります。というのも、Riceは参照を有効に保つために元のメモリ位置を保持する必要があるからです。ラップされたタイプの場合、これはラッピングされたRubyオブジェクトが有効であり、ガベージコレクションされない限り、参照が有効であることを意味します。ビルトインタイプについては、Riceはメソッド呼び出しのライフタイムを通じてのみ参照の有効性を保証します。したがって、呼び出されたC++コードが参照を格納し、後でそれを使用しようとすると、例外が発生します。

## スマートポインタ

スマートポインタは、現代のC++でメモリ安全なコードを書くための重要なツールです。Riceは`std::unique_ptr`と`std::shared_ptr`をサポートしています。また、Riceはカスタムスマートポインタタイプをサポートするように簡単に拡張できます。

### std::unique_ptr

ネイティブメソッドが`std::unique_ptr`を返すと、Riceはそれをムーブコンストラクタを介してコピーします。したがって、Riceは`std::unique_ptr`の所有権を取得し、その基礎となるオブジェクトの所有権も持ちます。

`std::unique_ptr`の使用はRubyコードに対して透過的です。Rubyから見れば、ユニークポインタによって管理されている型をラップしているだけなので、`std::unique_ptr::element_type`のようになります。その結果、`std::unique_ptr`自体に対するRubyから見えるAPIは存在しません。

Riceは`std::unique_ptr`を標準でサポートしており、追加作業は必要ありません。"rice/stl.hpp"ヘッダをインクルードするだけです。例を見てみましょう：

```cpp
class MyClass
{
public:
  int flag = 0;

public:
  void setFlag(int value)
  {
    this->flag = value;
  }
};

class Factory
{
public:
  std::unique_ptr<MyClass> transfer()
  {
    return std::make_unique<MyClass>();
  }
};

int extractFlagUniquePtrRef(std::unique_ptr<MyClass>& myClass)
{
  return myClass->flag;
}

void setupRice()
{
  define_class<MyClass>("MyClass").
    define_method("set_flag", &MyClass::setFlag);

  define_class<Factory>("Factory").
    define_constructor(Constructor<Factory>()).
    define_method("transfer", &Factory::transfer);

  define_global_function("extract_flag_unique_ptr_ref", &extractFlagUniquePtrRef);
}
```

Rubyでの使用例：

```ruby
factory = Factory.new
my_instance = factory.transfer
my_instance.set_flag(5)
flag = extract_flag_unique_ptr_ref(my_instance)
```

`my_instance`がスコープを外れ、ガベージコレクションされると、それがラップしている`std::unique_ptr`も解放され、それが管理しているC++の`MyClass`インスタンスも解放されます。

注意：Riceは、ネイティブ関数のパラメータとして`std::unique_ptr`を渡して、C++に所有権を戻すことをサポートしていません。つまり、`std::unique_ptr`がRubyに転送されると、Rubyによって解放されます。ただし、上記の例のように参照を返すことは可能です。`std::unique_ptr`の参照を渡すことは、その所有権を移さないことに注意してください。

### std::shared_ptr

ネイティブメソッドが`std::shared_ptr`を返すと、Riceはそれをムーブコンストラクタを介してコピーし、共有ポインタのインスタンスを1つ所有します。当然ながら、他のオブジェクトが所有する`std::shared_ptr`のコピーが存在する可能性があります。

`std::shared_ptr`の使用もRubyコードに対して透過的です。Rubyから見れば、共有ポインタによって管理されている型をラップしているだけなので、`std::shared_ptr::element_type`のようになります。その結果、`std::shared_ptr`自体に対するRubyから見えるAPIは存在しません。

Riceは`std::shared_ptr`を標準でサポートしており、追加作業は必要ありません。"rice/stl.hpp"ヘッダをインクルードするだけです。例を見てみましょう：

```cpp
class MyClass
{
public:
  int flag = 0;

public:
  void setFlag(int value)
  {
    this->flag = value;
  }
};

class Factory
{
public:
  std::shared_ptr<MyClass> share()
  {
    if (!instance_)
    {
      instance_ = std::make_shared<MyClass>();
    }
    return instance_;
  }

public:
  static inline std::shared_ptr<MyClass> instance_;
};

int extractFlagSharedPtr(std::shared_ptr<MyClass> myClass)
{
  return myClass->flag;
}

int extractFlagSharedPtrRef(std::shared_ptr<MyClass>& myClass)
{
  return myClass->flag;
}

void setupRice()
{
  embed_ruby();

  define_class<MyClass>("MyClass").
    define_method("set_flag", &MyClass::setFlag);

  define_class<Factory>("Factory").
    define_constructor(Constructor<Factory>()).
    define_method("share", &Factory::share);

  define_global_function("extract_flag_shared_ptr", &extractFlagSharedPtr);
  define_global_function("extract_flag_shared_ptr_ref", &extractFlagSharedPtrRef);
}
```

Rubyでの使用例：

```ruby
factory = Factory.new
my_instance = factory.share
my_instance.set_flag(5)
flag = extract_flag_shared_ptr(my_instance)
flag = extract_flag_shared_ptr_ref(my_instance)
```

`my_instance`がスコープを外れ、ガベージコレクションされると、それがラップしている`std::shared_ptr`も解放されます。それが基礎となるC++の`MyClass`インスタンスを解放するかどうかは、他の`std::shared_ptr`インスタンスがそれを管理しているかどうかによります。

`std::unique_ptr`とは異なり、関数パラメータを介して`std::shared_ptr`のコピーをネイティブコードに戻すことができます。ただし、RubyはラッパーRubyオブジェクトが解放されるまで、常に共有ポインタの1つのコピーを保持します。

### カスタムスマートポインタ

Riceは追加のスマートポインタタイプをサポートするように拡張することができます。まず、`stl/smart_ptr.hpp`を確認してください。これはスマートポインタを格納するために使用される以下のテンプレートクラスを定義しています：

```cpp
namespace Rice::detail
{
  template <template <typename, typename...> typename SmartPointer_T, typename...Arg_Ts>
  class WrapperSmartPointer : public Wrapper
  {
  public:
    WrapperSmartPointer(SmartPointer_T<Arg_Ts...>& data);
    void* get() override;
    SmartPointer_T<Arg_Ts...>& data();

  private:
    SmartPointer_T<Arg_Ts...> data_;
  };
}
```

スマートポインタがこのテンプレートクラスに収まると仮定すると、Riceに次の3つのことを伝える必要があります：

- それをRubyオブジェクトにラップする方法
- Rubyオブジェクトからそれを抽出する方法
- その管理対象タイプにアクセスする方法

まず、スマートポインタをどのようにラップするかをRiceに伝えます。`std::unique_ptr`の場合は次のようにします：

```cpp
namespace Rice::detail
{
  template <typename T>
  struct To_Ruby<std::unique_ptr<T>>
  {
    static VALUE convert(std::unique_ptr<T>& data, bool takeOwnership = true)
    {
      std::pair<VALUE, rb_data_type_t*> rubyTypeInfo = detail::Registries::instance.types.figureType<T>(*data);

      // Use custom wrapper type 
      using Wrapper_T = WrapperSmartPointer<std::unique_ptr, T>;
      return detail::wrap<std::unique_ptr<T>, Wrapper_T>(rubyTypeInfo.first, rubyTypeInfo.second, data, true);
    }
  };
}
```

最初に`detail::To_Ruby`をスマートポインタタイプに対して特殊化します。この場合、`std::unique_ptr`です。次に、ポインタをRiceの`TypeRegistry`に渡し、どのRubyクラスがC++クラスをラップしているかを確認します。その後、`Wrapper_T`クラスを`WrapperSmartPointer`からインスタンス化します。最後に、タイプ情報とポインタをヘルパーメソッドに渡し、まず`Wrapper_T`のインスタンスを作成し、次にそれを格納するRubyオブジェクトを作成します。

次のステップは、Rubyからスマートポインタをどのように抽出するかをRiceに伝えることです。

```cpp
namespace Rice::detail
{
  template <typename T>
  struct From_Ruby<std::unique_ptr<T>&>
  {
    static std::unique_ptr<T>& convert(VALUE value)
    {
      Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::ruby_data_type());

      using Wrapper_T = WrapperSmartPointer<std::unique_ptr, T>;
      Wrapper_T* smartWrapper = dynamic_cast<Wrapper_T*>(wrapper);
     

 if (!smartWrapper)
      {
        std::string message = "Invalid smart pointer wrapper";
        throw std::runtime_error(message.c_str());
      }
      return smartWrapper->data();
    }
  };
}
```

上記と同様に、スマートポインタタイプに対して`detail::From_Ruby`を特殊化します。この場合、`std::unique_ptr`です。

次に、ヘルパーメソッドを使用して、`detail::To_Ruby`メソッドで作成した`Wrapper`インスタンスへのポインタを取得します。注意すべき点は、`detail::Wrapper`はカスタムラッパータイプの基底クラスであるため、これを`Wrapper_T`にキャストする必要があることです。最後に、その`data()`メソッドを呼び出して、格納されたスマートポインタにアクセスします。

最後に、Riceにスマートポインタが含むタイプを検証する方法を伝える必要があります。これは次のようにして行います：

```cpp
namespace Rice::detail
{
  template<typename T>
  struct Type<std::unique_ptr<T>>
  {
    static bool verify()
    {
      return Type<T>::verify();
    }
  };
}
```

### std::string

おそらく`std::string`はC++標準テンプレートライブラリで最も一般的に使用されるクラスでしょう。Riceは`std::string`をビルトインタイプとして扱います。つまり、文字列はC++とRubyの間でコピーされます。したがって、`std::string`をRubyに渡してRuby側で変更しても、C++側にはその変更は反映されません（その逆も同様です）。

Rubyとは異なり、C++はエンコーディングのサポートが非常に少ないです。そのため、C++とRubyの間で文字列を正しく変換するのは推測ゲームのようなもので、正しくエンコードするかどうかはユーザー次第です。

Riceが`std::string`をRubyに変換する際、そのエンコーディングは`Encoding.default_external`で指定されたエンコーディングであると仮定されます。WindowsではおそらくUTF-8で、LinuxやMacOSではオペレーティングシステムのロケールに基づきます。外部エンコーディングが指定されていない場合、変換された文字列のエンコーディングはASCII-8BIT（Rubyの「エンコーディングがまったくない」という意味）になります。エンコーディングが間違っている場合は、Rubyで`String#force_encoding`を呼び出して修正する必要があります。

対照的に、RiceがRubyの文字列を`std::string`に変換する際には、単純に元の`char`バッファを`std::string`に渡してコピーします。したがって、C++に文字列を渡す前に、Rubyでエンコーディングが正しく設定されていることを確認するのは再びあなた次第です。

なお、Riceは`std::wstring`をサポートしていません。

### std::string_view

`std::string_view`は`char`のシーケンスへの読み取り専用の参照です。これにより、`std::string`をコピーするオーバーヘッドなしで文字列を渡す方法を提供します。

入力時、Riceは`std::string_view`をビルトインタイプとして扱います。つまり、C++の`std::string_view`が参照する`char`シーケンスの部分をRubyにコピーします。Riceがエンコーディングをどのように扱うかについては、:ref:`std_string`のドキュメントを参照してください。

出力時、RiceはRuby文字列の基礎となる`char`バッファを参照する`std::string_view`を作成します。注意：これは非常に**危険**です。Rubyの文字列は最終的にガベージコレクションされるか、コンパクションの一部として移動されるため、`char`バッファが無効になる可能性があります。この機能を使用する際は十分注意してください。

### std::unordered_map

`std::unordered_map`はC++のハッシュテーブルです。しかし、Riceは`unordered_map`をハッシュテーブルにコピーせず、ラップします。

これにはいくつかの理由があります：

- `std::unordered_map`のインスタンスは一種類のキーと値しか含められないのに対し、Rubyのハッシュは異なる種類のキーと値を含むことができます。
- `std::unordered_map`のインスタンスは非常に大きくなることがあります。
- `std::unordered_map`のインスタンスには、コピーやムーブセマンティクスが複雑なC++クラスが一般的に含まれます。
- データの2つの切り離されたコピー（C++とRubyでそれぞれ1つずつ）が存在することは通常望ましくありません。

Riceは見つけた各`std::unordered_map`のインスタンス化に対して、自動的にRubyクラスを定義します。また、`define_unordered_map`や`define_unordered_map_under`メソッドを使用してRubyクラスを手動で定義することもできます。ただし、Riceが自動的にそれらを作成する*前に*定義する必要があります。

例：

```cpp
std::unordered_map<std::string, int> makeStringIntMap()
{
   return std::unordered_map {{"one", 1}, {"two", 2}, {"three", 3}};
}

define_unordered_map<std::unordered_map<std::string, int>>("StringIntMap");
define_global_function("make_string_int_map", &makeStringIntMap);
```

このRubyクラスを定義した後、新しいインスタンスを次のように作成できます：

```ruby
map = StringIntMap.new
map["value 1"] = 1
map["value 2"] = 2
```

#### ハッシュからマップへ

マップ引数を取るC++メソッドの場合、Rubyから新しいマップをインスタンス化できます（詳細は:ref:`stl_class_names`を参照してください）。

例えば、次のC++コードを考えてみましょう：

```cpp
void passMap(std::unordered_map<std::string, int> stringIntMap)
{
}

define_unordered_map<std::unordered_map<std::string, int>>("StringIntMap");
define_global_function("pass_map", &passMap);
```

これをRubyから呼び出す1つの方法は次のとおりです：

```ruby
map = StringIntMap.new
map["thirty seven"] = 37
pass_map(map)
```

この場合、RubyはC++のマップをラップしています。したがって、C++でマップに行った変更はRubyに反映されます。

しかし、Rubyのハッシュを渡す方が便利な場合もあります。これは、特にRiceの:ref:`automatic <stl_class_names>` STLクラスを使用している場合に当てはまります。

したがって、Riceはこの使い方もサポートしています：

```ruby
hash = {"three" => 3, "five" => 5, "nine" => 9}
pass_map(hash)
```

この場合、RiceはRubyのハッシュを*コピー*します（ラップするのではなく）。したがって、C++で行った変更はRubyには反映されません。

#### Ruby API

Riceは`std::unordered_map`をRubyのハッシュのように見せるために、`Hash`のサブセットとなるAPIを提供しようとしています。ただし、いくつかの違いがありますので、注意が必要です。

まず、以下のメソッドはマップのタイプがコピー可能である場合にのみ動作します（コピーはC++で行われます）：

- `Map#copy(other)`

次に、以下のメソッドはマップのタイプがC++の等号演算子 `operator==` を実装している場合にのみ動作します：

- `Map#value?`

最後に、マップのタイプがC++ストリームをサポートしている場合、次のメソッドは動作します。サポートしていない場合は「Not Printable」を返します：

- `Map#to_s`

### std::variant

C++17で導入された`std::variant`は、型安全なユニオンです。`std::variant`は、サポートされているタイプの中から1つの値を保持できます。

Rubyの変数はどんなタイプの値でも指すことができるため、Rubyには`std::variant`に相当するタイプは必要ありませんし、存在しません。そのため、Riceは`std::variant`インスタンスをアンラップし、格納された値を適切なRubyタイプに変換します。

Rubyインスタンスを`std::variant`に渡す場合、RiceはRubyのタイプを適切なC++タイプに変換し、それをバリアントの中に格納します。

### std::vector

`std::string`と同様に、`std::vector`も多くのC++コードベースで多用される重要なクラスです。`std::vector`とRubyの`Array`には直接的な概念的対応がありますが、Riceはベクターを配列にコピーせず、代わりに`std::vector`をラップします。

これにはいくつかの理由があります：

- `std::vector`のインスタンスは1種類の型しか含められませんが、Rubyの配列は異なる型を含むことができます。
- `std::vector`のインスタンスは非常に大きくなることがあります。
- `std::vector`のインスタンスには、コピーやムーブセマンティクスが複雑なC++クラスが一般的に含まれます。
- データの2つの切り離されたコピー（1つはC++、もう1つはRuby）は通常望ましくありません。

Riceは見つけた各`std::vector`のインスタンス化に対して、自動的にRubyクラスを定義します。また、`define_vector`や`define_vector_under`メソッドを使用してRubyクラスを手動で定義することもできます。ただし、Riceが自動的にそれらを作成する*前に*定義する必要があります。

#### 例

```cpp
std::vector<std::string> makeStringVector()
{
   return std::vector {"one", "two", "three"};
}

define_vector<std::vector<std::string>>("StringVector");
define_global_function("make_string_vector", &makeStringVector);
```

このRubyクラスを定義した後、新しいインスタンスを次のように作成できます：

```ruby
vector = StringVector.new
vector.push("value 1")
vector.push("value 2")
```

#### 配列からベクターへ

ベクター引数を取るC++メソッドの場合、Rubyから新しいベクターをインスタンス化できます（詳細は:ref:`stl_class_names`を参照してください）。

例えば、次のC++コードを考えてみましょう：

```cpp
void passVector(std::vector<int> ints)
{
}

define_vector<std::vector<int>>("IntVector");
define_global_function("pass_vector", &passVector);
```

これをRubyから呼び出す1つの方法は次のとおりです：

```ruby
vector = IntVector.new
vector.push(37)
pass_vector(vector)
```

この場合、RubyはC++のベクターをラップしています。したがって、C++でベクターに行った変更はRubyに反映されます。

しかし、Rubyの配列を渡す方が便利な場合もあります。これは、特にRiceの:ref:`automatic <stl_class_names>` STLクラスを使用している場合に当てはまります。

したがって、Riceはこの使い方もサポートしています：

```ruby
array = [3, 5, 9]
pass_vector(array)
```

この場合、RiceはRubyの配列を*コピー*します（ラップするのではなく）。したがって、C++で行った変更はRubyには反映されません。

#### Ruby API

Riceは`std::vector`をRubyの配列のように見せるために、`Array`のサブセットとなるAPIを提供しようとしています。ただし、いくつかの違いがありますので、注意が必要です。

まず、以下のメソッドはベクターのタイプがコピー可能である場合にのみ動作します（コピーはC++で行われます）：

- `Vector#copy(other)`
- `Vector#resize`

次に、以下のメソッドはベクターのタイプがC++の等号演算子 `operator==` を実装している場合にのみ動作します：

- `Vector#delete`
- `Vector#include?`
- `Vector#index`

最後に、ベクターのタイプがC++ストリームをサポートしている場合、次のメソッドは動作します。サポートしていない場合は「Not Printable」を返します：

- `Vector#to_s`

## Ruby C++ API

Riceは、多くのビルトインRubyタイプのためのラッパーを提供しています。これには以下が含まれます：

- Object
- Module
- Class
- String
- Array
- Hash
- Struct
- Symbol
- Exception

Riceは可能な限りRubyのクラス階層を模倣しています。

例えば：

```cpp
Object object_id = obj.call("object_id");
std::cout << object_id << std::endl;

Class rb_cTest = define_class<Test>("Test");
Object object_id = rb_cTest.call("object_id");
std::cout << object_id << std::endl;
```

`Array`および`Hash`タイプは、STLコンテナをイテレートするのと同じ方法でイテレートすることもできます：

```cpp
Array a;
a.push(detail::To_Ruby<int>().convert(42));
a.push(detail::To_Ruby<int>().convert(43));
a.push(detail::To_Ruby<int>().convert(44));
Array::iterator it = a.begin();
Array::iterator end = a.end();
for(; it != end; ++it)
{
  std::cout << *it << std::endl;
}
```

STLのアルゴリズムも`Array`および`Hash`コンテナで期待通りに動作するはずです。

## 移行ガイド：バージョン3から4への変更点

以前のバージョンのRiceでは、`gem install`時にいくつかの初期コードのコンパイルが必要でした。これにより、HerokuやGitHub Actionsのような、適切な共有ライブラリやビルドシステムが許可されていない、または利用できないプラットフォームでRiceやRiceを使用するライブラリの利用が難しくなっていました。

Rice 4では、ヘッダーのみのライブラリに移行し、Riceを使用するライブラリがバイナリビルドを提供しやすくなりました。しかし、この移行には多くの作業が必要で、いくつかの後方互換性のない変更が導入されました。このページでは、Rice 3を使用している人がRice 4以降でライブラリを動作させるために必要な主な変更点を記載しています。

### #include <rice/rice.hpp>

最初の変更点は、Riceが単一の結合されたヘッダーファイルとして公開されるようになったことです。そのため、すべてのインクルードをこの1つに変更できます。他にはSTLラッパー定義を含むヘッダーファイルが1つあり、それを取得するには `#include <rice/stl.hpp>` を使用します。詳細については、:doc:`stl/stl`セクションを参照してください。

### to_ruby / from_ruby

最も顕著な後方互換性のない変更は、RubyとC++の間のタイプ変換の定義方法です。

Rice 3では、`to_ruby`および`from_ruby`メソッドを次のように定義していました：

```cpp
template<>
Foo from_ruby<Foo>(Rice::Object x)
{
  // ...
}

template<>
Rice::Object to_ruby<Foo>(Foo const & x)
{
  // ...
}
```

Rice 4では、これらの関数の構造が大幅に変更されました。これらのメソッドは、`convert`関数として構造体内で定義し、`Rice::detail`名前空間内に配置する必要があります：

```cpp
namespace Rice::detail
{
  template<>
  class From_Ruby<Foo>
  {
    Foo convert(VALUE x)
    {
      // ...
    }
  };

  template<>
  class To_Ruby<Foo>
  {
    VALUE convert(Foo const & x)
    {
      // ...
    }
  };
}
```

さらに、これらの関数はRiceの`Object`型ではなく、Rubyの`VALUE`型を使用します。この変更は、C++とRubyの間でオブジェクトを変換する際に余分なコピーを作成しないようにするために行われました。詳細については、:doc:`type conversion <bindings/type_conversions>`セクションを参照してください。

### 関数とメソッドの違い

Riceでは、オブジェクト上のメソッドを定義するか通常の関数を定義するかに応じて異なる`define_`メソッドを使用します。`self`が必要な場合は、`define_method`または`define_singleton_method`を使用します。そうでない場合は、`define_function`および`define_singleton_function`を使用する必要があります。詳細は:ref:`the tutorial <Defining Methods>`で読むことができます。

### デフォルト引数

RiceがC++の:ref:`default_arguments`を定義する方法が微妙に変更されました。これは見逃しやすく、バグ（デフォルト値が正しく設定されない）を引き起こす可能性があります。以前はRiceはC++のカンマ演算子に依存して引数を組み合わせていました：

```cpp
define_method("hello", &Test::hello, (Arg("hello"), Arg("second") = "world"));
```

2つの`Args`が括弧で囲まれていることに注意してください。Riceはこのスタイルをサポートしなくなりました。代わりに、より自然な方法で括弧なしで`Args`を渡します：

```cpp
define_method("hello", &Test::hello, Arg("hello"), Arg("second") = "world");
```

### メモリ管理

Rice 4では、Rubyのガベージコレクタによって管理されるべきオブジェクトを明示的に定義するためのツールをさらに提供しています。詳細は:ref:`Memory Management`をお読みください。

## Why Rice?

### Motivation

RubyのためのCまたはC++拡張を作成する際に、いくつかの共通の問題があります：

**型の安全性**。IDやVALUEのような整数型を混同しやすいです。Ruby APIの関数の中には、受け取る型が一貫していないものもあります（例：`rb_const_defined`はIDを取り、`rb_mod_remove_const`はシンボルを取る）。

**DRY（Don't Repeat Yourself）原則**。ラップされた関数が受け取る引数の数を指定するのは間違いやすいです。関数に新しい引数を追加すると、`rb_define_method`に渡す引数の数も更新する必要があります。

**型変換**。データをRubyの型に変換するための関数は多数あり、その多くは異なるセマンティクスや形式を持っています。例えば、文字列を変換するには`StringValue`マクロを使うかもしれませんが、整数を変換するには`FIX2INT`を使います。以前にラップされたCデータをアンラップするにはさらに別の形式を使用します。

**例外の安全性**。C++の例外がCコードに到達しないようにすること、およびRubyの例外が非自明なデストラクタを持つオブジェクトがスタック上にある間に逃げないようにすることが重要です。どの例外をいつ使って良いかのルールは、特にコードが時間をかけて維持されると、正しくするのが難しいです。

**スレッドの安全性**。Rubyインタプリタはスレッドセーフではないため、複数のスレッドから実行してはいけません。GCやスケジューラがCスタックで行うトリックのため、あるスレッドでインタプリタが一度実行されたら、将来的にはそのスレッドからのみ実行されるべきです。また、Rubyがスレッドを切り替える際にスタックをコピーするため、あるRubyスレッドで作成されたオブジェクトを別のRubyスレッドでアクセスしないようにする必要があります。

**CベースのAPI**。RubyのAPIは、HashやArrayなどのRubyデータ構造にアクセスする際に必ずしも便利ではありません。特にC++コードを書くときには、これらのコンテナのインターフェースが標準のコンテナと一貫していないためです。

**呼び出し規約**。Ruby APIに渡される関数ポインタはCの呼び出し規約に従わなければなりません。これにより、テンプレート関数や静的メンバ関数へのポインタを渡すことができなくなります（つまり、いくつかのプラットフォームでは動作しますが、移植性はありません）。

**継承**。C++オブジェクトをラップするとき、派生クラスへのポインタを格納するのは簡単ですが、基底クラスのメソッドはオブジェクトをアンラップするために派生クラスについての知識を持たなければなりません。基底クラスへのポインタを常に格納し、必要に応じて派生型へのポインタをdynamic_castすることも可能ですが、これは遅くて面倒であり、複数の継承にはうまく機能しない可能性があります。すべての隅々まで継承を適切に処理するシステムは簡単ではありません。

**複数継承**。C++は真の複数継承をサポートしていますが、Rubyのオブジェクトモデルはミックスインを用いた単一継承を使用しています。複数継承を使用するライブラリをラップする際には、マッピングを構築する際に注意が必要です。

**GCの安全性**。すべてのライブRubyオブジェクトは、ガベージコレクタのマークフェーズ中にマークされなければなりません。そうでないと、早期に破棄されてしまいます。一般的なルールとして、ヒープ上に保存されているオブジェクトの参照は`rb_gc_register_address`で登録するか、データオブジェクトのマーク関数でマークするべきです。スタック上に保存されているオブジェクトの参照は、Rubyインタプリタが適切に初期化されていれば、自動的にマークされます。

**コールバック**。Cは関数ポインタを通じてコールバックを実装しますが、Rubyは通常プロックを通じてコールバックを実装します。プロックを呼び出すアダプタ関数を書くのは難しくありませんが、エラーの機会が多くあります（特に例外の安全性に関して）。

**データのシリアル化**。デフォルトでは、Cレイヤーで定義されたデータオブジェクトはマシュアラブルではありません。ユーザーはメンバーごとにデータをシリアル化する関数を明示的に定義しなければなりません。

Riceはこれらの問題に対して多くの方法で対応しています：

- **型の安全性**：RiceはObject、Identifier、Class、Module、Stringなどのすべてのビルトイン型をカプセル化します。オブジェクトのインスタンスをラップする前に動的型を自動的にチェックします。
- **DRY原則**：Riceはテンプレートと関数オーバーロードを使用して、関数の引数の数と型を自動的に決定します。ただし、デフォルト引数は明示的に処理する必要があります。
- **型変換**：Riceは`to_ruby<>`と`from_ruby<>`のキャストスタイルのテンプレート関数を提供し、明示的な型変換を簡素化します。すべてのラップされた関数のパラメータと戻り値の自動型変換が生成されます。
- **例外の安全性**：Riceは一般的な例外を自動的に変換し、ユーザー定義の例外タイプを変換するためのメカニズムを提供します。Riceはまた、Rubyコードにコールバックする際に例外を変換するための便利な関数も提供します。
- **スレッドの安全性**：Riceはスレッドの安全性を扱うためのメカニズムを提供していません。多くの一般的なスレッドの安全性の問題は、POSIXスレッドをサポートするYARVによって軽減されるはずです。
- **C++ベースのAPI**：RiceはRuby C APIの多くの共通関数に対してオブジェクト指向のC++スタイルのAPIを提供します。
- **呼び出し規約**：Riceは、Ruby APIに渡されるすべての関数ポインタに対してCの呼び出し規約を自動的に使用します。
- **継承**：Riceは、基底クラスでラップされたメンバー関数が呼び出されたときに、基底クラスの型に自動的に変換を行います。
- **複数継承**：Riceは複数継承のメカニズムを提供していません。複数継承はミックスインを通じてシミュレートすることができますが、これはまだ十分に簡単ではありません。
- **GCの安全性**：Riceはガベージコレクタと対話するためのいくつかの便利なクラスを提供します。オブジェクトが適切に破棄されるようにするために従わなければならない基本的なルールはまだあります。
- **コールバック**：Riceはコールバックを処理するためのいくつかの便利なクラスを提供します。
- **データのシリアル化**：Riceはデータシリアル化のメカニズムを提供していませんが、将来のリリースで追加される可能性があります。

### History

Riceは、サウスカロライナ州マウントプレザントにあるAutomated Trading DeskでC++ベースのトレーディングソフトウェアとインターフェースするためのプロジェクトとしてExcrubyとして始

まりました。当時、SwigのRubyバインディングは現在ほど成熟しておらず、プロジェクトのニーズに適していませんでした。

ExcrubyはRuby APIのラッパーとしてではなく、例外安全な方法でRubyインタプリタとインターフェースするためのヘルパー関数とクラスのセットとして書かれました。5年間の間に、プロジェクトはAPIの一部のラッパーに成長しましたが、元のヘルパー関数はパブリックインターフェースの一部として残っていました。

これはライブラリのユーザーに混乱を招きました。なぜなら、ほとんどのタスクを達成するための複数の方法があったからです。直接C APIを使う方法、C APIの低レベルラッパーを使う方法、そして低レベルのインターフェースの高レベル抽象化を使う方法です。

そこでRiceはインターフェースを整理する試みとして誕生しました。Riceは低レベルのラッパーを保持していますが、それは実装の詳細としてです。パブリックインターフェースは真にRuby C APIの高レベルの抽象化です。