Pull to refresh

Ruby и C. Часть 3.

Reading time 3 min
Views 3.3K
В прошлых частях(часть 1,часть 2) мы рассмотрели использование С для ускорения или расширения Ruby. Сейчас же мы узнаем как использовать Ruby интерпретатор в программах, написанных на С/С++.
В некоторых приложениях возникает необходимость во встроенном языке, для более тонкой настройки или для написания расширений без перекомпиляции. Ruby очень хорошо подходит для этой цели, т.к. имеет простое и удобное API для втраивания в C/C++ приложения. Например Google SketchUp использует Ruby в качестве скриптового языка.

Давайте рассмотрим как встроить Ruby в наши С/С++ приложения.


Начнем с рассмотрения функции rb_eval_string, которая выполняет строку с Ruby кодом.
Давайте рассмотрим простой пример — програма для вычисления выражений. Для простоты у нас в выражениях будет только две переменные.
Пример работы приложения, входные данные:
	($a+$b)*$a-$b
	2
	3

Результат:
	($a+$b)*$a-$b = 7


Посмотрим код:
	#include <ruby.h>
	#include <stdio.h>

	int main() {
		ruby_init(); //инициализация интерпретатора
	
		int a = 0;
		int b = 0;
		char expr[256];
	
		//читаем выражение и две переменные
		scanf("%s", &expr); 
		scanf("%d", &a); 
		scanf("%d", &b);
	
		//создаем Ruby переменные
		VALUE r_a = INT2NUM(a);
		VALUE r_b = INT2NUM(b);
		//и делаем их доступными в интерпретаторе
		rb_define_variable("$a", &r_a);
		rb_define_variable("$b", &r_b);
	
		//выполняем выражение и выводим результат
		VALUE res = rb_eval_string(expr); 
		printf("%s = %d\n", expr, NUM2INT(res));

		return 0;
	}

Примечание: про тип данных VALUE, макросы INT2NUM и т.п., и более подробно про работу с Ruby C API смотрите в части 2

В данном примере мы считали с клавиатуры две переменные и выражение с ними. Затем, на основе этих переменных, создали глобальные Ruby переменные и, с помощью метода rb_define_variable, сделали их доступными в интерпретаторе.
В итоге, с помощью метода rb_eval_string, мы выполнили выражение и вывели его результат на экран.

Теперь перейдем к загрузке и выполнению скриптов на Ruby.
Рассмотрим такой пример: нам нужно получать хэш строки по пользовательскому алгоритму. Сценарий работы такой, пользователь создает файл alg.rb и реализует в нем алгоритм хэширования. В нашей програме мы загружаем пользовательский скрипт, передаем ему строку для хэширования, выполняем скрипт и печатаем результат.
Посмотрим код:
	#include <ruby.h>
	#include <stdio.h>

	int main() {
		ruby_init(); //инициализация интерпретатора
		ruby_init_loadpath(); //возможность подключать стандартные библиотеки, например require 'MD5'
		VALUE res = rb_str_new2("some test string"); //наша тестовая строка
		rb_define_variable("$res", &res); //делаем ее доступной скрипту
		rb_load_file("alg.rb"); //загружаем файл с алгоритмом
		//выполняем скрипт и выводим результат
		ruby_exec(); 
		printf("%s\n", StringValuePtr(res));
		return 0;
	}


Теперь в файле alg.rb мы можем реализовывать какие угодно алгоритмы хэширования.
Например, к количество пробелов в строке(не очень удачный хэш:)):
	hash = 0
	$res.split(//).each {|c| hash += 1 if c == " "}
	$res = hash.to_s

или сумма всех символов в шестнадцатеричном виде:
	hash = 0
	$res.split(//).each {|c| hash += c[0]}
	$res = hash.to_s(16)

или же md5, с помощью стандартной библиотеки:
	require "md5"
	$res = MD5.hexdigest($res)


Теперь о компиляции приложений со встроенным Ruby, на пример gcc:
gcc test.c -o test -I<папка с ruby.h> -lruby
либо:
gcc test.c -o test -I<папка с ruby.h> -lruby1.8

Для компиляции в *nix системах нужно поставить пакет ruby-dev, например так:
sudo apt-get install ruby1.8-dev
Tags:
Hubs:
+6
Comments 11
Comments Comments 11

Articles