Rice を使用して Ruby の拡張機能を C++ で作成する

Ruby に新しいプログラミング拡張機能を追加する

Ruby ネイティブの C API に対する Rice インターフェースを使用して、Ruby プログラミング言語を拡張する方法を学びましょう。このオブジェクト指向のインターフェースを使用すると、C++ のコードで Ruby のオブジェクトを作成したり、Ruby と C++ との間でデータ・オブジェクトを交換したりすることができます。

Arpan Sen, Author, Independent

Arpan Sen は電子設計自動化業界でソフトウェア開発に従事する先進的なエンジニアです。彼はこれまで、Solaris や SunOS、HP-UX、IRIX など、さまざまな種類の UNIX を扱ってきており、また Linux と Microsoft Windows にも数年の経験があります。彼はソフトウェアのパフォーマンス最適化技術やグラフ理論、並列コンピューティングに大きな関心を持っています。彼はソフトウェア・システムの修士号を取得しています。


developerWorks 貢献著者レベル

2012年 8月 09日

Ruby の最も優れた機能の 1 つは、C/C++ で定義された API (Application Programming Interface) を使用して Ruby を拡張できることです。Ruby には、Ruby のクラスやモジュール等々を作成するためのあらゆる関数を含む C ヘッダー、ruby.h が用意されています。Ruby が提供するヘッダー以外にも、ネイティブの ruby.h を使用して構築された、いくつかの上位レベルの抽象化を利用することで、Ruby を拡張することができます。この記事では、そうした抽象化の一例として、Rice (Ruby Interface for C++ Extensions) について調べます。

Ruby の拡張機能を作成する

Ruby の C API、つまり Rice 拡張機能に取りかかる前に、拡張機能を作成するための標準的なプロセスを明確にしておきたいと思います。

  1. 共有ライブラリーを作成する元となる 1 つまたは複数の C/C++ ソースがあることが前提です。
  2. Rice を使用して拡張機能を作成する場合、そのコードを libruby.a と librice.a の両方にリンクさせる必要があります。
  3. 共有ライブラリーを何らかのフォルダーにコピーし、そのフォルダーを RUBYLIB 環境変数の一部として指定します。
  4. Interactive Ruby (irb) プロンプト、または Ruby スクリプトで、おなじみの require を使用してロードします。rubytest.so という共有ライブラリーの場合であれば、単純に「require 'rubytest'」と入力すると共有ライブラリーがロードされます。

ここで仮に、ruby.h ヘッダーが /usr/lib/ruby/1.8/include にあり、Rice ヘッダーが /usr/local/include/rice/include にあり、拡張機能のコードが rubytest.cpp ファイルの中にあるとします。リスト 1 はこのコードをコンパイルしてロードするための方法です。

リスト 1. Ruby の拡張機能をコンパイルしてロードする
bash# g++ -c rubytest.cpp –g –Wall -I/usr/lib/ruby/1.8/include  \
    -I/usr/local/include/rice/include
bash# g++ -shared –o rubytest.so rubytest.o -L/usr/lib/ruby/1.8/lib \
    -L/usr/local/lib/rice/lib  -lruby –lrice –ldl  -lpthread
bash# cp rubytest.so /opt/test
bash# export RUBYLIB=$RUBYLIB:/opt/test
bash# irb
irb> require 'rubytest'
=> true

Hello World プログラム

Rice を使用して最初の Hello World プログラムを作成する準備ができたので、Rice API を使用して Test というクラスを作成します。このクラスには文字列 “Hello, World!” を表示する hello メソッドがあります。Ruby インタープリターは拡張機能をロードすると、Init_<共有ライブラリー名> 関数を呼び出します。リスト 1rubytest 拡張機能の場合、この関数を呼び出すということは rubytest.cpp に関数 Init_rubytest が定義されていることです。Rice を使用すると、define_class という API を使用して独自のクラスを定義することができます。これらの内容が含まれるコードがリスト 2 です。

リスト 2. Rice API を使用してクラスを作成する
#include "rice/Class.hpp"
extern "C"
void Init_rubytest( ) { 
  Class tmp_ = define_class("Test");
}

