You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
395 lines
8.8 KiB
395 lines
8.8 KiB
#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. |
|
|
|
*/ |
|
|
|
namespace swig { |
|
|
|
%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 {}; |
|
|
|
|
|
%apply VALUE {GC_VALUE}; |
|
|
|
// Make sure this is the last typecheck done |
|
%typecheck(999999,noblock=1) GC_VALUE, GC_VALUE&, |
|
const GC_VALUE& { $1 = 1; }; |
|
|
|
/* For input */ |
|
%typemap(in,noblock=1) GC_VALUE* (GC_VALUE r), GC_VALUE& (GC_VALUE r) { |
|
r = $input; $1 = &r; |
|
} |
|
|
|
/* For output */ |
|
%typemap(out,noblock=1) GC_VALUE { |
|
$result = (VALUE)$1; |
|
} |
|
|
|
%typemap(out,noblock=1) GC_VALUE*, GC_VALUE const & { |
|
$result = (VALUE)*$1; |
|
} |
|
|
|
%ignore LANGUAGE_OBJ; |
|
typedef GC_VALUE LANGUAGE_OBJ; |
|
} |
|
|
|
|
|
%{ |
|
namespace swig { |
|
class GC_VALUE { |
|
protected: |
|
// Hash of all GC_VALUE's currently in use |
|
static VALUE _hash; |
|
|
|
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: |
|
static void initialize() |
|
{ |
|
if ( _hash == Qnil ) |
|
{ |
|
_hash = rb_hash_new(); |
|
rb_gc_register_address( &_hash ); |
|
} |
|
} |
|
|
|
// this function is never called. Provided for symmetry only. |
|
static void cleanup() |
|
{ |
|
rb_gc_unregister_address( &_hash ); |
|
} |
|
|
|
GC_VALUE() : _obj( Qnil ) |
|
{ |
|
} |
|
|
|
GC_VALUE(const GC_VALUE& item) : _obj(item._obj) |
|
{ |
|
GC_register(); |
|
} |
|
|
|
GC_VALUE(VALUE obj) :_obj(obj) |
|
{ |
|
GC_register(); |
|
} |
|
|
|
~GC_VALUE() |
|
{ |
|
GC_unregister(); |
|
} |
|
|
|
GC_VALUE & operator=(const GC_VALUE& item) |
|
{ |
|
GC_unregister(); |
|
_obj = item._obj; |
|
GC_register(); |
|
return *this; |
|
} |
|
|
|
void GC_register() |
|
{ |
|
if ( FIXNUM_P(_obj) || SPECIAL_CONST_P(_obj) || SYMBOL_P(_obj) ) |
|
return; |
|
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() |
|
{ |
|
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; |
|
|
|
VALUE val = rb_hash_aref( _hash, _obj ); |
|
unsigned n = FIXNUM_P(val) ? NUM2UINT(val) : 1; |
|
--n; |
|
if ( n ) |
|
rb_hash_aset( _hash, _obj, INT2NUM(n) ); |
|
else |
|
rb_hash_delete( _hash, _obj ); |
|
} |
|
|
|
operator VALUE() const |
|
{ |
|
return _obj; |
|
} |
|
|
|
VALUE inspect() const |
|
{ |
|
return rb_inspect(_obj); |
|
} |
|
|
|
VALUE to_s() const |
|
{ |
|
return rb_inspect(_obj); |
|
} |
|
|
|
static VALUE swig_protect_funcall( VALUE p ) |
|
{ |
|
OpArgs* args = (OpArgs*) p; |
|
return rb_funcall( args->src, args->id, args->nargs, args->target ); |
|
} |
|
|
|
|
|
#define GC_VALUE_CMP( op_id, op, cmp, cmpval ) \ |
|
bool op( const GC_VALUE& other ) const \ |
|
{ \ |
|
if ( FIXNUM_P(_obj) && FIXNUM_P(other._obj) ) \ |
|
{ \ |
|
return _obj cmp other._obj; \ |
|
} \ |
|
bool res = false; \ |
|
VALUE ret = Qnil; \ |
|
SWIG_RUBY_THREAD_BEGIN_BLOCK; \ |
|
if ( rb_respond_to( _obj, op_id ) == Qtrue ) \ |
|
{ \ |
|
int status; \ |
|
OpArgs args; \ |
|
args.src = _obj; \ |
|
args.id = op_id; \ |
|
args.nargs = 1; \ |
|
args.target = VALUE(other); \ |
|
ret = rb_protect( PROTECTFUNC(swig_protect_funcall), \ |
|
VALUE(&args), &status ); \ |
|
} \ |
|
if ( ret == Qnil ) { \ |
|
VALUE a = rb_funcall( _obj, hash_id, 0 ); \ |
|
VALUE b = rb_funcall( VALUE(other), hash_id, 0 ); \ |
|
res = a cmp b; \ |
|
} \ |
|
else \ |
|
{ \ |
|
res = RTEST( ret ); \ |
|
} \ |
|
SWIG_RUBY_THREAD_END_BLOCK; \ |
|
return res; \ |
|
} |
|
|
|
|
|
GC_VALUE_CMP( eq_id, operator==, ==, == 0 ) |
|
GC_VALUE_CMP( lt_id, operator<, < , < 0 ) |
|
GC_VALUE_CMP( le_id, operator<=, <=, <= 0 ) |
|
GC_VALUE_CMP( gt_id, operator>, > , > 0 ) |
|
GC_VALUE_CMP( ge_id, operator>=, >=, >= 0 ) |
|
#undef GC_VALUE_CMP |
|
|
|
bool operator!=( const GC_VALUE& other ) |
|
{ |
|
return !(this->operator==(other)); |
|
} |
|
|
|
#define GC_VALUE_UNARY( proc_id, op ) \ |
|
GC_VALUE op() const \ |
|
{ \ |
|
VALUE ret = Qnil; \ |
|
SWIG_RUBY_THREAD_BEGIN_BLOCK; \ |
|
int status; \ |
|
OpArgs args; \ |
|
args.src = _obj; \ |
|
args.id = proc_id; \ |
|
args.nargs = 0; \ |
|
args.target = Qnil; \ |
|
ret = rb_protect( PROTECTFUNC(swig_protect_funcall), VALUE(&args), \ |
|
&status ); \ |
|
SWIG_RUBY_THREAD_END_BLOCK; \ |
|
return ret; \ |
|
} |
|
|
|
GC_VALUE_UNARY( pos_id, operator+ ) |
|
GC_VALUE_UNARY( neg_id, operator- ) |
|
GC_VALUE_UNARY( inv_id, operator~ ) |
|
#undef GC_VALUE_BINARY |
|
|
|
#define GC_VALUE_BINARY( proc_id, op ) \ |
|
GC_VALUE op( const GC_VALUE& other ) const \ |
|
{ \ |
|
VALUE ret = Qnil; \ |
|
SWIG_RUBY_THREAD_BEGIN_BLOCK; \ |
|
int status; \ |
|
OpArgs args; \ |
|
args.src = _obj; \ |
|
args.id = proc_id; \ |
|
args.nargs = 1; \ |
|
args.target = VALUE(other); \ |
|
ret = rb_protect( PROTECTFUNC(swig_protect_funcall), VALUE(&args), \ |
|
&status ); \ |
|
SWIG_RUBY_THREAD_END_BLOCK; \ |
|
return GC_VALUE(ret); \ |
|
} |
|
|
|
GC_VALUE_BINARY( add_id, operator+ ); |
|
GC_VALUE_BINARY( sub_id, operator- ); |
|
GC_VALUE_BINARY( mul_id, operator* ); |
|
GC_VALUE_BINARY( div_id, operator/ ); |
|
GC_VALUE_BINARY( mod_id, operator% ); |
|
|
|
GC_VALUE_BINARY( and_id, operator& ); |
|
GC_VALUE_BINARY( xor_id, operator^ ); |
|
GC_VALUE_BINARY( or_id, operator| ); |
|
|
|
GC_VALUE_BINARY( lshift_id, operator<< ); |
|
GC_VALUE_BINARY( rshift_id, operator>> ); |
|
#undef GC_VALUE_BINARY |
|
|
|
}; |
|
|
|
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(">>"); |
|
|
|
VALUE GC_VALUE::_hash = Qnil; |
|
|
|
typedef GC_VALUE LANGUAGE_OBJ; |
|
|
|
} // namespace swig |
|
|
|
%} |
|
|
|
|
|
%init { |
|
swig::GC_VALUE::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") { |
|
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 |
|
|
|
|