1 #ifdef __cplusplus 2 3 /* 4 GC_VALUE is used as a replacement of Ruby's VALUE. 5 GC_VALUE automatically handles registering and unregistering 6 of the underlying Ruby object with the GC. 7 8 It can be used if you want to create STL containers of VALUEs, such as: 9 10 std::vector< GC_VALUE >; 11 12 or as a member variable: 13 14 struct A { 15 GC_VALUE _obj; 16 A(VALUE o) : _obj(o) { 17 } 18 }; 19 20 or as a input/output value (not much use for this, as VALUE works just as 21 well here, thou): 22 23 GC_VALUE func(GC_VALUE obj) { 24 GC_VALUE out = rb_obj_classname(obj); 25 return out; 26 } 27 28 29 GC_VALUE is 'visible' at the wrapped side, so you can do: 30 31 %template(RubyVector) std::vector<swig::GC_VALUE>; 32 33 and all the proper typemaps will be used. 34 35 */ 36 37 %fragment("GC_VALUE_definition","header") { 38 namespace swig { 39 class SwigGCReferences { 40 // Hash of all GC_VALUE's currently in use 41 static SwigGCReferences s_references; 42 43 VALUE _hash; 44 45 SwigGCReferences() : _hash(Qnil) { 46 } 47 ~SwigGCReferences() { 48 if (_hash != Qnil) 49 rb_gc_unregister_address(&_hash); 50 } 51 static void EndProcHandler(VALUE) { 52 // Ruby interpreter ending - _hash can no longer be accessed. 53 s_references._hash = Qnil; 54 } 55 public: 56 static SwigGCReferences& instance() { 57 return s_references; 58 } 59 static void initialize() { 60 if (s_references._hash == Qnil) { 61 rb_set_end_proc(&EndProcHandler, Qnil); 62 s_references._hash = rb_hash_new(); 63 rb_gc_register_address(&s_references._hash); 64 } 65 } 66 void GC_register(VALUE& obj) { 67 if (FIXNUM_P(obj) || SPECIAL_CONST_P(obj) || SYMBOL_P(obj)) 68 return; 69 if (_hash != Qnil) { 70 VALUE val = rb_hash_aref(_hash, obj); 71 unsigned n = FIXNUM_P(val) ? NUM2UINT(val) : 0; 72 ++n; 73 rb_hash_aset(_hash, obj, INT2NUM(n)); 74 } 75 } 76 void GC_unregister(const VALUE& obj) { 77 if (FIXNUM_P(obj) || SPECIAL_CONST_P(obj) || SYMBOL_P(obj)) 78 return; 79 // this test should not be needed but I've noticed some very erratic 80 // behavior of none being unregistered in some very rare situations. 81 if (BUILTIN_TYPE(obj) == T_NONE) 82 return; 83 if (_hash != Qnil) { 84 VALUE val = rb_hash_aref(s_references._hash, obj); 85 unsigned n = FIXNUM_P(val) ? NUM2UINT(val) : 1; 86 --n; 87 if (n) 88 rb_hash_aset(s_references._hash, obj, INT2NUM(n)); 89 else 90 rb_hash_delete(s_references._hash, obj); 91 } 92 } 93 }; 94 95 class GC_VALUE { 96 protected: 97 VALUE _obj; 98 99 static ID hash_id; 100 static ID lt_id; 101 static ID gt_id; 102 static ID eq_id; 103 static ID le_id; 104 static ID ge_id; 105 106 static ID pos_id; 107 static ID neg_id; 108 static ID inv_id; 109 110 static ID add_id; 111 static ID sub_id; 112 static ID mul_id; 113 static ID div_id; 114 static ID mod_id; 115 116 static ID and_id; 117 static ID or_id; 118 static ID xor_id; 119 120 static ID lshift_id; 121 static ID rshift_id; 122 123 struct OpArgs 124 { 125 VALUE src; 126 ID id; 127 int nargs; 128 VALUE target; 129 }; 130 131 132 public: 133 GC_VALUE() : _obj(Qnil) 134 { 135 } 136 137 GC_VALUE(const GC_VALUE& item) : _obj(item._obj) 138 { 139 SwigGCReferences::instance().GC_register(_obj); 140 } 141 142 GC_VALUE(VALUE obj) :_obj(obj) 143 { 144 SwigGCReferences::instance().GC_register(_obj); 145 } 146 147 ~GC_VALUE() 148 { 149 SwigGCReferences::instance().GC_unregister(_obj); 150 } 151 152 GC_VALUE & operator=(const GC_VALUE& item) 153 { 154 SwigGCReferences::instance().GC_unregister(_obj); 155 _obj = item._obj; 156 SwigGCReferences::instance().GC_register(_obj); 157 return *this; 158 } 159 160 operator VALUE() const 161 { 162 return _obj; 163 } 164 165 VALUE inspect() const 166 { 167 return rb_inspect(_obj); 168 } 169 170 VALUE to_s() const 171 { 172 return rb_inspect(_obj); 173 } 174 175 static VALUE swig_rescue_swallow(VALUE) 176 { 177 /* 178 VALUE errstr = rb_obj_as_string(rb_errinfo()); 179 printf("Swallowing error: '%s'\n", RSTRING_PTR(StringValue(errstr))); 180 */ 181 return Qnil; /* Swallow Ruby exception */ 182 } 183 184 static VALUE swig_rescue_funcall(VALUE p) 185 { 186 OpArgs* args = (OpArgs*) p; 187 return rb_funcall(args->src, args->id, args->nargs, args->target); 188 } 189 190 bool relational_equal_op(const GC_VALUE& other, const ID& op_id, bool (*op_func)(const VALUE& a, const VALUE& b)) const 191 { 192 if (FIXNUM_P(_obj) && FIXNUM_P(other._obj)) { 193 return op_func(_obj, other._obj); 194 } 195 bool res = false; 196 VALUE ret = Qnil; 197 SWIG_RUBY_THREAD_BEGIN_BLOCK; 198 if (rb_respond_to(_obj, op_id)) { 199 OpArgs args; 200 args.src = _obj; 201 args.id = op_id; 202 args.nargs = 1; 203 args.target = VALUE(other); 204 ret = rb_rescue(RUBY_METHOD_FUNC(swig_rescue_funcall), VALUE(&args), 205 (RUBY_METHOD_FUNC(swig_rescue_swallow)), Qnil); 206 } 207 if (ret == Qnil) { 208 VALUE a = rb_funcall( _obj, hash_id, 0 ); 209 VALUE b = rb_funcall( VALUE(other), hash_id, 0 ); 210 res = op_func(a, b); 211 } else { 212 res = RTEST(ret); 213 } 214 SWIG_RUBY_THREAD_END_BLOCK; 215 return res; 216 } 217 218 static bool operator_eq(const VALUE& a, const VALUE& b) { return a == b; } 219 static bool operator_lt(const VALUE& a, const VALUE& b) { return a < b; } 220 static bool operator_le(const VALUE& a, const VALUE& b) { return a <= b; } 221 static bool operator_gt(const VALUE& a, const VALUE& b) { return a > b; } 222 static bool operator_ge(const VALUE& a, const VALUE& b) { return a >= b; } 223 224 bool operator==(const GC_VALUE& other) const { return relational_equal_op(other, eq_id, operator_eq); } 225 bool operator<(const GC_VALUE& other) const { return relational_equal_op(other, lt_id, operator_lt); } 226 bool operator<=(const GC_VALUE& other) const { return relational_equal_op(other, le_id, operator_le); } 227 bool operator>(const GC_VALUE& other) const { return relational_equal_op(other, gt_id, operator_gt); } 228 bool operator>=(const GC_VALUE& other) const { return relational_equal_op(other, ge_id, operator_ge); } 229 230 bool operator!=(const GC_VALUE& other) const 231 { 232 return !(this->operator==(other)); 233 } 234 235 GC_VALUE unary_op(const ID& op_id) const 236 { 237 VALUE ret = Qnil; 238 SWIG_RUBY_THREAD_BEGIN_BLOCK; 239 OpArgs args; 240 args.src = _obj; 241 args.id = op_id; 242 args.nargs = 0; 243 args.target = Qnil; 244 ret = rb_rescue(RUBY_METHOD_FUNC(swig_rescue_funcall), VALUE(&args), 245 (RUBY_METHOD_FUNC(swig_rescue_swallow)), Qnil); 246 SWIG_RUBY_THREAD_END_BLOCK; 247 return ret; 248 } 249 250 GC_VALUE operator+() const { return unary_op(pos_id); } 251 GC_VALUE operator-() const { return unary_op(neg_id); } 252 GC_VALUE operator~() const { return unary_op(inv_id); } 253 254 GC_VALUE binary_op(const GC_VALUE& other, const ID& op_id) const 255 { 256 VALUE ret = Qnil; 257 SWIG_RUBY_THREAD_BEGIN_BLOCK; 258 OpArgs args; 259 args.src = _obj; 260 args.id = op_id; 261 args.nargs = 1; 262 args.target = VALUE(other); 263 ret = rb_rescue(RUBY_METHOD_FUNC(swig_rescue_funcall), VALUE(&args), 264 (RUBY_METHOD_FUNC(swig_rescue_swallow)), Qnil); 265 SWIG_RUBY_THREAD_END_BLOCK; 266 return GC_VALUE(ret); 267 } 268 269 GC_VALUE operator+(const GC_VALUE& other) const { return binary_op(other, add_id); } 270 GC_VALUE operator-(const GC_VALUE& other) const { return binary_op(other, sub_id); } 271 GC_VALUE operator*(const GC_VALUE& other) const { return binary_op(other, mul_id); } 272 GC_VALUE operator/(const GC_VALUE& other) const { return binary_op(other, div_id); } 273 GC_VALUE operator%(const GC_VALUE& other) const { return binary_op(other, mod_id); } 274 GC_VALUE operator&(const GC_VALUE& other) const { return binary_op(other, and_id); } 275 GC_VALUE operator^(const GC_VALUE& other) const { return binary_op(other, xor_id); } 276 GC_VALUE operator|(const GC_VALUE& other) const { return binary_op(other, or_id); } 277 GC_VALUE operator<<(const GC_VALUE& other) const { return binary_op(other, lshift_id); } 278 GC_VALUE operator>>(const GC_VALUE& other) const { return binary_op(other, rshift_id); } 279 }; 280 281 ID GC_VALUE::hash_id = rb_intern("hash"); 282 ID GC_VALUE::lt_id = rb_intern("<"); 283 ID GC_VALUE::gt_id = rb_intern(">"); 284 ID GC_VALUE::eq_id = rb_intern("=="); 285 ID GC_VALUE::le_id = rb_intern("<="); 286 ID GC_VALUE::ge_id = rb_intern(">="); 287 288 ID GC_VALUE::pos_id = rb_intern("+@"); 289 ID GC_VALUE::neg_id = rb_intern("-@"); 290 ID GC_VALUE::inv_id = rb_intern("~"); 291 292 ID GC_VALUE::add_id = rb_intern("+"); 293 ID GC_VALUE::sub_id = rb_intern("-"); 294 ID GC_VALUE::mul_id = rb_intern("*"); 295 ID GC_VALUE::div_id = rb_intern("/"); 296 ID GC_VALUE::mod_id = rb_intern("%"); 297 298 ID GC_VALUE::and_id = rb_intern("&"); 299 ID GC_VALUE::or_id = rb_intern("|"); 300 ID GC_VALUE::xor_id = rb_intern("^"); 301 302 ID GC_VALUE::lshift_id = rb_intern("<<"); 303 ID GC_VALUE::rshift_id = rb_intern(">>"); 304 305 SwigGCReferences SwigGCReferences::s_references; 306 307 typedef GC_VALUE LANGUAGE_OBJ; 308 309 } // namespace swig 310 311 } // %fragment(GC_VALUE_definition) 312 313 314 315 namespace swig { 316 317 %apply VALUE {GC_VALUE}; 318 319 // Make sure this is the last typecheck done 320 %typecheck(999999,fragment="GC_VALUE_definition",noblock=1) GC_VALUE, GC_VALUE&, 321 const GC_VALUE& { $1 = 1; }; 322 323 /* For input */ 324 %typemap(in,fragment="GC_VALUE_definition",noblock=1) GC_VALUE* (GC_VALUE r), GC_VALUE& (GC_VALUE r) { 325 r = $input; $1 = &r; 326 } 327 328 /* For output */ 329 %typemap(out,fragment="GC_VALUE_definition",noblock=1) GC_VALUE { 330 $result = (VALUE)$1; 331 } 332 333 %typemap(out,fragment="GC_VALUE_definition",noblock=1) GC_VALUE*, GC_VALUE const & { 334 $result = (VALUE)*$1; 335 } 336 337 %nodirector GC_VALUE; 338 339 // We ignore the constructor so that user can never create a GC_VALUE 340 // manually 341 %ignore GC_VALUE::GC_VALUE; 342 343 struct GC_VALUE { 344 VALUE inspect() const; 345 VALUE to_s() const; 346 GC_VALUE(); 347 protected: 348 GC_VALUE(const GC_VALUE&); 349 ~GC_VALUE(); 350 }; 351 352 %exception GC_VALUE {}; 353 354 355 %ignore LANGUAGE_OBJ; 356 typedef GC_VALUE LANGUAGE_OBJ; 357 } 358 359 360 %init { 361 swig::SwigGCReferences::initialize(); 362 } 363 364 365 366 // 367 // Fragment that contains traits to properly deal with GC_VALUE. 368 // These functions may be invoked as a need of the from(), asval(), 369 // asptr() and as() template functors, usually used in %typemaps. 370 // 371 %fragment(SWIG_Traits_frag(swig::GC_VALUE),"header",fragment="StdTraits",fragment="GC_VALUE_definition") { 372 namespace swig { 373 template <> struct traits<GC_VALUE > { 374 typedef value_category category; 375 static const char* type_name() { return "GC_VALUE"; } 376 }; 377 378 template <> struct traits_from<GC_VALUE> { 379 typedef GC_VALUE value_type; 380 static VALUE from(const value_type& val) { 381 return static_cast<VALUE>(val); 382 } 383 }; 384 385 template <> 386 struct traits_check<GC_VALUE, value_category> { 387 static bool check(GC_VALUE) { 388 return true; 389 } 390 }; 391 392 template <> struct traits_asval<GC_VALUE > { 393 typedef GC_VALUE value_type; 394 static int asval(VALUE obj, value_type *val) { 395 if (val) *val = obj; 396 return SWIG_OK; 397 } 398 }; 399 } // swig 400 } // %fragment(traits for swig::GC_VALUE) 401 402 403 #endif // __cplusplus 404 405