リスト 2 のコードをコンパイルし、irb でロードしてテストすると、リスト 3のような出力が得られるはずです。

リスト 3. Rice を使用して作成したクラスをテストする
irb> require ‘rubytest’
=> true
irb> a = Test.new
=> #<Test:0x1084a3928>
irb> a.methods
=> ["inspect", "tap", "clone", "public_methods", "__send__", 
      "instance_variable_defined?", "equal?", "freeze", …]

inspect など、定義済みのクラス・メソッドがいくつかあることに注意してください。これは、ここで定義した Test クラスが暗黙的に Object クラスから派生しているためです (すべての Ruby のクラスは Object から派生しています。実際、Ruby では、(数値を含む) すべてのものは Object を基底クラスとするオブジェクトです)。

今度は Test クラスにメソッドを追加します。そのコードがリスト 4 です。

リスト 4. Test クラスにメソッドを追加する
void hello() {
   std::cout << "Hello World!";
}
extern "C"
 void Init_rubytest() {
      Class test_ = define_class("Test")
         .define_method("hello", &hello);
}

リスト 4define_method API を使用して Test クラスにメソッドを追加しています。define_classClass 型のオブジェクトを返す関数であること、define_methodClass の基底クラスである Module_Impl クラスのメンバー関数であることに注意してください。すべてが実際に適切であるかどうかを検証するための Ruby のテストを以下に示します。

irb> require ‘rubytest’
=> true
irb> Test.new.hello
Hello, World!
=> nil

Ruby から C/C++ のコードに引数を渡す

これで Hello World プログラムの準備ができたので、Ruby から hello 関数に引数を渡し、hello 関数によってその引数を標準出力 (sdtout) に表示してみましょう。そのための最も簡単な方法は、以下のように単に hello 関数に文字列の引数を追加する方法です。

void hello(std::string args) {
   std::cout << args << std::endl;
}
extern "C"
 void Init_rubytest() {
      Class test_ = define_class("Test")
         .define_method("hello", &hello);
}

Ruby の世界では、以下のようにして hello 関数を呼び出します。

irb> a = Test.new
<Test:0x0145e42112>
irb> a.hello "Hello World in Ruby"
Hello World in Ruby
=> nil

Rice を使用することによる最大のメリットは、Ruby の文字列を std::string に変換するために何も特別なことをする必要がないことです。

では、hello 関数の中で文字列の配列を使用して、Ruby から C++ のコードに情報を渡す方法について調べてみましょう。そのための最も簡単な方法は、Rice に用意されている Array データ型を使用する方法です。rice/Array.hpp ヘッダーに定義される Rice::Array を使う方法は、STL (Standard Template Library) コンテナーを使う場合と似ています。通常の STL スタイルのイテレーターなどは Array インターフェースの一部として定義されます。リスト 5 は Rice の Array を引数に取る count ルーチンを示しています。

リスト 5. Ruby の配列を表示する
#include "rice/Array.hpp"

void Array_Print (Array a)   {
      Array::iterator aI = a.begin();
      Array::iterator aE = a.end();
      while (aI != aE) {
        std::cout << "Array has " << *aI << std::endl;
        ++aI;
      }
  }

ここで、このソリューションの素晴らしさを説明しましょう。例えば、Array_Print の引数として std::vector<std::string> を渡すとすると、Ruby は以下のようなエラーをスローします。

