| #ifdef __cplusplus |
| |
| /* |
| GC_VALUE is used as a replacement of Ruby's VALUE. |
| GC_VALUE automatically handles registering and unregistering |
| of the underlying Ruby object with the GC. |
| |
| It can be used if you want to create STL containers of VALUEs, such as: |
| |
| std::vector< GC_VALUE >; |
| |
| or as a member variable: |
| |
| struct A { |
| GC_VALUE _obj; |
| A(VALUE o) : _obj(o) { |
| } |
| }; |
| |
| or as a input/output value (not much use for this, as VALUE works just as |
| well here, thou): |
| |
| GC_VALUE func(GC_VALUE obj) { |
| GC_VALUE out = rb_obj_classname(obj); |
| return out; |
| } |
| |
| |
| GC_VALUE is 'visible' at the wrapped side, so you can do: |
| |
| %template(RubyVector) std::vector<swig::GC_VALUE>; |
| |
| and all the proper typemaps will be used. |
| |
| */ |
| |
| %fragment("GC_VALUE_definition","header") { |
| namespace swig { |
| class SwigGCReferences { |
| // Hash of all GC_VALUE's currently in use |
| static SwigGCReferences s_references; |
| |
| VALUE _hash; |
| |
| SwigGCReferences() : _hash(Qnil) { |
| } |
| ~SwigGCReferences() { |
| if (_hash != Qnil) |
| rb_gc_unregister_address(&_hash); |
| } |
| static void EndProcHandler(VALUE) { |
| // Ruby interpreter ending - _hash can no longer be accessed. |
| s_references._hash = Qnil; |
| } |
| public: |
| static SwigGCReferences& instance() { |
| return s_references; |
| } |
| static void initialize() { |
| if (s_references._hash == Qnil) { |
| rb_set_end_proc(&EndProcHandler, Qnil); |
| s_references._hash = rb_hash_new(); |
| rb_gc_register_address(&s_references._hash); |
| } |
| } |
| void GC_register(VALUE& obj) { |
| if (FIXNUM_P(obj) || SPECIAL_CONST_P(obj) || SYMBOL_P(obj)) |
| return; |
| if (_hash != Qnil) { |
| VALUE val = rb_hash_aref(_hash, obj); |
| unsigned n = FIXNUM_P(val) ? NUM2UINT(val) : 0; |
| ++n; |
| rb_hash_aset(_hash, obj, INT2NUM(n)); |
| } |
| } |
| void GC_unregister(const VALUE& obj) { |
| if (FIXNUM_P(obj) || SPECIAL_CONST_P(obj) || SYMBOL_P(obj)) |
| return; |
| // this test should not be needed but I've noticed some very erratic |
| // behavior of none being unregistered in some very rare situations. |
| if (BUILTIN_TYPE(obj) == T_NONE) |
| return; |
| if (_hash != Qnil) { |
| VALUE val = rb_hash_aref(s_references._hash, obj); |
| unsigned n = FIXNUM_P(val) ? NUM2UINT(val) : 1; |
| --n; |
| if (n) |
| rb_hash_aset(s_references._hash, obj, INT2NUM(n)); |
| else |
| rb_hash_delete(s_references._hash, obj); |
| } |
| } |
| }; |
| |
| class GC_VALUE { |
| protected: |
| VALUE _obj; |
| |
| static ID hash_id; |
| static ID lt_id; |
| static ID gt_id; |
| static ID eq_id; |
| static ID le_id; |
| static ID ge_id; |
| |
| static ID pos_id; |
| static ID neg_id; |
| static ID inv_id; |
| |
| static ID add_id; |
| static ID sub_id; |
| static ID mul_id; |
| static ID div_id; |
| static ID mod_id; |
| |
| static ID and_id; |
| static ID or_id; |
| static ID xor_id; |
| |
| static ID lshift_id; |
| static ID rshift_id; |
| |
| struct OpArgs |
| { |
| VALUE src; |
| ID id; |
| int nargs; |
| VALUE target; |
| }; |
| |
| |
| public: |
| GC_VALUE() : _obj(Qnil) |
| { |
| } |
| |
| GC_VALUE(const GC_VALUE& item) : _obj(item._obj) |
| { |
| SwigGCReferences::instance().GC_register(_obj); |
| } |
| |
| GC_VALUE(VALUE obj) :_obj(obj) |
| { |
| SwigGCReferences::instance().GC_register(_obj); |
| } |
| |
| ~GC_VALUE() |
| { |
| SwigGCReferences::instance().GC_unregister(_obj); |
| } |
| |
| GC_VALUE & operator=(const GC_VALUE& item) |
| { |
| SwigGCReferences::instance().GC_unregister(_obj); |
| _obj = item._obj; |
| SwigGCReferences::instance().GC_register(_obj); |
| return *this; |
| } |
| |
| operator VALUE() const |
| { |
| return _obj; |
| } |
| |
| VALUE inspect() const |
| { |
| return rb_inspect(_obj); |
| } |
| |
| VALUE to_s() const |
| { |
| return rb_inspect(_obj); |
| } |
| |
| static VALUE swig_rescue_swallow(VALUE) |
| { |
| /* |
| VALUE errstr = rb_obj_as_string(rb_errinfo()); |
| printf("Swallowing error: '%s'\n", RSTRING_PTR(StringValue(errstr))); |
| */ |
| return Qnil; /* Swallow Ruby exception */ |
| } |
| |
| static VALUE swig_rescue_funcall(VALUE p) |
| { |
| OpArgs* args = (OpArgs*) p; |
| return rb_funcall(args->src, args->id, args->nargs, args->target); |
| } |
| |
| bool relational_equal_op(const GC_VALUE& other, const ID& op_id, bool (*op_func)(const VALUE& a, const VALUE& b)) const |
| { |
| if (FIXNUM_P(_obj) && FIXNUM_P(other._obj)) { |
| return op_func(_obj, other._obj); |
| } |
| bool res = false; |
| VALUE ret = Qnil; |
| SWIG_RUBY_THREAD_BEGIN_BLOCK; |
| if (rb_respond_to(_obj, op_id)) { |
| OpArgs args; |
| args.src = _obj; |
| args.id = op_id; |
| args.nargs = 1; |
| args.target = VALUE(other); |
| ret = rb_rescue(RUBY_METHOD_FUNC(swig_rescue_funcall), VALUE(&args), |
| (RUBY_METHOD_FUNC(swig_rescue_swallow)), Qnil); |
| } |
| if (ret == Qnil) { |
| VALUE a = rb_funcall( _obj, hash_id, 0 ); |
| VALUE b = rb_funcall( VALUE(other), hash_id, 0 ); |
| res = op_func(a, b); |
| } else { |
| res = RTEST(ret); |
| } |
| SWIG_RUBY_THREAD_END_BLOCK; |
| return res; |
| } |
| |
| static bool operator_eq(const VALUE& a, const VALUE& b) { return a == b; } |
| static bool operator_lt(const VALUE& a, const VALUE& b) { return a < b; } |
| static bool operator_le(const VALUE& a, const VALUE& b) { return a <= b; } |
| static bool operator_gt(const VALUE& a, const VALUE& b) { return a > b; } |
| static bool operator_ge(const VALUE& a, const VALUE& b) { return a >= b; } |
| |
| bool operator==(const GC_VALUE& other) const { return relational_equal_op(other, eq_id, operator_eq); } |
| bool operator<(const GC_VALUE& other) const { return relational_equal_op(other, lt_id, operator_lt); } |
| bool operator<=(const GC_VALUE& other) const { return relational_equal_op(other, le_id, operator_le); } |
| bool operator>(const GC_VALUE& other) const { return relational_equal_op(other, gt_id, operator_gt); } |
| bool operator>=(const GC_VALUE& other) const { return relational_equal_op(other, ge_id, operator_ge); } |
| |
| bool operator!=(const GC_VALUE& other) const |
| { |
| return !(this->operator==(other)); |
| } |
| |
| GC_VALUE unary_op(const ID& op_id) const |
| { |
| VALUE ret = Qnil; |
| SWIG_RUBY_THREAD_BEGIN_BLOCK; |
| OpArgs args; |
| args.src = _obj; |
| args.id = op_id; |
| args.nargs = 0; |
| args.target = Qnil; |
| ret = rb_rescue(RUBY_METHOD_FUNC(swig_rescue_funcall), VALUE(&args), |
| (RUBY_METHOD_FUNC(swig_rescue_swallow)), Qnil); |
| SWIG_RUBY_THREAD_END_BLOCK; |
| return ret; |
| } |
| |
| GC_VALUE operator+() const { return unary_op(pos_id); } |
| GC_VALUE operator-() const { return unary_op(neg_id); } |
| GC_VALUE operator~() const { return unary_op(inv_id); } |
| |
| GC_VALUE binary_op(const GC_VALUE& other, const ID& op_id) const |
| { |
| VALUE ret = Qnil; |
| SWIG_RUBY_THREAD_BEGIN_BLOCK; |
| OpArgs args; |
| args.src = _obj; |
| args.id = op_id; |
| args.nargs = 1; |
| args.target = VALUE(other); |
| ret = rb_rescue(RUBY_METHOD_FUNC(swig_rescue_funcall), VALUE(&args), |
| (RUBY_METHOD_FUNC(swig_rescue_swallow)), Qnil); |
| SWIG_RUBY_THREAD_END_BLOCK; |
| return GC_VALUE(ret); |
| } |
| |
| GC_VALUE operator+(const GC_VALUE& other) const { return binary_op(other, add_id); } |
| GC_VALUE operator-(const GC_VALUE& other) const { return binary_op(other, sub_id); } |
| GC_VALUE operator*(const GC_VALUE& other) const { return binary_op(other, mul_id); } |
| GC_VALUE operator/(const GC_VALUE& other) const { return binary_op(other, div_id); } |
| GC_VALUE operator%(const GC_VALUE& other) const { return binary_op(other, mod_id); } |
| GC_VALUE operator&(const GC_VALUE& other) const { return binary_op(other, and_id); } |
| GC_VALUE operator^(const GC_VALUE& other) const { return binary_op(other, xor_id); } |
| GC_VALUE operator|(const GC_VALUE& other) const { return binary_op(other, or_id); } |
| GC_VALUE operator<<(const GC_VALUE& other) const { return binary_op(other, lshift_id); } |
| GC_VALUE operator>>(const GC_VALUE& other) const { return binary_op(other, rshift_id); } |
| }; |
| |
| ID GC_VALUE::hash_id = rb_intern("hash"); |
| ID GC_VALUE::lt_id = rb_intern("<"); |
| ID GC_VALUE::gt_id = rb_intern(">"); |
| ID GC_VALUE::eq_id = rb_intern("=="); |
| ID GC_VALUE::le_id = rb_intern("<="); |
| ID GC_VALUE::ge_id = rb_intern(">="); |
| |
| ID GC_VALUE::pos_id = rb_intern("+@"); |
| ID GC_VALUE::neg_id = rb_intern("-@"); |
| ID GC_VALUE::inv_id = rb_intern("~"); |
| |
| ID GC_VALUE::add_id = rb_intern("+"); |
| ID GC_VALUE::sub_id = rb_intern("-"); |
| ID GC_VALUE::mul_id = rb_intern("*"); |
| ID GC_VALUE::div_id = rb_intern("/"); |
| ID GC_VALUE::mod_id = rb_intern("%"); |
| |
| ID GC_VALUE::and_id = rb_intern("&"); |
| ID GC_VALUE::or_id = rb_intern("|"); |
| ID GC_VALUE::xor_id = rb_intern("^"); |
| |
| ID GC_VALUE::lshift_id = rb_intern("<<"); |
| ID GC_VALUE::rshift_id = rb_intern(">>"); |
| |
| SwigGCReferences SwigGCReferences::s_references; |
| |
| typedef GC_VALUE LANGUAGE_OBJ; |
| |
| } // namespace swig |
| |
| } // %fragment(GC_VALUE_definition) |
| |
| |
| |
| namespace swig { |
| |
| %apply VALUE {GC_VALUE}; |
| |
| // Make sure this is the last typecheck done |
| %typecheck(999999,fragment="GC_VALUE_definition",noblock=1) GC_VALUE, GC_VALUE&, |
| const GC_VALUE& { $1 = 1; }; |
| |
| /* For input */ |
| %typemap(in,fragment="GC_VALUE_definition",noblock=1) GC_VALUE* (GC_VALUE r), GC_VALUE& (GC_VALUE r) { |
| r = $input; $1 = &r; |
| } |
| |
| /* For output */ |
| %typemap(out,fragment="GC_VALUE_definition",noblock=1) GC_VALUE { |
| $result = (VALUE)$1; |
| } |
| |
| %typemap(out,fragment="GC_VALUE_definition",noblock=1) GC_VALUE*, GC_VALUE const & { |
| $result = (VALUE)*$1; |
| } |
| |
| %nodirector GC_VALUE; |
| |
| // We ignore the constructor so that user can never create a GC_VALUE |
| // manually |
| %ignore GC_VALUE::GC_VALUE; |
| |
| struct GC_VALUE { |
| VALUE inspect() const; |
| VALUE to_s() const; |
| GC_VALUE(); |
| protected: |
| GC_VALUE(const GC_VALUE&); |
| ~GC_VALUE(); |
| }; |
| |
| %exception GC_VALUE {}; |
| |
| |
| %ignore LANGUAGE_OBJ; |
| typedef GC_VALUE LANGUAGE_OBJ; |
| } |
| |
| |
| %init { |
| swig::SwigGCReferences::initialize(); |
| } |
| |
| |
| |
| // |
| // Fragment that contains traits to properly deal with GC_VALUE. |
| // These functions may be invoked as a need of the from(), asval(), |
| // asptr() and as() template functors, usually used in %typemaps. |
| // |
| %fragment(SWIG_Traits_frag(swig::GC_VALUE),"header",fragment="StdTraits",fragment="GC_VALUE_definition") { |
| namespace swig { |
| template <> struct traits<GC_VALUE > { |
| typedef value_category category; |
| static const char* type_name() { return "GC_VALUE"; } |
| }; |
| |
| template <> struct traits_from<GC_VALUE> { |
| typedef GC_VALUE value_type; |
| static VALUE from(const value_type& val) { |
| return static_cast<VALUE>(val); |
| } |
| }; |
| |
| template <> |
| struct traits_check<GC_VALUE, value_category> { |
| static bool check(GC_VALUE) { |
| return true; |
| } |
| }; |
| |
| template <> struct traits_asval<GC_VALUE > { |
| typedef GC_VALUE value_type; |
| static int asval(VALUE obj, value_type *val) { |
| if (val) *val = obj; |
| return SWIG_OK; |
| } |
| }; |
| } // swig |
| } // %fragment(traits for swig::GC_VALUE) |
| |
| |
| #endif // __cplusplus |
| |