From: Luigi Ballabio Date: 2002-04-04T20:03:21+09:00 Subject: Help: Ruby<->C++ callbacks Hi all, I'm writing a C++ extension to Ruby. I need to write a class which can take a Ruby procedure and store it to be used as a callback, i.e., irb> c = Callback(proc { puts "Hello world!" }) irb> c.doit "Hello world!" irb> This has not much use within the Ruby interpreter: however, it would allow me to wrap Ruby code and use it as a callback in the C++ library I'm interfacing. However, my concern is how to make sure that the procedure is not garbage-collected as long as there are Callback objects storing a reference to it. Also, the lifetime of the procedure does not in principle depend on the lifetime of c since copies of the latter could be made in the C++ library which are not known to Ruby. Research on readme.ext and Programming Ruby yielded: 1) rb_global_variable: it can protect a VALUE, but I don't know if the process is undoable, i.e., if the VALUE can be marked again for gc. 2) rb_gc_mark: it marks an object for gc, but can it be applied to one previously protected by rb_global_variable? Also, if two callback objects refer to a VALUE and one calls rb_gc_mark, will the latter be collected regardless of the fact that another reference remains in use, or is some kind of reference counting implemented? At the end, I came up with the following design: however, holes remain which should be filled before the class is safely useable. Any suggestions? Thanks in advance, Luigi class RubyCallback : public Callback { public: RubyCallback(VALUE p) : p_(p), refCount_(new int(1)) { /* how to make sure the Ruby proc stays alive as long as we need it? Is the following the right way? */ rb_global_variable(p_); } RubyCallback(const RubyCallback& o) : p_(o.p_), refCount_(o.refCount_) { /* how to make sure the Ruby proc stays alive as long as we need it? */ o.refCount_++; } RubyCallback& operator=(const RubyCallback& o) { if (refCount_ != o.refCount_) { if (--*refCount == 0) { // tell p_ it can go as far as we are concerned delete refCount_; } p_ = o.p_; refCount_ = o.refCount_; // make sure p_ stays alive *refCount_++; } return *this; } ~RubyCallback() { if (--*refCount_ == 0) { // now it can go as far as we are concerned // is the following the right way? rb_gc_mark(p_); delete refCount_; } } void doit() { static ID callId = rb_intern("call"); rb_funcall(p_,callId,0); } private: VALUE p_; int* refCount_; // is this needed? };