>> t = Test.new
=> #<Test:0x100494688>
>> t.Array_Print ["g", "ggh1", "hh1"]
ArgumentError: Unable to convert Array to std::vector<std::string, 
    std::allocator<std::string> >
	from (irb):3:in `hello'
	from (irb):3

しかし上記の Array_Print ルーチンでわかるように、Ruby の配列から C++ の Array 型への変換を Rice が行ってくれます。実際に実行した例を以下に示します。

>> t = Test.new
=> #<Test:0x100494688>
>>  t.Array_Print ["hello", "world", "ruby"]
Array has hello
Array has world
Array has ruby
=> nil

今度は逆に、C++ から Ruby の世界に配列を渡してみましょう。Ruby では配列要素の型が必ずしも同じであるとは限らないことに注意してください。リスト 6 はそのコードです。

リスト 6. C++ から Ruby に配列を渡す
#include "rice/String.hpp"
#include "rice/Array.hpp"
using namespace rice; 

Array return_array (Array a)  {
      Array tmp_;
      tmp_.push(1);
      tmp_.push(2.3);
      tmp_.push(String("hello"));
      return tmp_;
 }

リスト 6 から、異なる型を持つ Ruby の配列を C++ の中で作成できることが明確にわかります。以下は Ruby で行ったテストのコードとその結果です。

>> x = t.return_array
=> [1, 2.3, "hello"]
>> x[0].class
=> Fixnum
>> x[1].class
=> Float
>> x[2].class
=> String

C++ の引数リストを柔軟に変更することができない場合

皆さんは、シグニチャーを変更できない C++ の関数に合わせてデータを変換するように Ruby のインターフェースが作られていることに、よくお気付きのことと思います。例えば、文字列の配列を Ruby から C++ に渡さなければならない場合を考えてみてください。C++ の関数のシグニチャーは以下のようなものです。

void print_array(std::vector<std::string> args)

ここで求めているものは、実質的には Ruby の配列を引数として取り、それを std::vector<std::string> に変換する何らかの from_ruby 関数です。Rice は、まさにその関数、つまり以下のシグニチャーを持つ from_ruby 関数を提供します。

template <typename T>
T from_ruby(Object );

C++ の型への変換が必要な Ruby のデータ型すべてに対し、専用の from_ruby ルーチンをテンプレート化する必要があります。例えば Ruby の配列を上記の処理関数に渡す場合には、リスト 7 のように from_ruby 関数を定義する必要があります。

リスト 7. Ruby の配列を std::vector<std::string> に変換する
template<>
std::vector<std::string> from_ruby< std::vector<std::string> > (Object o)   {
    Array a(o);
    std::vector<std::string> v;
    for(Array::iterator aI = a.begin(); aI != a.end(); ++aI)
        v.push_back(((String)*aI).str());
    return v;
    }

from_ruby 関数を明示的に呼び出す必要がないことに注意してください。Ruby の世界から関数の引数として string の配列が渡されると、その配列は from_ruby によって std::vector<std::string> に変換されます。ただしリスト 7 のコードは完璧ではありません。既に説明したとおり、Ruby の配列は異なる型を持つ場合があるからです。一方でリスト 7 では ((String)*aI).str() を呼び出して Rice::String から std::string を取得しています (strRice::String のメソッドです。詳細については String.hpp を調べてください)。最も一般的なケースを扱えるようにするのであれば、そのコードはリスト 8 のようになります。

リスト 8. Ruby の配列を std::vector<std::string> に変換する (一般的なケース)
template<>
std::vector<std::string> from_ruby< std::vector<std::string> > (Object o)   {
    Array a(o);
    std::vector<std::string> v;
    for(Array::iterator aI = a.begin(); aI != a.end(); ++aI)
        v.push_back(from_ruby<std::string> (*aI));
    return v;
    }

Ruby の配列の各要素は String 型の Ruby のオブジェクトでもあり、String 型を std::string に変換する from_ruby メソッドが Rice に定義されているはずなので、何もする必要がありません。それ以外の場合には、変換のための from_ruby メソッドを用意する必要があります。以下は Rice のソースの to_from_ruby.ipp に含まれている from_ruby メソッドです。

template<>
inline std::string from_ruby<std::string>(Rice::Object x) {
  return Rice::String(x).str();
}

Ruby の世界からこのコードを試してみてください。まず、すべて文字列で構成される配列を渡します (リスト 9)。

リスト 9. from_ruby の機能を検証する
>> t = Test.new
=> #<Test:0x10e71c5c8>
>> t.print_array ["aa", "bb"]
aa bb
=> nil
>> t.print_array ["aa", "bb", 111]
TypeError: wrong argument type Fixnum (expected String)
	from (irb):4:in `print_array'
	from (irb):4

