Next Previous Contents

4. Integrering med C/C++

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.

4.1 Ruby i C

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(', ')

 

4.2 SWIG

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.

 

 

4.3 Ruby/DL

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

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}"

4.4 Win32OLE

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()

 

 

 

 


Next Previous Contents