Home | History | Annotate | Download | only in ruby
      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