期待したとおり、最初の print_array の呼び出しは問題ありませんでした。Fixnumstd::string に変換する from_ruby メソッドは存在しないため、2 番目の呼び出しでは Ruby インタープリターが TypeError をスローしています。このエラーの修正方法はいくつかあり、例えば Ruby を呼び出す際に配列の一部として文字列のみを渡す方法 (t.print_array["aa", "bb", 111.to_s] など) や、C++ のコードの中で Object.to_s を呼び出す方法などがあります。to_s メソッドは Rice::Object インターフェースの一部であり、Rice::String を返します。Rice::String には、std::string を返す定義済みの str メソッドがあります。リスト 10C++ の方法を使用しています。

リスト 10. Object.to_s を使用して文字列のベクターに値を追加する
template<>
std::vector<std::string> from_ruby< std::vector<std::string> > (Object o)   {
    Array a(o);
    std::vector<std::string> v;
    for(Array::iterator aI = a.begin(); aI != a.end(); ++aI)
        v.push_back(aI->to_s().str());
    return v;
    }

一般に、リスト 10 のコードはユーザー定義のクラスに対するカスタムの文字列表現を扱う必要があるため、もっと複雑になります。


変数を持つ完全なクラスを C++ を使用して作成する

ここまでで、Ruby のクラスとそれに関連する関数を C++ のコードの中で作成する方法を説明しましたが、もっと一般的なクラスの場合には、インスタンス変数を定義する手段が必要であり、また initialize メソッドを用意する必要があります。Ruby のオブジェクトのインスタンス変数の値を設定したり、取得したりするためには、それぞれ Rice::Object::iv_set メソッドと Rice::Object::iv_get メソッドを使用します。そのコードをリスト 11 に示します。

リスト 11. initialize メソッドを C++ で定義する
void init(Object self) {
      self.iv_set("@intvar", 121);
      self.iv_set("@stringvar", String("testing"));
 }
Class cTest = define_class("Test").
                          define_method("initialize", &init);

define_method API を使用して C++ の関数を Ruby のクラスのメソッドとして宣言する場合、C++ の関数の最初の引数を Object として宣言することができます。その場合、Ruby はこの Object に対し、呼び出し側のインスタンスへの参照を指定します。すると、この Objectiv_set を呼び出すことで、インスタンス変数を設定することができます。このインターフェースを Ruby の世界で表現すると以下のようになります。

>> require 'rubytest'
=> true
>> t = Test.new
=> #<Test:0x1010fe400 @stringvar="testing", @intvar=121>

同様に、インスタンス変数を返すためには、その変数を返す関数は Ruby のオブジェクトを参照する Object を引数に取り、その Objectiv_get を呼び出す必要があります。そのスニペットをリスト 12 に示します。

リスト 12. Ruby のオブジェクトから値を取得する
void init(Object self) {
      self.iv_set("@intvar", 121);
      self.iv_set("@stringvar", String("testing"));
 }
int getvalue(Object self) { 
    return self.iv_get("@intvar");
}
Class cTest = define_class("Test").
                          define_method("initialize", &init).
                          define_method("getint", &getvalue);

C++ のクラスを Ruby の型に変更する

ここまでの説明では、任意の関数 (つまりクラスのメソッドではない関数) を Ruby のクラスのメソッドとしてラップしてきました。つまり 1 つ目の引数として Object を持つC 関数を宣言することで、Ruby のオブジェクトに対する参照を渡しました。この方法でもよいのですが、C++ のクラスを Ruby のオブジェクトとしてラップするのみでは十分ではありません。C++ のクラスをラップする場合にも define_class メソッドを使用しますが、この場合は C++ タイプのクラスを使用して define_class メソッドをテンプレート化する点が異なります。リスト 13 のコードは C++ のクラスを Ruby の型としてラップしています。

リスト 13. C++ のクラスを Ruby の型としてラップする
class cppType {
    public:
      void print(String args) {
        std::cout << args.str() << endl;
      }
};
Class rb_cTest =
        define_class<cppType>("Test")
         .define_method("print", &cppType::print);

今説明したように、define_class がテンプレート化されていることに注意してください。ただし、このクラスのすべてが適切というわけではありません。以下に示すのは Test 型のオブジェクトをインスタンス化しようとした場合のRuby インタープリターのログの一部です。

