Создание расширений Ruby в C ++ с использованием Rice: Как передать указатель на метод Ruby (обратный вызов) в функцию C ++?

У меня есть довольно крутая библиотека C ++ с именем libcage. Мне нужны методы класса в библиотеке для вызова из сценария Ruby. Я строю расширения Ruby в C ++, используя Рис.

Метод join из класса cage использует параметр как указатель на обратный вызов функция.
Упрощенный иллюстративный пример использования обратного вызова в C ++:

/* File example.cpp */
#include <libcage/cage.hpp>

void joinCallback(bool joinResult) {
// do something
}
int main(int argc, char *argv[]) {
libcage::cage *cage = new libcage::cage;
cage->join("localhost", 80, &joinCallback);
}
/* End of file example.cpp */

После некоторого события join метод вызовет callback функция. Это не проблема, но Я хочу определить функцию обратного вызова в Ruby!

# File example.rb
require './rb_libcage'  # C++ extension library

def joinCallback(joinResult)
# do something
end

cage = Cage.new
cage.join "localhost", 80, method(:joinCallback)
# End of file example.rb

Это не работает сейчас. Я не знаю, как передать указатель метода Ruby в функцию C ++ через параметр вызова callback_to_ruby в образце ниже.


РЕДАКТИРОВАТЬ:

У меня на самом деле сейчас это:

/* File rb_libcage.cpp */
#include "rice/Class.hpp"#include "rice/Data_Type.hpp"#include "rice/Constructor.hpp"
#include "cage.hpp"
Rice::Object __callback_to_ruby;
ID           __callback_id_to_ruby;

namespace {
void c_callback(bool result)
{
if (result)
std::cout << "join: succeeded" << std::endl;
else
std::cout << "join: failed" << std::endl;

/* This does not work! */
__callback_to_ruby.call(__callback_id_to_ruby,"AsyncCallback");
/**
* terminate called after throwing an instance of 'Rice::Exception'
*  what():  undefined method `call' for "AsyncCallback":String
* Aborted
*/
}
class Cage_wrapper
{
public:
Cage_wrapper() : cage(new libcage::cage()) {}

void join(std::string host, int port, Rice::Object callback_to_ruby) {
__callback_to_ruby = callback_to_ruby;
__callback_id_to_ruby = rb_intern("call");
__callback_to_ruby.call(__callback_id_to_ruby,"test"); /* This works! */

/* cage->join() will make another thread that will call ASYNCHRONOUSLY c_callback function */
cage->join(host, port, &c_callback);
}
private:
libcage::cage *cage;
};

extern "C"void Init_rb_libcage(void)
{
RUBY_TRY
{
Rice::define_class<Cage_wrapper>("Cage")
.define_constructor(Rice::Constructor<Cage_wrapper>())
.define_method("join", &Cage_wrapper::join);
}
RUBY_CATCH
}
} // namespace
/* End of file rb_libcage.cpp */

и Ruby скрипт:

# File example.rb
require './rb_libcage'  # C++ extension library

ruby_callback = lambda {|x| print "From ruby script join result: ", x, "\n" }
cage = Cage.new
cage.join "localhost", 80, ruby_callback
puts 'After join'
# End of file example.rb

rb_libcage.cpp компилируется и запускается example.rb выглядит так:

From ruby script join result: test
After join
join: succeeded
terminate called after throwing an instance of 'Rice::Exception'
what():  undefined method `call' for "AsyncCallback":String
Aborted

Я могу вызвать синхронный обратный вызов, но не асинхронный обратный вызов. Это потерял контекст? Как успешно вызвать асинхронный обратный вызов ruby ​​из кода C ++?

С наилучшими пожеланиями,
Pawel

П.С .: Я нашел полезным статья о построении асинхронных обратных вызовов в расширениях Ruby C

2

Решение

Я не эксперт по ruby, но думаю, что сигнатура метода Cage_wrapper.join может выглядеть как

void join(std::string host, int port, Rice::Object *ruby_proc)

Вы можете использовать процедуру ruby ​​в качестве обратного вызова для нативной библиотеки. то есть Rice :: Object — это рубиновый процесс.

Затем в какой-то момент вы захотите сделать ruby_proc.call (). Таким образом, вам может понадобиться обернуть вызов ruby_proc.call в обратный вызов, который приемлем для соединения с собственной клеткой.

0

Другие решения

Других решений пока нет …