ゼロからRubyC拡張機能を構築する
今回のRubyMagicでは、RubyからCで記述されたコードの使用方法を紹介します。これを使用して、コードのパフォーマンスに敏感な部分を最適化したり、CライブラリとRuby間のインターフェイスを作成したりできます。これは、Cで記述されたライブラリをラップする拡張機能を作成することによって行われます。
Cで書かれた成熟したパフォーマンスの高いライブラリがたくさんあります。それらを移植して車輪の再発明を行う代わりに、Rubyからこれらのライブラリを活用することもできます。このようにして、Rubyが従来は強力ではなかった領域でCライブラリを使用しながら、お気に入りの言語でコーディングすることができます。 AppSignalでは、このアプローチを使用してrdkafkagemを開発しました。
では、これにどのように取り組むことができるか見てみましょう。フォローして自分で実験したい場合は、サンプルコードを確認してください。まず、文字列、数値、ブール値(Cの理由、しゃれを意図したもの)を含むこのRubyコードを取得し、Cライブラリに移植します。
module CFromRubyExample
class Helpers
def self.string(value)
"String: '#{value}'"
end
def self.number(value)
value + 1
end
def self.boolean(value)
!value
end
end
end
順番に、示されているメソッドは文字列を連結し、数値を1つ増やし、ブール値の反対を返します。
Cに移植されたライブラリ
以下に、Cに移植されたコードを示します。文字列フォーマットを使用できるように、C標準ライブラリとIOライブラリが含まれています。 char*
を使用します RubyのString
の代わりに 。 char*
メモリ内のどこかにある文字のバッファの場所を指します。
# include <stdlib.h>
# include <stdio.h>
char* string_from_library(char* value) {
char* out = (char*)malloc(256 * sizeof(char));
sprintf(out, "String: '%s'", value);
return out;
}
int number_from_library(int value) {
return value + 1;
}
int boolean_from_library(int value) {
if (value == 0) {
return 1;
} else {
return 0;
}
}
ご覧のとおり、単純な文字列フォーマットを行うには、いくつかのフープをジャンプする必要があります。文字列を連結するには、最初にバッファを割り当てる必要があります。これが完了すると、sprintf
次に、関数はフォーマットされた結果をそれに書き込むことができます。最後に、バッファを返すことができます。
上記のコードでは、クラッシュまたはセキュリティの問題が発生する可能性があります。着信文字列が245バイトより長い場合、恐ろしいバッファオーバーフローが発生します。 Cを書くときは、絶対に注意する必要があります。足を撃ちやすいのです。
次はヘッダーファイルです:
char* string_from_library(char*);
int number_from_library(int);
int boolean_from_library(int);
このファイルは、CライブラリのパブリックAPIについて説明しています。他のプログラムは、ライブラリ内のどの関数を呼び出すことができるかを知るためにそれを使用します。
2018年の方法:ffi
を使用する 宝石
これで、Rubyから使用したいCライブラリができました。このCコードをgemでラップする方法は2つあります。最新の方法では、ffi
を使用します 宝石。それは私たちが飛び越えなければならないフープの多くを自動化します。 ffi
の使用 今書いたCコードは次のようになります:
module CFromRubyExample
class Helpers
extend FFI::Library
ffi_lib File.join(File.dirname(__FILE__), "../../ext/library.so")
attach_function :string, [:string], :string
attach_function :number, [:int], :int
attach_function :boolean, [:int], :int
end
end
この記事では、CコードをC拡張子でラップする方法についても説明します。これにより、Rubyの内部ですべてがどのように機能するかについてより多くの洞察が得られます。
ライブラリをC拡張機能でラップする
これで、Rubyから使用したいCライブラリができました。次のステップは、コンパイルしてラップするgemを作成することです。宝石を作成した後、最初にext
を追加します require_paths
へ gemspec内:
Gem::Specification.new do |spec|
spec.name = "c_from_ruby_example"
# ...
spec.require_paths = ["lib", "ext"]
end
これは、ビルドする必要のあるネイティブ拡張があることをRubygemsに通知します。 extconf.rb
というファイルを探します またはRakefile
。この場合、extconf.rb
を追加しました :
require "mkmf"
create_makefile "extension"
mkmf
が必要です 、「MakeMakefile」の略です。これは、Rubyに含まれている一連のヘルパーであり、Cビルドをセットアップする際の厄介な部分を排除します。 create_makefile
と呼びます 拡張子の名前を設定します。これにより、Makefile
が作成されます これには、Cコードをビルドするためのすべての構成とコマンドが含まれています。
次に、ライブラリをRubyに接続するためのCコードを作成する必要があります。 char*
などのC型を変換するいくつかの関数を作成します String
などのRuby型に 。次に、Cコードを使用してRubyクラスを作成します。
まず、Rubyのヘッダーファイルをいくつかインクルードします。これらは、型変換を行うために必要な関数をインポートします。 library.h
も含まれています ライブラリを呼び出すことができるように、以前に作成したヘッダーファイル。
#include "ruby/ruby.h"
#include "ruby/encoding.h"
#include "library.h"
次に、ライブラリ内の各関数をラップする関数を作成します。これは文字列用のものです:
static VALUE string(VALUE self, VALUE value) {
Check_Type(value, T_STRING);
char* pointer_in = RSTRING_PTR(value);
char* pointer_out = string_from_library(pointer_in);
return rb_str_new2(pointer_out);
}
文字列以外の値を処理するとあらゆる種類のバグが発生する可能性があるため、最初に、入力されるRuby値が文字列であるかどうかを確認します。次に、RubyのString
を変換します char*
に RSTRING_PTR
を使用 Rubyが提供するヘルパーマクロ。これで、Cライブラリを呼び出すことができます。返されたchar*
を変換するには 、インクルードrb_str_new2
を使用します 関数。数値とブール値に同様のラッピング関数を追加します。
数値については、NUM2INT
を使用して同様のことを行います およびINT2NUM
ヘルパー:
static VALUE number(VALUE self, VALUE value) {
Check_Type(value, T_FIXNUM);
int number_in = NUM2INT(value);
int number_out = number_from_library(number_in);
return INT2NUM(number_out);
}
ブールバージョンも同様です。 Cには実際にはブール型がないことに注意してください。慣例では、代わりに0と1を使用します。
static VALUE boolean(VALUE self, VALUE value) {
int boolean_in = RTEST(value);
int boolean_out = boolean_from_library(boolean_in);
if (boolean_out == 1) {
return Qtrue;
} else {
return Qfalse;
}
}
最後に、Rubyから呼び出すことができるようにすべてを接続できます:
void Init_extension(void) {
VALUE CFromRubyExample = rb_define_module("CFromRubyExample");
VALUE NativeHelpers = rb_define_class_under(CFromRubyExample, "NativeHelpers", rb_cObject);
rb_define_singleton_method(NativeHelpers, "string", string, 1);
rb_define_singleton_method(NativeHelpers, "number", number, 1);
rb_define_singleton_method(NativeHelpers, "boolean", boolean, 1);
}
はい、その通りです。CでRubyモジュール、クラス、メソッドを作成できます。ここでクラスを設定します。次に、Rubyメソッドをクラスに追加します。 Rubyメソッドの名前、呼び出されるCラッパー関数の名前、および引数の数を指定する必要があります。
すべての作業が終わったら、最終的にCコードを呼び出すことができます:
CFromRubyExample::NativeHelpers.string("a string")
結論
私たちはフープを飛び越え、クラッシュせず、C拡張機能を動作させました。 C拡張機能を書くことは、気の弱い人向けではありません。 ffi
を使用している場合でも それでも、Rubyプロセスを非常に簡単にクラッシュさせることができます。しかし、それは実行可能であり、パフォーマンスが高く安定したCライブラリの世界を開くことができます!
-
Rubyでの新しいプログラミング言語の構築:インタープリター
Githubのフルソース Stoffleプログラミング言語の完全な実装は、GitHubで入手できます。バグを見つけたり質問がある場合は、遠慮なく問題を開いてください。 このブログ投稿では、完全にRubyで構築されたおもちゃのプログラミング言語であるStoffleのインタープリターの実装を開始します。このプロジェクトの詳細については、このシリーズの最初の部分をご覧ください。 これから作成するインタプリタは、一般にツリーウォークインタプリタと呼ばれます。このシリーズの前回の投稿では、トークンのフラットシーケンスをツリーデータ構造(抽象構文木、または略してAST)に変換するパーサーを構築しま
-
Windows 11 を最初からインストールする方法 (USB を使用してインストール)
Microsoft は、世界で最も使用されているコンピューター オペレーティング システムの新しいバージョンである Windows 11 をリリースしました。 Windows 11 オペレーティング システムの最新バージョンには、ゲーム特典、簡素化されたレイアウト、直感的なショートカットなど、多くの新機能が搭載されています。 Windows 11 の最小ハードウェア要件を満たす Windows 10 コンピューターをお持ちの場合は、無料で Windows 11 にアップグレードできます。 Windows 11 をインストールする方法もいくつかあります すでにお伝えしたとおりですが、あなたの場合