>> t = Test.new
TypeError: allocator undefined for Test
	from (irb):3:in `new'
	from (irb):3

何が起きたのかというと、Ruby の型に明示的にコンストラクターをバインドする必要があったのです (これは Rice の予想外の動作の 1 つです)。Rice には、C++ タイプのクラスにコンストラクターを関連付ける define_constructor メソッドがあります。このメソッドを使用するには、Constructor.hpp ヘッダーをインクルードする必要があります。コード内にコンストラクターが明示的に含まれているわけではない場合にも Constructor.hpp ヘッダーをインクルードする必要があることに注意してください。リスト 14 はそのコードの例です。

リスト 14. C++ のコンストラクターを Ruby の型と関連付ける
#include "rice/Constructor.hpp"
#include "rice/String.hpp"
class cppType {
    public:
    void print(String args) {
        std::cout << args.str() << endl;
      }
    };

Class rb_cTest =
        define_class<cppType>("Test")
         .define_constructor(Constructor<cppType>())
        .define_method("print", &cppType::print);

また、define_constructor メソッドを使用して引数リストにコンストラクターを関連付けることもできます。それを Rice で行うためには、引数の型をテンプレート・リストに追加します。例えば、整数を引数に取るコンストラクターが cppType にある場合、define_constructor(Constructor<cppType, int>()) のようにして define_constructor を呼び出す必要があります。ここで 1 つ注意する点は、Ruby の型には複数のコンストラクターはない、という点です。そのため、複数のコンストラクターを持つ C++ タイプのクラスがある場合、define_constructor を使用してすべてのコンストラクターを関連付けるようにすれば、Ruby の世界からは、ソース・コード内にある最後の define_constructor が定義する引数を使って、その C++ タイプのクラスをインスタンス化することができます (インスタンス化しない場合もあります)。リスト 15 には、ここで説明したすべての内容が含まれています。

リスト 15. コンストラクターと引数とを関連付ける
class cppType {
    public:
      cppType(int m) {
        std::cout << m << std::endl;
      }
      cppType(Array a) {
        std::cout << a.size() << std::endl;
      }
      void print(String args) {
        std::cout << args.str() << endl;
      }
    };
Class rb_cTest =
        define_class<cppType>("Test")
         .define_constructor(Constructor<cppType, int>())
         .define_constructor(Constructor<cppType, Array>())
         .define_method("print", &cppType::print);

以下は Ruby の世界のログの抜粋です。Ruby は最後に関連付けられているコンストラクターのみを認識する、という点に注意してください。

>> t = Test.new 2
TypeError: wrong argument type Fixnum (expected Array)
	from (irb):2:in `initialize'
	from (irb):2:in `new'
	from (irb):2
>> t = Test.new [1, 2]
2
=> #<Test:0x10d52cf48>

モジュールの一部として新しい Ruby の型を定義する

C++ から新しい Ruby のモジュールを定義するためには、最終的には define_module を呼び出す必要があります。新しい Ruby のモジュールの一部としてのみ利用可能なクラスを定義する場合には、通常の define_class メソッドの代わりに define_class_under メソッドを使用します。define_class_under メソッドの 1 つ目の引数はモジュール・オブジェクトです。リスト 14 から、types という Ruby のモジュールの一部として cppType を定義する場合には、リスト 16 のようにします。

リスト 16. モジュールの一部として型を宣言する
#include "rice/Constructor.hpp"
#include "rice/String.hpp"
class cppType {
    public:
    void print(String args) {
        std::cout << args.str() << endl;
      }
    };

Module rb_cModule = define_module("Types");
Class rb_cTest =
        define_class_under<cppType>(rb_cModule, "Test")
         .define_constructor(Constructor<cppType>())
        .define_method("print", &cppType::print);

それを Ruby で使用する場合には以下のようにします。

>> include Types
=> Object
>> y = Types::Test.new [1, 1, 1]
3
=> #<Types::Test:0x1058efbd8>

Ruby ではモジュール名とクラス名が大文字で始まる必要があることに注意してください。Rice では、モジュールの名前を Types ではなく types にしてもエラーにはなりません。


C++ のコードを使用して Ruby の struct を作成する

Ruby の struct 構成体を使用すると Ruby のボイラープレート・クラスを簡単に作成することができます。リスト 17 は、aabaab という 3 つの変数を持つ NewClass 型の新しいクラスを Ruby で作成する場合の典型的な方法を示しています。

リスト 17. Ruby の Struct を使用して新しいクラスを作成する
>> NewClass = Struct.new(:a, :ab, :aab)
=> NewClass
>> NewClass.class
=> Class
>> a = NewClass.new
=> #<struct NewClass a=nil, ab=nil, aab=nil>
>> a.a = 1
=> 1
>> a.ab = "test"
=> "test"
>> a.aab = 2.33
=> 2.33
>> a
=> #<struct NewClass a=1, ab="test", aab=2.33>
>> a.a.class
=> Fixnum
>> a.ab.class
=> String
>> a.aab.class
=> Float

リスト 17 と等価なコードを C++ で作成するためには、rice/Struct.hpp ヘッダーで宣言された define_struct( ) API を使用する必要があります。この API は Rice::Struct を返します。この struct によって作成される Ruby のクラスと、このクラスが所属するモジュールとを関連付けます。そのために initialize メソッドがあります。クラスの個々のメンバーは define_member 関数呼び出しによって定義されます。新しい Ruby の型を作成しましたが、この型にはまだ C++ タイプのクラスや関数を関連付けていないことに注意してください。NewClass というクラスを作成するためのコードを以下に示します。

#include "rice/Struct.hpp"
…
Module rb1 = define_module("Types");
define_struct().
        define_member("a").
        define_member("ab").
        define_member("aab").
        initialize(rb1, "NewClass");

まとめ

この記事では、C++ のコードで Ruby のオブジェクトを作成する方法、C スタイルの関数を Ruby のオブジェクトのメソッドとして関連付ける方法、Ruby と C++ との間でデータ型を変換する方法、インスタンス変数を作成する方法、C++ のクラスを Ruby の型としてラップする方法など、広範な内容を説明しました。これらはすべて ruby.h ヘッダーと libruby を使用することで実現可能ですが、すべてが適切に動作するためには大量のボイラープレート・コードを作成する必要があります。Rice を使用すると、それらの作業のすべてが容易になります。皆さんもぜひ、Ruby の世界のための新しい拡張機能を C++ で作成してみてください。

参考文献

学ぶために

  • rice.rubyforge.org は Rice のための出発点であり、doxygen スタイルの優れたドキュメントを提供しています。
  • ツルハシの本として有名な『プログラミング Ruby』(Dave Thomas、Chad Fowler、Andy Hunt の共著、第 2 版) は、Ruby に関する必読の本です。
  • Ruby に関するもう 1 つの貴重な資料として、(Ruby の作成者である) 松本行弘氏 (「Matz」) と David Flanagan との共著、『プログラミング言語 Ruby』(2009年オライリージャパン刊) があります。
  • To Ruby From C and C++ は Ruby を学ぼうとする C/C++ プログラマーのための優れたサイトです。
  • developerWorks の Open source ゾーンには、オープンソース・ツールの情報やオープンソース技術の使い方に関する情報が豊富に用意されています。
  • IBM 製品や IT 業界のトピックに焦点を絞った developerWorks の Technical events and webcasts で最新情報を入手してください。
  • 無料の developerWorks Live! briefing に参加して、IBM の製品およびツールについての情報や IT 業界の動向についての情報を迅速に把握してください。
  • developerWorks On demand demos をご覧ください。初心者のための製品インストール方法やセットアップのデモから、上級開発者のための高度な機能に至るまで、多様な話題が解説されています。
  • Twitter で developerWorks をフォローしてください。

製品や技術を入手するために

  • 皆さんの目的に最適な方法で IBM 製品を評価してください: 製品の試用版をダウンロードする方法、オンラインで製品を試す方法、クラウド環境で製品を使う方法、あるいは SOA Sandbox で数時間を費やし、サービス指向アーキテクチャーの効率的な実装方法を学ぶ方法などがあります。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source
ArticleID=829155
ArticleTitle=Rice を使用して Ruby の拡張機能を C++ で作成する
publish-date=08092012