swig/Lib/ruby/rubyclasses.swg
2013-04-06 00:30:50 +01:00

411 lines
10 KiB
Text

#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 );
}
%#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 ) ) \
{ \
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 = 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; \
OpArgs args; \
args.src = _obj; \
args.id = proc_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_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; \
OpArgs args; \
args.src = _obj; \
args.id = proc_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_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(">>");
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