/
niceSOFT
/
swig
Обзор
Документация
Войти
/
niceSOFT
/
swig
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
Lib/ruby/rubyclasses.swg
428 строк
12 KB
William S Fulton
Ruby: complete the TypedData migration - GC marker, docs and test enforcement
09 июн 2026, 00:16
09 июн 2026, 00:16
fd008b1
Код
Авторство
О чём код?
#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 { // Registry of the Ruby objects that GC_VALUE holds as raw VALUEs in C++. // Keeping a reference here keeps them alive; marking them with the pinning // mark rb_gc_mark() (rather than rb_gc_mark_movable()) also stops Ruby's GC // compaction from relocating them, which would otherwise leave the C++ copies // dangling. The registry is a Ruby st_table keyed by the VALUE itself, so // registering/unregistering never dispatches a method on the object (unlike a // Ruby Hash) and is therefore safe even for an object pending collection. class SwigGCReferences { st_table *_table; VALUE _marker; SwigGCReferences() : _table(0), _marker(Qnil) { } ~SwigGCReferences() { } static void EndProcHandler(VALUE) { // Ruby interpreter ending - the registry can no longer be accessed. SwigGCReferences &s_references = instance(); s_references._table = 0; } static int MarkEntry(st_data_t key, st_data_t, st_data_t) { rb_gc_mark((VALUE)key); return ST_CONTINUE; } static void MarkReferences(void *) { SwigGCReferences &s_references = instance(); if (s_references._table) st_foreach(s_references._table, SWIG_RUBY_INT_ANYARGS_FUNC(MarkEntry), 0); } public: static SwigGCReferences& instance() { // Registry of all GC_VALUE's currently in use static SwigGCReferences s_references; return s_references; } static void initialize() { SwigGCReferences &s_references = instance(); if (!s_references._table) { rb_set_end_proc(&EndProcHandler, Qnil); s_references._table = st_init_numtable(); // A hidden object whose only purpose is to run MarkReferences during GC; // registering its address keeps the object and mark hook alive. The data // pointer must be non-NULL: Ruby's GC skips a TypedData object's mark // function when its data pointer is NULL. static const rb_data_type_t swig_gcrefs_marker_type = { "SwigGCReferences", { MarkReferences, 0, 0, 0 }, 0, 0 #ifdef RUBY_TYPED_FREE_IMMEDIATELY , RUBY_TYPED_FREE_IMMEDIATELY #endif }; s_references._marker = TypedData_Wrap_Struct(rb_cObject, &swig_gcrefs_marker_type, &s_references); rb_gc_register_address(&s_references._marker); } } void GC_register(VALUE& obj) { if (FIXNUM_P(obj) || SPECIAL_CONST_P(obj) || SYMBOL_P(obj)) return; if (_table) { st_data_t count = 0; st_lookup(_table, (st_data_t)obj, &count); st_insert(_table, (st_data_t)obj, (st_data_t)(count + 1)); } } void GC_unregister(const VALUE& obj) { if (FIXNUM_P(obj) || SPECIAL_CONST_P(obj) || SYMBOL_P(obj)) return; if (_table) { st_data_t count = 0; if (st_lookup(_table, (st_data_t)obj, &count)) { if (count > 1) { st_insert(_table, (st_data_t)obj, (st_data_t)(count - 1)); } else { st_data_t key = (st_data_t)obj; st_delete(_table, &key, 0); } } } } }; 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) { /* 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(VALUEFUNC(swig_rescue_funcall), VALUE(&args), (VALUEFUNC(swig_rescue_swallow)), Qnil); } if (ret == Qnil) { VALUE a = rb_funcall2( _obj, hash_id, 0, 0 ); VALUE b = rb_funcall2( VALUE(other), hash_id, 0, 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(VALUEFUNC(swig_rescue_funcall), VALUE(&args), (VALUEFUNC(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(VALUEFUNC(swig_rescue_funcall), VALUE(&args), (VALUEFUNC(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(">>"); 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