For å koble Ruby-kode med C/C++ kode kan du: skrive egne utvidelser, innebake Ruby-fortolkeren i programmet ditt, bruke SWIG til å pakke inn C/C++ koden, arbeide mot dynamiske bibliotek med DL eller gjøre småtriks med RubyInline.
Ruby er skrevet i C, så man kan enkelt skrive egne metoder i C for å øke hastigheten.
1| #include "ruby.h" 2| 3| VALUE factorial( VALUE self, VALUE index ) 4| { 5| VALUE fact = Qnil; 6| long i = NUM2LONG( index ); 7| long f = 1; 8| while(i>0){ 9| f = f * i--; 10| } 11| fact = LONG2NUM( f ); 12| return( fact ); 13| }; 14| 15| Init_ext_fact1() 16| { 17| rb_define_global_function("factorial", factorial, 1 ); 18| }; |
Når utvidelsen er kompilert kan vi hente inn modulen og kalle koden.
1| # kompilerer C-koden med: 2| # gcc -shared ext_fact1.c -o ext_fact1.so 3| require 'ext_fact1.so' 4| puts (1..10).collect{|i| factorial i }.join(', ') |
SWIG (Simplified Wrapper and Interface Generator) pakker inn C/C++ kode for bruk i andre språk, slik som Ruby, Python, Perl, Java m.m.
Vi finner ut at Vector-klassen fra tidligere ble for treg for våre 3D-beregninger, så vi prøver å lage en C++-variant. Vi har følgende headerfil:
1| class Vector {
2| public:
3| double x, y, z;
4| Vector( double x = 0, double y = 0, double z = 0 );
5| Vector operator+( const Vector & );
6| Vector operator-( const Vector & );
7| Vector operator*( const Vector & );
8| };
|
Når vi så skal bruke denne i Ruby, kan vi bruke SWIG til å pakke inn C++-metodene for oss. Vi definerer da en interfacefil:
1| %module graphics 2| %{ 3| #include "swig_vector.hpp" 4| %} 5| class Vector { 6| public: 7| %immutable; 8| double x, y, z; 9| %mutable; 10| Vector( double x = 0, double y = 0, double z = 0 ); 11| Vector operator+( const Vector & ); 12| Vector operator-( const Vector & ); 13| Vector operator*( const Vector & ); 14| }; 15| 16| %extend Vector { 17| VALUE to_a(){ 18| VALUE arr = rb_ary_new(); 19| rb_ary_store( arr, 0, rb_float_new(self->x)); 20| rb_ary_store( arr, 1, rb_float_new(self->y)); 21| rb_ary_store( arr, 2, rb_float_new(self->z)); 22| return( arr ); 23| } 24| }; |
Denne kjøres gjennom SWIG og lager en C++-fil med wrapper-funksjoner. Alt kompileres og lenkes:
1| system('g++ -c swig_vector.cpp') 2| system('swig -c++ -ruby swig_vector.i') 3| system('g++ -c swig_vector_wrap.cxx -I/usr/lib/ruby/1.8/i586-linux-gnu/') 4| system('g++ -shared swig_vector_wrap.o swig_vector.o -o graphics.so') 5| 6| require 'graphics.so' 7| p Graphics 8| p Graphics::Vector 9| v = Graphics::Vector.new(1,3,7) 10| p v.x, v.y, v.z |
Nå for den store testen... Kan vi bruke enhetstestene fra Ruby-versjonen av Vector-klassen?
1| require 'graphics' #<- Endret 2| require 'test/unit' 3| 4| class TestVector < Test::Unit::TestCase 5| include Graphics #<- Lagt til 6| # --- kuttet her --- |
Jepp, kun to små endringer nødvendig.
DL er et standardbibliotekt gir oss tilgang til den dynamiske lenkeren. Dermed kan vi hente inn og bruke vilkårlige dynamiske biblioteker (DLL og SO) fra Ruby.
1| require 'dl/import' 2| module Clib 3| extend DL::Importable 4| dlload "/lib/libc.so.6" 5| extern "int printf(char*,int)" 6| end 7| Clib.printf("Testing. Tall: %i !\n", 5) |
Win32API er et deprecated bibliotek som gav tilgang til API-funksjoner under Windows. Det er deprecated til fordel for en modul i DL som tilbyr samme funksjonalitet.
1| require 'dl/win32' 2| 3| message_box = Win32API.new("user32",'MessageBoxA', 'ISSI', 'I') 4| 5| parent = 0 6| message = "Ok?" 7| title = "Error!" 8| style = 1 # MB_OKCANCEL 9| 10| retval = message_box.call( parent, message, title, style ) 11| 12| knapp = case retval 13| when 1 then "OK" 14| when 2 then "Cancel" 15| else "(?)" 16| end 17| puts "Returverdi: #{retval}. Knapp: #{knapp}" |
Har man først nevnt Win32API, bør man få med seg Win32OLE. Det er en mer høynivå innpakking av OLE i Windows.
1| require 'win32ole' 2| ie = WIN32OLE.new('InternetExplorer.Application') 3| ie.visible = TRUE 4| ie.navigate( "http://www.ruby.no/" ) 5| print "Trykk Enter." 6| gets 7| ie.Quit() |