value.cc
Go to the documentation of this file.
00001 #include "typelib.hh"
00002 #include <ruby.h>
00003 
00004 #include <sstream>
00005 #include <limits>
00006 #include <typelib/value.hh>
00007 #include <typelib/registry.hh>
00008 #include <typelib/typevisitor.hh>
00009 #include <typelib/csvoutput.hh>
00010 #include <typelib/endianness.hh>
00011 #include <typelib/value_ops.hh>
00012 #include <typelib/typename.hh>
00013 
00014 #include <iostream>
00015 
00016 using namespace Typelib;
00017 using std::numeric_limits;
00018 using std::vector;
00019 using namespace typelib_ruby;
00020 
00021 void* value_root_ptr(VALUE value)
00022 {
00023     VALUE parent = Qnil;
00024     while (RTEST(value))
00025     {
00026         parent = value;
00027         value = rb_iv_get(value, "@parent");
00028     }
00029     if (RTEST(parent))
00030     {
00031         Value v = rb2cxx::object<Value>(parent);
00032         return v.getData();
00033     }
00034     else return 0;
00035 }
00036 
00037 /* There are constraints when creating a Ruby wrapper for a Type, mainly
00038  * for avoiding GC issues. This function does the work.
00039  *
00040  * The main issue is that Ruby/DL does not keep refcounters on memory.
00041  * Instead, it returns always the same DLPtr object for a given memory
00042  * pointer. Problems arise when one of the two following situations are met:
00043  * * we reference a memory zone inside another memory zone (for instance,
00044  *   an array element or a structure field). In that case, the DLPtr object
00045  *   must not free the allocated zone. 
00046  * * two Typelib objects reference the same memory zone (first array
00047  *   element or first field of a structure). In that case, we must reuse the
00048  *   same DLPtr object, or DL will override the free function of the other
00049  *   DLPtr object -- which is obviously wrong, but nevertheless done.
00050  */
00051 VALUE cxx2rb::value_wrap(Value v, VALUE registry, VALUE parent)
00052 {
00053     VALUE type = type_wrap(v.getType(), registry);
00054 #   ifdef VERBOSE
00055     Value parent_value = rb2cxx::object<Value>(parent);
00056     fprintf(stderr, "wrapping Typelib::Value %p from C++, type=%s, parent=%p and root=%p\n", v.getData(), v.getType().getName().c_str(), parent_value.getData(), value_root_ptr(parent));
00057 #   endif
00058     VALUE ptr  = memory_wrap(v.getData(), false, value_root_ptr(parent));
00059     VALUE wrapper = rb_funcall(type, rb_intern("wrap"), 1, ptr);
00060 
00061     rb_iv_set(wrapper, "@parent", parent);
00062     return wrapper;
00063 }
00064 
00065 static VALUE value_allocate(Type const& type, VALUE registry)
00066 {
00067     VALUE rb_type = cxx2rb::type_wrap(type, registry);
00068 #   ifdef VERBOSE
00069     fprintf(stderr, "allocating new value of type %s\n", type.getName().c_str());
00070 #   endif
00071     VALUE ptr     = memory_allocate(type.getSize());
00072     memory_init(ptr, rb_type);
00073     VALUE wrapper = rb_funcall(rb_type, rb_intern("wrap"), 1, ptr);
00074     return wrapper;
00075 }
00076 
00077 namespace typelib_ruby {
00078     VALUE cType  = Qnil;
00079     VALUE cNumeric       = Qnil;
00080     VALUE cOpaque        = Qnil;
00081     VALUE cNull  = Qnil;
00082     VALUE cIndirect  = Qnil;
00083     VALUE cPointer   = Qnil;
00084     VALUE cArray     = Qnil;
00085     VALUE cCompound  = Qnil;
00086     VALUE cEnum      = Qnil;
00087     VALUE cContainer = Qnil;
00088 }
00089 
00090 VALUE cxx2rb::class_of(Typelib::Type const& type)
00091 {
00092     using Typelib::Type;
00093     switch(type.getCategory()) {
00094         case Type::Numeric:     return cNumeric;
00095         case Type::Compound:    return cCompound;
00096         case Type::Pointer:     return cPointer;
00097         case Type::Array:       return cArray;
00098         case Type::Enum:        return cEnum;
00099         case Type::Container:   return cContainer;
00100         case Type::Opaque:      return cOpaque;
00101         case Type::NullType:    return cNull;
00102         default:                return cType;
00103     }
00104 }
00105 
00106 VALUE cxx2rb::type_wrap(Type const& type, VALUE registry)
00107 {
00108     // Type objects are unique, we can register Ruby wrappers based
00109     // on the type pointer (instead of using names)
00110     WrapperMap& wrappers = rb2cxx::object<RbRegistry>(registry).wrappers;
00111 
00112     WrapperMap::const_iterator it = wrappers.find(&type);
00113     if (it != wrappers.end())
00114         return it->second.second;
00115 
00116     VALUE base  = class_of(type);
00117     VALUE klass = rb_funcall(rb_cClass, rb_intern("new"), 1, base);
00118     VALUE rb_type = Data_Wrap_Struct(rb_cObject, 0, 0, const_cast<Type*>(&type));
00119     rb_iv_set(klass, "@registry", registry);
00120     rb_iv_set(klass, "@type", rb_type);
00121     rb_iv_set(klass, "@name", rb_str_new2(type.getName().c_str()));
00122     rb_iv_set(klass, "@null", (type.getCategory() == Type::NullType) ? Qtrue : Qfalse);
00123     rb_iv_set(klass, "@opaque", (type.getCategory() == Type::Opaque) ? Qtrue : Qfalse);
00124 
00125     if (rb_respond_to(klass, rb_intern("subclass_initialize")))
00126         rb_funcall(klass, rb_intern("subclass_initialize"), 0);
00127 
00128     wrappers.insert(std::make_pair(&type, std::make_pair(false, klass)));
00129     return klass;
00130 }
00131 
00132 /**********************************************
00133  * Typelib::Type
00134  */
00135 
00152 static VALUE type_to_csv(int argc, VALUE* argv, VALUE rbself)
00153 {
00154     VALUE basename = Qnil;
00155     VALUE separator = Qnil;
00156     rb_scan_args(argc, argv, "02", &basename, &separator);
00157 
00158     std::string bname = "", sep = " ";
00159     if (!NIL_P(basename)) bname = StringValuePtr(basename);
00160     if (!NIL_P(separator)) sep = StringValuePtr(separator);
00161 
00162     Type const& self(rb2cxx::object<Type>(rbself));
00163     std::ostringstream stream;
00164     stream << csv_header(self, bname, sep);
00165     std::string str = stream.str();
00166     return rb_str_new(str.c_str(), str.length());
00167 }
00168 
00169 /* call-seq:
00170  *  t.basename => name
00171  *
00172  * Returns the type name of the receiver with the namespace part
00173  * removed
00174  */
00175 static VALUE typelib_do_basename(VALUE mod, VALUE name)
00176 {
00177     std::string result = Typelib::getTypename(StringValuePtr(name));
00178     return rb_str_new(result.c_str(), result.length());
00179 }
00180 
00181 /* Internal helper method for Type#namespace */
00182 static VALUE typelib_do_namespace(VALUE mod, VALUE name)
00183 {
00184     std::string result = Typelib::getNamespace(StringValuePtr(name));
00185     return rb_str_new(result.c_str(), result.length());
00186 }
00187 
00188 /* Internal helper method for Type#namespace */
00189 static VALUE typelib_do_split_name(VALUE mod, VALUE name)
00190 {
00191     std::list<std::string> splitted = Typelib::splitTypename(StringValuePtr(name));
00192     VALUE result = rb_ary_new();
00193     for (std::list<std::string>::const_iterator it = splitted.begin(); it != splitted.end(); ++it)
00194         rb_ary_push(result, rb_str_new(it->c_str(), it->length()));
00195     return result;
00196 }
00197 
00198 
00199 /* call-seq:
00200  *  t1 == t2 => true or false
00201  *
00202  * Returns true if +t1+ and +t2+ are the same type definition.
00203  */
00204 static VALUE type_equal_operator(VALUE rbself, VALUE rbwith)
00205 { 
00206     if (! rb_respond_to(rbwith, rb_intern("superclass")))
00207         return Qfalse;
00208     if (rb_funcall(rbself, rb_intern("superclass"), 0) != rb_funcall(rbwith, rb_intern("superclass"), 0))
00209         return Qfalse;
00210     if (rbself == rbwith)
00211         return Qtrue;
00212 
00213     Type const& self(rb2cxx::object<Type>(rbself));
00214     Type const& with(rb2cxx::object<Type>(rbwith));
00215     bool result = (self == with) || self.isSame(with);
00216     return result ? Qtrue : Qfalse;
00217 }
00218 
00219 /* call-seq:
00220  *  type.size   => size
00221  *
00222  * Returns the size in bytes of instances of +type+
00223  */
00224 static VALUE type_size(VALUE self)
00225 {
00226     Type const& type(rb2cxx::object<Type>(self));
00227     return INT2FIX(type.getSize());
00228 }
00229 
00230 /* call-seq:
00231  *  type.dependencies => set_of_type
00232  *
00233  * Returns the set of Type subclasses that represent the types needed to build
00234  * +type+.
00235  */
00236 static VALUE type_dependencies(VALUE self)
00237 {
00238     Type const& type(rb2cxx::object<Type>(self));
00239 
00240     typedef std::set<Type const*> TypeSet;
00241     TypeSet dependencies = type.dependsOn();
00242     VALUE registry = type_get_registry(self);
00243 
00244     VALUE result = rb_ary_new();
00245     for (TypeSet::const_iterator it = dependencies.begin(); it != dependencies.end(); ++it)
00246         rb_ary_push(result, cxx2rb::type_wrap(**it, registry));
00247     return result;
00248 }
00249 
00250 /* call-seq:
00251  *  type.casts_to?(other_type) => true or false
00252  *
00253  * Returns true if a value that is described by +type+ can be manipulated using
00254  * +other_type+. This is a weak form of equality
00255  */
00256 static VALUE type_can_cast_to(VALUE self, VALUE to)
00257 {
00258     Type const& from_type(rb2cxx::object<Type>(self));
00259     Type const& to_type(rb2cxx::object<Type>(to));
00260     return from_type.canCastTo(to_type) ? Qtrue : Qfalse;
00261 }
00262 
00263 /*
00264  *  type.do_memory_layout(VALUE accept_pointers, VALUE accept_opaques, VALUE merge_skip_copy, VALUE remove_trailing_skips) => [operations]
00265  *
00266  * Returns a representation of the MemoryLayout for this type. If
00267  * +with_pointers+ is true, then pointers will be included in the layout.
00268  * Otherwise, an exception is raised if pointers are part of the type
00269  */
00270 static VALUE type_memory_layout(VALUE self, VALUE pointers, VALUE opaques, VALUE merge, VALUE remove_trailing_skips)
00271 {
00272     Type const& type(rb2cxx::object<Type>(self));
00273     VALUE registry = type_get_registry(self);
00274 
00275     VALUE result = rb_ary_new();
00276 
00277     VALUE rb_memcpy = ID2SYM(rb_intern("FLAG_MEMCPY"));
00278     VALUE rb_skip = ID2SYM(rb_intern("FLAG_SKIP"));
00279     VALUE rb_array = ID2SYM(rb_intern("FLAG_ARRAY"));
00280     VALUE rb_end = ID2SYM(rb_intern("FLAG_END"));
00281     VALUE rb_container = ID2SYM(rb_intern("FLAG_CONTAINER"));
00282 
00283     try {
00284         MemoryLayout layout = Typelib::layout_of(type, RTEST(pointers), RTEST(opaques), RTEST(merge), RTEST(remove_trailing_skips));
00285 
00286         // Now, convert into something representable in Ruby
00287         for (MemoryLayout::const_iterator it = layout.begin(); it != layout.end(); ++it)
00288         {
00289             switch(*it)
00290             {
00291                 case MemLayout::FLAG_MEMCPY:
00292                     rb_ary_push(result, rb_memcpy);
00293                     rb_ary_push(result, LONG2NUM(*(++it)));
00294                     break;
00295                 case MemLayout::FLAG_SKIP:
00296                     rb_ary_push(result, rb_skip);
00297                     rb_ary_push(result, LONG2NUM(*(++it)));
00298                     break;
00299                 case MemLayout::FLAG_ARRAY:
00300                     rb_ary_push(result, rb_array);
00301                     rb_ary_push(result, LONG2NUM(*(++it)));
00302                     break;
00303                 case MemLayout::FLAG_END:
00304                     rb_ary_push(result, rb_end);
00305                     break;
00306                 case MemLayout::FLAG_CONTAINER:
00307                     rb_ary_push(result, rb_container);
00308                     rb_ary_push(result, cxx2rb::type_wrap(*reinterpret_cast<Container*>(*(++it)), registry));
00309                     break;
00310                 default:
00311                     rb_raise(rb_eArgError, "error encountered while parsing memory layout");
00312             }
00313         }
00314 
00315     } catch(std::exception const& e) {
00316         rb_raise(rb_eArgError, "%s", e.what());
00317     }
00318 
00319     return result;
00320 }
00321 
00322 /* PODs are assignable, pointers are dereferenced */
00323 static VALUE type_is_assignable(Type const& type)
00324 {
00325     switch(type.getCategory())
00326     {
00327     case Type::Numeric:
00328         return INT2FIX(1);
00329     case Type::Pointer:
00330         return type_is_assignable( dynamic_cast<Pointer const&>(type).getIndirection());
00331     case Type::Enum:
00332         return INT2FIX(1);
00333     default:
00334         return INT2FIX(0);
00335     }
00336     // never reached
00337 }
00338 
00339 VALUE typelib_ruby::type_get_registry(VALUE self)
00340 {
00341     return rb_iv_get(self, "@registry");
00342 }
00343 
00344 
00345 /***********************************************************************************
00346  *
00347  * Wrapping of the Value class
00348  *
00349  */
00350 
00351 static void value_delete(void* self) { delete reinterpret_cast<Value*>(self); }
00352 
00353 static VALUE value_alloc(VALUE klass)
00354 { return Data_Wrap_Struct(klass, 0, value_delete, new Value); }
00355 
00356 static
00357 VALUE value_initialize(VALUE self, VALUE ptr)
00358 {
00359     Type const& t(rb2cxx::object<Type>(rb_class_of(self)));
00360 
00361     if (NIL_P(ptr) || rb_obj_is_kind_of(ptr, rb_cString))
00362     {
00363 #       ifdef VERBOSE
00364         fprintf(stderr, "allocating new value of type %s to copy an existing buffer\n", t.getName().c_str());
00365 #       endif
00366         VALUE buffer = memory_allocate(t.getSize());
00367         memory_init(buffer, rb_class_of(self));
00368         if (! NIL_P(ptr))
00369         {
00370             char* ruby_buffer = StringValuePtr(ptr);
00371             vector<uint8_t> cxx_buffer(ruby_buffer, ruby_buffer + RSTRING_LEN(ptr));
00372             try { Typelib::load(Value(memory_cptr(buffer), t), cxx_buffer); }
00373             catch(std::exception const& e)
00374             { rb_raise(rb_eArgError, "%s", e.what()); }
00375         }
00376 
00377         ptr = buffer;
00378     }
00379 
00380     // Protect 'ptr' against the GC
00381     rb_iv_set(self, "@ptr", ptr);
00382     Value& value  = rb2cxx::object<Value>(self);
00383     value = Value(memory_cptr(ptr), t);
00384 #   ifdef VERBOSE
00385     fprintf(stderr, "object %llu uses memory zone %p\n", NUM2ULL(rb_obj_id(self)), value.getData());
00386 #   endif
00387     return self;
00388 }
00389 
00390 static
00391 VALUE value_address(VALUE self)
00392 {
00393     Value value = rb2cxx::object<Value>(self);
00394     return LONG2NUM((long)value.getData());
00395 }
00396 
00397 static
00398 VALUE value_endian_swap(VALUE self)
00399 {
00400     Value& value = rb2cxx::object<Value>(self);
00401     CompileEndianSwapVisitor compiled;
00402     compiled.apply(value.getType());
00403 
00404     VALUE registry = value_get_registry(self);
00405     VALUE result   = value_allocate(value.getType(), registry);
00406     compiled.swap(value, rb2cxx::object<Value>(result));
00407     return result;
00408 }
00409 
00410 static
00411 VALUE value_endian_swap_b(VALUE self, VALUE rb_compile)
00412 {
00413     Value& value = rb2cxx::object<Value>(self);
00414     endian_swap(value);
00415     return self;
00416 }
00417 
00418 static
00419 VALUE value_do_cast(VALUE self, VALUE target_type)
00420 {
00421     Value& value = rb2cxx::object<Value>(self);
00422     Type const& to_type(rb2cxx::object<Type>(target_type));
00423 
00424     if (value.getType() == to_type)
00425         return self;
00426 
00427     VALUE registry = rb_iv_get(target_type, "@registry");
00428     Value casted(value.getData(), to_type);
00429 #   ifdef VERBOSE
00430     fprintf(stderr, "wrapping casted value\n");
00431 #   endif
00432 
00433     return cxx2rb::value_wrap(casted, registry, self);
00434 }
00435 
00436 static
00437 VALUE value_invalidate(VALUE self)
00438 {
00439     if (NIL_P(rb_iv_get(self, "@parent")))
00440         rb_raise(rb_eArgError, "cannot invalidate a toplevel value");
00441 
00442     Value& value = rb2cxx::object<Value>(self);
00443 #ifdef VERBOSE
00444     fprintf(stderr, "invalidating %llu, ptr=%p\n", NUM2ULL(rb_obj_id(self)), value.getData());
00445 #endif
00446     value = Value(0, value.getType());
00447     rb_funcall(rb_iv_get(self, "@ptr"), rb_intern("invalidate"), 0);
00448     return Qnil;
00449 }
00450 
00451 static
00452 VALUE value_do_byte_array(VALUE self, VALUE pointers, VALUE opaques, VALUE merge, VALUE remove_trailing_skips)
00453 {
00454     Value& value = rb2cxx::object<Value>(self);
00455     MemoryLayout layout = Typelib::layout_of(value.getType(), RTEST(pointers), RTEST(opaques), RTEST(merge), RTEST(remove_trailing_skips));
00456 
00457     vector<uint8_t> buffer;
00458     Typelib::dump(value, buffer, layout);
00459     return rb_str_new(reinterpret_cast<char*>(&buffer[0]), buffer.size());
00460 }
00461 
00462 /* call-seq:
00463  *  obj.marshalling_size => integer
00464  *
00465  * Returns the size of this value once marshalled by Typelib, i.e. the size of
00466  * the byte array returned by #to_byte_array
00467  */
00468 static
00469 VALUE value_marshalling_size(VALUE self)
00470 {
00471     Value& value = rb2cxx::object<Value>(self);
00472     try { return INT2NUM(Typelib::getDumpSize(value)); }
00473     catch(Typelib::NoLayout)
00474     { return Qnil; }
00475 }
00476 
00477 VALUE value_memory_eql_p(VALUE rbself, VALUE rbwith)
00478 {
00479     Value& self = rb2cxx::object<Value>(rbself);
00480     Value& with = rb2cxx::object<Value>(rbwith);
00481         
00482     if (self.getData() == with.getData())
00483         return Qtrue;
00484     
00485     // Type#== checks for type equality before calling memory_equal?
00486     Type const& type = self.getType();
00487     return memcmp(self.getData(), with.getData(), type.getSize()) == 0 ? Qtrue : Qfalse;
00488 }
00489 
00490 VALUE typelib_ruby::value_get_registry(VALUE self)
00491 {
00492     VALUE type = rb_funcall(self, rb_intern("class"), 0);
00493     return rb_iv_get(type, "@registry");
00494 }
00495 
00503 static VALUE value_to_csv(int argc, VALUE* argv, VALUE self)
00504 {
00505     VALUE separator = Qnil;
00506     rb_scan_args(argc, argv, "01", &separator);
00507 
00508     Value const& value(rb2cxx::object<Value>(self));
00509     std::string sep = " ";
00510     if (!NIL_P(separator)) sep = StringValuePtr(separator);
00511 
00512     std::ostringstream stream;
00513     stream << csv(value.getType(), value.getData(), sep);
00514     std::string str = stream.str();
00515     return rb_str_new(str.c_str(), str.length());
00516 }
00517 
00518 /* Initializes the memory to 0 */
00519 static VALUE value_zero(VALUE self)
00520 {
00521     Value const& value(rb2cxx::object<Value>(self));
00522     Typelib::zero(value);
00523     return self;
00524 }
00525 
00526 static VALUE typelib_do_copy(VALUE, VALUE to, VALUE from)
00527 {
00528     Value v_from = rb2cxx::object<Value>(from);
00529     Value v_to   = rb2cxx::object<Value>(to);
00530 
00531     if (v_from.getType() != v_to.getType())
00532     {
00533         // Do a deep check for type equality
00534         if (!v_from.getType().canCastTo(v_to.getType()))
00535             rb_raise(rb_eArgError, "cannot copy: types are not compatible");
00536     }
00537     Typelib::copy(v_to.getData(), v_from.getData(), v_from.getType());
00538     return to;
00539 }
00540 
00541 /* call-seq:
00542  *  Typelib.compare(to, from) => true or false
00543  *
00544  * Proper comparison of two values. +to+ and +from+'s types do not have to be of
00545  * the same registries, as long as the types can be cast'ed into each other.
00546  */
00547 static VALUE typelib_compare(VALUE, VALUE to, VALUE from)
00548 {
00549     Value v_from = rb2cxx::object<Value>(from);
00550     Value v_to   = rb2cxx::object<Value>(to);
00551 
00552     if (v_from.getType() != v_to.getType())
00553     {
00554         // Do a deep check for type equality
00555         if (!v_from.getType().canCastTo(v_to.getType()))
00556             rb_raise(rb_eArgError, "cannot compare: %s and %s are not compatible types",
00557                     v_from.getType().getName().c_str(),
00558                     v_to.getType().getName().c_str());
00559     }
00560     try {
00561         bool result = Typelib::compare(v_to.getData(), v_from.getData(), v_from.getType());
00562         return result ? Qtrue : Qfalse;
00563     } catch(std::exception const& e) {
00564         rb_raise(rb_eArgError, "%s", e.what());
00565     }
00566 }
00567 
00568 
00569 void typelib_ruby::Typelib_init_values()
00570 {
00571     VALUE mTypelib  = rb_define_module("Typelib");
00572     rb_define_singleton_method(mTypelib, "do_copy", RUBY_METHOD_FUNC(typelib_do_copy), 2);
00573     rb_define_singleton_method(mTypelib, "compare", RUBY_METHOD_FUNC(typelib_compare), 2);
00574 
00575     cType     = rb_define_class_under(mTypelib, "Type", rb_cObject);
00576     rb_define_alloc_func(cType, value_alloc);
00577     rb_define_singleton_method(cType, "==",            RUBY_METHOD_FUNC(type_equal_operator), 1);
00578     rb_define_singleton_method(cType, "size",          RUBY_METHOD_FUNC(&type_size), 0);
00579     rb_define_singleton_method(cType, "do_memory_layout", RUBY_METHOD_FUNC(&type_memory_layout), 4);
00580     rb_define_singleton_method(cType, "do_dependencies",  RUBY_METHOD_FUNC(&type_dependencies), 0);
00581     rb_define_singleton_method(cType, "casts_to?",     RUBY_METHOD_FUNC(&type_can_cast_to), 1);
00582     rb_define_method(cType, "__initialize__",   RUBY_METHOD_FUNC(&value_initialize), 1);
00583     rb_define_method(cType, "zero!",      RUBY_METHOD_FUNC(&value_zero), 0);
00584     rb_define_method(cType, "memory_eql?",      RUBY_METHOD_FUNC(&value_memory_eql_p), 1);
00585     rb_define_method(cType, "endian_swap",      RUBY_METHOD_FUNC(&value_endian_swap), 0);
00586     rb_define_method(cType, "endian_swap!",      RUBY_METHOD_FUNC(&value_endian_swap_b), 0);
00587     rb_define_method(cType, "zone_address", RUBY_METHOD_FUNC(&value_address), 0);
00588     rb_define_method(cType, "do_cast", RUBY_METHOD_FUNC(&value_do_cast), 1);
00589     rb_define_method(cType, "do_invalidate", RUBY_METHOD_FUNC(&value_invalidate), 0);
00590 
00591     rb_define_singleton_method(mTypelib, "do_basename",  RUBY_METHOD_FUNC(typelib_do_basename), 1);
00592     rb_define_singleton_method(mTypelib, "do_namespace", RUBY_METHOD_FUNC(typelib_do_namespace), 1);
00593     rb_define_singleton_method(mTypelib, "split_typename", RUBY_METHOD_FUNC(typelib_do_split_name), 1);
00594 
00595     rb_define_singleton_method(cType, "to_csv", RUBY_METHOD_FUNC(type_to_csv), -1);
00596     rb_define_method(cType, "to_csv", RUBY_METHOD_FUNC(value_to_csv), -1);
00597     rb_define_method(cType, "do_byte_array", RUBY_METHOD_FUNC(value_do_byte_array), 4);
00598     rb_define_method(cType, "marshalling_size", RUBY_METHOD_FUNC(value_marshalling_size), 0);
00599 
00600     Typelib_init_specialized_types();
00601 }
00602 


typelib
Author(s): Sylvain Joyeux/sylvain.joyeux@m4x.org
autogenerated on Mon Sep 14 2015 15:08:18