Ruby Language
C 확장
수색…
첫 번째 확장 프로그램
C 확장은 두 가지 일반적인 부분으로 구성됩니다.
- C 코드 자체.
- 확장 구성 파일.
첫 번째 확장을 시작하려면 extconf.rb
파일에 다음을 입력하십시오.
require 'mkmf'
create_makefile('hello_c')
몇 가지 지적해야 할 점은 다음과 같습니다.
첫째, hello_c
라는 이름은 컴파일 된 확장의 출력 이름입니다. 그것은 당신이 require
와 함께 사용 require
입니다.
둘째, extconf.rb
파일의 이름을 실제로 지정할 수 있습니다.이 파일은 원래 고유 코드를 가진 보석을 만드는 데 사용되는 것으로 전통적으로 확장자를 실제로 컴파일하는 파일은 ruby extconf.rb
실행할 때 생성되는 Makefile입니다. 생성 된 기본 Makefile은 현재 디렉토리의 모든 .c
파일을 컴파일합니다.
다음을 hello.c
라는 파일에 ruby extconf.rb && make
실행하십시오.
#include <stdio.h>
#include "ruby.h"
VALUE world(VALUE self) {
printf("Hello World!\n");
return Qnil;
}
// The initialization method for this module
void Init_hello_c() {
VALUE HelloC = rb_define_module("HelloC");
rb_define_singleton_method(HelloC, "world", world, 0);
}
코드 분석 :
Init_hello_c
라는 이름은 extconf.rb
파일에 정의 된 이름과 일치해야합니다. 그렇지 않으면 확장을 동적으로로드 할 때 Ruby가 확장을 부트 스트랩하기 위해 심볼을 찾을 수 없습니다.
rb_define_module
대한 호출은 HelloC
라는 루비 모듈을 생성하는데,이 모듈은 우리의 C 함수의 이름 공간을 만듭니다.
마지막으로, 호출 rb_define_singleton_method
받는 직접 연결 모듈 수준의 방법을 만드는 HelloC
우리가 루비에서 호출 할 수있는 모듈 HelloC.world
.
make
호출하여 확장을 컴파일 한 후 C 확장자로 코드를 실행할 수 있습니다.
콘솔을 켜십시오!
irb(main):001:0> require './hello_c'
=> true
irb(main):002:0> HelloC.world
Hello World!
=> nil
C 구조체로 작업하기
C 구조체를 Ruby 객체로 사용할 수 있으려면 Data_Wrap_Struct
및 Data_Get_Struct
를 호출하여 C 구조체를 래핑해야합니다.
Data_Wrap_Struct
는 C 데이터 구조를 Ruby 객체로 래핑합니다. 콜백 함수에 대한 몇 가지 포인터와 함께 데이터 구조에 대한 포인터를 취해 VALUE를 반환합니다. Data_Get_Struct
매크로는 그 값을 취하여 C 데이터 구조에 대한 포인터를 돌려줍니다.
다음은 간단한 예입니다.
#include <stdio.h>
#include <ruby.h>
typedef struct example_struct {
char *name;
} example_struct;
void example_struct_free(example_struct * self) {
if (self->name != NULL) {
free(self->name);
}
ruby_xfree(self);
}
static VALUE rb_example_struct_alloc(VALUE klass) {
return Data_Wrap_Struct(klass, NULL, example_struct_free, ruby_xmalloc(sizeof(example_struct)));
}
static VALUE rb_example_struct_init(VALUE self, VALUE name) {
example_struct* p;
Check_Type(name, T_STRING);
Data_Get_Struct(self, example_struct, p);
p->name = (char *)malloc(RSTRING_LEN(name) + 1);
memcpy(p->name, StringValuePtr(name), RSTRING_LEN(name) + 1);
return self;
}
static VALUE rb_example_struct_name(VALUE self) {
example_struct* p;
Data_Get_Struct(self, example_struct, p);
printf("%s\n", p->name);
return Qnil;
}
void Init_example()
{
VALUE mExample = rb_define_module("Example");
VALUE cStruct = rb_define_class_under(mExample, "Struct", rb_cObject);
rb_define_alloc_func(cStruct, rb_example_struct_alloc);
rb_define_method(cStruct, "initialize", rb_example_struct_init, 1);
rb_define_method(cStruct, "name", rb_example_struct_name, 0);
}
그리고 extconf.rb
:
require 'mkmf'
create_makefile('example')
확장 프로그램을 컴파일 한 후 :
irb(main):001:0> require './example'
=> true
irb(main):002:0> test_struct = Example::Struct.new("Test Struct")
=> #<Example::Struct:0x007fc741965068>
irb(main):003:0> test_struct.name
Test Struct
=> nil
인라인 C 작성 - RubyInLine
RubyInline은 Ruby 코드 안에 다른 언어를 포함시킬 수있는 프레임 워크입니다. 빌더 객체를 반환하는 Module # inline 메소드를 정의합니다. 빌더에 Ruby 이외의 다른 언어로 작성된 코드가 포함 된 문자열을 전달하면 빌더는이를 빌더가 Ruby에서 호출 할 수있는 것으로 변환합니다.
C 또는 C ++ 코드 (기본 RubyInline 설치에서 지원되는 두 언어)에서 빌더 객체는 작은 확장자를 디스크에 쓰고 컴파일 한 다음로드합니다. 컴파일을 직접 처리 할 필요는 없지만 홈 디렉토리의 .ruby_inline 서브 디렉토리에서 생성 된 코드와 컴파일 된 확장을 볼 수 있습니다.
Ruby 프로그램에 C 코드를 삽입하십시오.
- RubyInline ( 루비 읶 젬으로 사용 가능)이 자동으로 확장 기능을 생성합니다.
RubyInline은 irb 내에서 작동하지 않습니다.
#!/usr/bin/ruby -w
# copy.rb
require 'rubygems'
require 'inline'
class Copier
inline do |builder|
builder.c <<END
void copy_file(const char *source, const char *dest)
{
FILE *source_f = fopen(source, "r");
if (!source_f)
{
rb_raise(rb_eIOError, "Could not open source : '%s'", source);
}
FILE *dest_f = fopen(dest, "w+");
if (!dest_f)
{
rb_raise(rb_eIOError, "Could not open destination : '%s'", dest);
}
char buffer[1024];
int nread = fread(buffer, 1, 1024, source_f);
while (nread > 0)
{
fwrite(buffer, 1, nread, dest_f);
nread = fread(buffer, 1, 1024, source_f);
}
}
END
end
end
C 함수 copy_file
이제 Copier
의 인스턴스 메소드로 존재합니다.
open('source.txt', 'w') { |f| f << 'Some text.' }
Copier.new.copy_file('source.txt', 'dest.txt')
puts open('dest.txt') { |f| f.read }