【问题标题】:Building Ruby extensions in C++ using Rice: How to transmit the pointer of Ruby method (callback) to C++ function?使用 Rice 在 C++ 中构建 Ruby 扩展:如何将 Ruby 方法(回调)的指针传递给 C++ 函数?
【发布时间】:2013-10-01 14:05:33
【问题描述】:

我有一个非常酷的 C++ 库,名为 libcage。我需要库中的类方法来从 Ruby 脚本调用。 我正在使用 Rice 在 C++ 中构建 Ruby 扩展

来自类cage 的方法join 使用参数作为指向回调函数的指针。 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

这现在不起作用。 我不知道如何通过下面示例中的调用参数callback_to_ruby 将Ruby 方法的指针传递给C++ 函数


编辑:

其实我现在有这个:

/* 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

我可以调用同步回调,但不能调用异步回调。它会失去上下文吗?如何从 C++ 代码中成功调用异步 ruby​​ 回调?

最好的问候, 帕维尔

P.S.:我觉得很有用 article about building Asynchronous callbacks in Ruby C extensions

【问题讨论】:

  • 对于初学者来说,你没有传递对方法的引用——:callback 是一个符号。将它传递给 proc (lambda) 或块 - 然后您可以从本机代码中生成它。

标签: c++ ruby callback asynccallback


【解决方案1】:

我不是 ruby​​ 专家,但我认为 Cage_wrapper.join 方法签名可能希望看起来更像

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

您可以使用 ruby​​ proc 作为本机库的回调。即 Rice::Object 是 ruby​​ proc。

然后在某些时候你会想要做一个 ruby​​_proc.call()。因此,您可能需要将对 ruby​​_proc.call 的调用包装在一个回调中,该回调对本机笼子连接来说是可口的。

【讨论】:

    猜你喜欢
    • 2014-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-03
    • 1970-01-01
    • 2019-03-20
    • 2012-04-21
    相关资源
    最近更新 更多