$search
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 00211 Type const& self(rb2cxx::object<Type>(rbself)); 00212 Type const& with(rb2cxx::object<Type>(rbwith)); 00213 bool result = (self == with) || self.isSame(with); 00214 return result ? Qtrue : Qfalse; 00215 } 00216 00217 /* call-seq: 00218 * type.size => size 00219 * 00220 * Returns the size in bytes of instances of +type+ 00221 */ 00222 static VALUE type_size(VALUE self) 00223 { 00224 Type const& type(rb2cxx::object<Type>(self)); 00225 return INT2FIX(type.getSize()); 00226 } 00227 00228 /* call-seq: 00229 * type.dependencies => set_of_type 00230 * 00231 * Returns the set of Type subclasses that represent the types needed to build 00232 * +type+. 00233 */ 00234 static VALUE type_dependencies(VALUE self) 00235 { 00236 Type const& type(rb2cxx::object<Type>(self)); 00237 00238 typedef std::set<Type const*> TypeSet; 00239 TypeSet dependencies = type.dependsOn(); 00240 VALUE registry = type_get_registry(self); 00241 00242 VALUE result = rb_ary_new(); 00243 for (TypeSet::const_iterator it = dependencies.begin(); it != dependencies.end(); ++it) 00244 rb_ary_push(result, cxx2rb::type_wrap(**it, registry)); 00245 return result; 00246 } 00247 00248 /* call-seq: 00249 * type.casts_to?(other_type) => true or false 00250 * 00251 * Returns true if a value that is described by +type+ can be manipulated using 00252 * +other_type+. This is a weak form of equality 00253 */ 00254 static VALUE type_can_cast_to(VALUE self, VALUE to) 00255 { 00256 Type const& from_type(rb2cxx::object<Type>(self)); 00257 Type const& to_type(rb2cxx::object<Type>(to)); 00258 return from_type.canCastTo(to_type) ? Qtrue : Qfalse; 00259 } 00260 00261 /* 00262 * type.do_memory_layout(VALUE accept_pointers, VALUE accept_opaques, VALUE merge_skip_copy, VALUE remove_trailing_skips) => [operations] 00263 * 00264 * Returns a representation of the MemoryLayout for this type. If 00265 * +with_pointers+ is true, then pointers will be included in the layout. 00266 * Otherwise, an exception is raised if pointers are part of the type 00267 */ 00268 static VALUE type_memory_layout(VALUE self, VALUE pointers, VALUE opaques, VALUE merge, VALUE remove_trailing_skips) 00269 { 00270 Type const& type(rb2cxx::object<Type>(self)); 00271 VALUE registry = type_get_registry(self); 00272 00273 VALUE result = rb_ary_new(); 00274 00275 VALUE rb_memcpy = ID2SYM(rb_intern("FLAG_MEMCPY")); 00276 VALUE rb_skip = ID2SYM(rb_intern("FLAG_SKIP")); 00277 VALUE rb_array = ID2SYM(rb_intern("FLAG_ARRAY")); 00278 VALUE rb_end = ID2SYM(rb_intern("FLAG_END")); 00279 VALUE rb_container = ID2SYM(rb_intern("FLAG_CONTAINER")); 00280 00281 try { 00282 MemoryLayout layout = Typelib::layout_of(type, RTEST(pointers), RTEST(opaques), RTEST(merge), RTEST(remove_trailing_skips)); 00283 00284 // Now, convert into something representable in Ruby 00285 for (MemoryLayout::const_iterator it = layout.begin(); it != layout.end(); ++it) 00286 { 00287 switch(*it) 00288 { 00289 case MemLayout::FLAG_MEMCPY: 00290 rb_ary_push(result, rb_memcpy); 00291 rb_ary_push(result, LONG2NUM(*(++it))); 00292 break; 00293 case MemLayout::FLAG_SKIP: 00294 rb_ary_push(result, rb_skip); 00295 rb_ary_push(result, LONG2NUM(*(++it))); 00296 break; 00297 case MemLayout::FLAG_ARRAY: 00298 rb_ary_push(result, rb_array); 00299 rb_ary_push(result, LONG2NUM(*(++it))); 00300 break; 00301 case MemLayout::FLAG_END: 00302 rb_ary_push(result, rb_end); 00303 break; 00304 case MemLayout::FLAG_CONTAINER: 00305 rb_ary_push(result, rb_container); 00306 rb_ary_push(result, cxx2rb::type_wrap(*reinterpret_cast<Container*>(*(++it)), registry)); 00307 break; 00308 default: 00309 rb_raise(rb_eArgError, "error encountered while parsing memory layout"); 00310 } 00311 } 00312 00313 } catch(std::exception const& e) { 00314 rb_raise(rb_eArgError, "%s", e.what()); 00315 } 00316 00317 return result; 00318 } 00319 00320 /* PODs are assignable, pointers are dereferenced */ 00321 static VALUE type_is_assignable(Type const& type) 00322 { 00323 switch(type.getCategory()) 00324 { 00325 case Type::Numeric: 00326 return INT2FIX(1); 00327 case Type::Pointer: 00328 return type_is_assignable( dynamic_cast<Pointer const&>(type).getIndirection()); 00329 case Type::Enum: 00330 return INT2FIX(1); 00331 default: 00332 return INT2FIX(0); 00333 } 00334 // never reached 00335 } 00336 00337 VALUE typelib_ruby::type_get_registry(VALUE self) 00338 { 00339 return rb_iv_get(self, "@registry"); 00340 } 00341 00342 00343 /*********************************************************************************** 00344 * 00345 * Wrapping of the Value class 00346 * 00347 */ 00348 00349 static void value_delete(void* self) { delete reinterpret_cast<Value*>(self); } 00350 00351 static VALUE value_alloc(VALUE klass) 00352 { return Data_Wrap_Struct(klass, 0, value_delete, new Value); } 00353 00354 static 00355 VALUE value_initialize(VALUE self, VALUE ptr) 00356 { 00357 Type const& t(rb2cxx::object<Type>(rb_class_of(self))); 00358 00359 if (NIL_P(ptr) || rb_obj_is_kind_of(ptr, rb_cString)) 00360 { 00361 # ifdef VERBOSE 00362 fprintf(stderr, "allocating new value of type %s to copy an existing buffer\n", t.getName().c_str()); 00363 # endif 00364 VALUE buffer = memory_allocate(t.getSize()); 00365 memory_init(buffer, rb_class_of(self)); 00366 if (! NIL_P(ptr)) 00367 { 00368 char* ruby_buffer = StringValuePtr(ptr); 00369 vector<uint8_t> cxx_buffer(ruby_buffer, ruby_buffer + RSTRING_LEN(ptr)); 00370 try { Typelib::load(Value(memory_cptr(buffer), t), cxx_buffer); } 00371 catch(std::exception const& e) 00372 { rb_raise(rb_eArgError, "%s", e.what()); } 00373 } 00374 00375 ptr = buffer; 00376 } 00377 00378 // Protect 'ptr' against the GC 00379 rb_iv_set(self, "@ptr", ptr); 00380 Value& value = rb2cxx::object<Value>(self); 00381 value = Value(memory_cptr(ptr), t); 00382 # ifdef VERBOSE 00383 fprintf(stderr, "object %llu uses memory zone %p\n", NUM2ULL(rb_obj_id(self)), value.getData()); 00384 # endif 00385 return self; 00386 } 00387 00388 static 00389 VALUE value_address(VALUE self) 00390 { 00391 Value value = rb2cxx::object<Value>(self); 00392 return LONG2NUM((long)value.getData()); 00393 } 00394 00395 static 00396 VALUE value_endian_swap(VALUE self) 00397 { 00398 Value& value = rb2cxx::object<Value>(self); 00399 CompileEndianSwapVisitor compiled; 00400 compiled.apply(value.getType()); 00401 00402 VALUE registry = value_get_registry(self); 00403 VALUE result = value_allocate(value.getType(), registry); 00404 compiled.swap(value, rb2cxx::object<Value>(result)); 00405 return result; 00406 } 00407 00408 static 00409 VALUE value_endian_swap_b(VALUE self, VALUE rb_compile) 00410 { 00411 Value& value = rb2cxx::object<Value>(self); 00412 endian_swap(value); 00413 return self; 00414 } 00415 00416 static 00417 VALUE value_do_cast(VALUE self, VALUE target_type) 00418 { 00419 Value& value = rb2cxx::object<Value>(self); 00420 Type const& to_type(rb2cxx::object<Type>(target_type)); 00421 00422 if (value.getType() == to_type) 00423 return self; 00424 00425 VALUE registry = rb_iv_get(target_type, "@registry"); 00426 Value casted(value.getData(), to_type); 00427 # ifdef VERBOSE 00428 fprintf(stderr, "wrapping casted value\n"); 00429 # endif 00430 00431 return cxx2rb::value_wrap(casted, registry, self); 00432 } 00433 00434 static 00435 VALUE value_invalidate(VALUE self) 00436 { 00437 if (NIL_P(rb_iv_get(self, "@parent"))) 00438 rb_raise(rb_eArgError, "cannot invalidate a toplevel value"); 00439 00440 Value& value = rb2cxx::object<Value>(self); 00441 #ifdef VERBOSE 00442 fprintf(stderr, "invalidating %llu, ptr=%p\n", NUM2ULL(rb_obj_id(self)), value.getData()); 00443 #endif 00444 // memory and Typelib::destroy are handled at the pointer level, not at the 00445 // Type instance level. Do not memory_unref here ! 00446 value = Value(0, value.getType()); 00447 return Qnil; 00448 } 00449 00450 static 00451 VALUE value_do_byte_array(VALUE self, VALUE pointers, VALUE opaques, VALUE merge, VALUE remove_trailing_skips) 00452 { 00453 Value& value = rb2cxx::object<Value>(self); 00454 MemoryLayout layout = Typelib::layout_of(value.getType(), RTEST(pointers), RTEST(opaques), RTEST(merge), RTEST(remove_trailing_skips)); 00455 00456 vector<uint8_t> buffer; 00457 Typelib::dump(value, buffer, layout); 00458 return rb_str_new(reinterpret_cast<char*>(&buffer[0]), buffer.size()); 00459 } 00460 00461 /* call-seq: 00462 * obj.marshalling_size => integer 00463 * 00464 * Returns the size of this value once marshalled by Typelib, i.e. the size of 00465 * the byte array returned by #to_byte_array 00466 */ 00467 static 00468 VALUE value_marshalling_size(VALUE self) 00469 { 00470 Value& value = rb2cxx::object<Value>(self); 00471 return INT2NUM(Typelib::getDumpSize(value)); 00472 } 00473 00474 VALUE value_memory_eql_p(VALUE rbself, VALUE rbwith) 00475 { 00476 Value& self = rb2cxx::object<Value>(rbself); 00477 Value& with = rb2cxx::object<Value>(rbwith); 00478 00479 if (self.getData() == with.getData()) 00480 return Qtrue; 00481 00482 // Type#== checks for type equality before calling memory_equal? 00483 Type const& type = self.getType(); 00484 return memcmp(self.getData(), with.getData(), type.getSize()) == 0 ? Qtrue : Qfalse; 00485 } 00486 00487 VALUE typelib_ruby::value_get_registry(VALUE self) 00488 { 00489 VALUE type = rb_funcall(self, rb_intern("class"), 0); 00490 return rb_iv_get(type, "@registry"); 00491 } 00492 00500 static VALUE value_to_csv(int argc, VALUE* argv, VALUE self) 00501 { 00502 VALUE separator = Qnil; 00503 rb_scan_args(argc, argv, "01", &separator); 00504 00505 Value const& value(rb2cxx::object<Value>(self)); 00506 std::string sep = " "; 00507 if (!NIL_P(separator)) sep = StringValuePtr(separator); 00508 00509 std::ostringstream stream; 00510 stream << csv(value.getType(), value.getData(), sep); 00511 std::string str = stream.str(); 00512 return rb_str_new(str.c_str(), str.length()); 00513 } 00514 00515 /* Initializes the memory to 0 */ 00516 static VALUE value_zero(VALUE self) 00517 { 00518 Value const& value(rb2cxx::object<Value>(self)); 00519 Typelib::zero(value); 00520 return self; 00521 } 00522 00523 static VALUE typelib_do_copy(VALUE, VALUE to, VALUE from) 00524 { 00525 Value v_from = rb2cxx::object<Value>(from); 00526 Value v_to = rb2cxx::object<Value>(to); 00527 00528 if (v_from.getType() != v_to.getType()) 00529 { 00530 // Do a deep check for type equality 00531 if (!v_from.getType().canCastTo(v_to.getType())) 00532 rb_raise(rb_eArgError, "cannot copy: types are not compatible"); 00533 } 00534 Typelib::copy(v_to.getData(), v_from.getData(), v_from.getType()); 00535 return to; 00536 } 00537 00538 /* call-seq: 00539 * Typelib.compare(to, from) => true or false 00540 * 00541 * Proper comparison of two values. +to+ and +from+'s types do not have to be of 00542 * the same registries, as long as the types can be cast'ed into each other. 00543 */ 00544 static VALUE typelib_compare(VALUE, VALUE to, VALUE from) 00545 { 00546 Value v_from = rb2cxx::object<Value>(from); 00547 Value v_to = rb2cxx::object<Value>(to); 00548 00549 if (v_from.getType() != v_to.getType()) 00550 { 00551 // Do a deep check for type equality 00552 if (!v_from.getType().canCastTo(v_to.getType())) 00553 rb_raise(rb_eArgError, "cannot compare: %s and %s are not compatible types", 00554 v_from.getType().getName().c_str(), 00555 v_to.getType().getName().c_str()); 00556 } 00557 try { 00558 bool result = Typelib::compare(v_to.getData(), v_from.getData(), v_from.getType()); 00559 return result ? Qtrue : Qfalse; 00560 } catch(std::exception const& e) { 00561 rb_raise(rb_eArgError, "%s", e.what()); 00562 } 00563 } 00564 00565 00566 void typelib_ruby::Typelib_init_values() 00567 { 00568 VALUE mTypelib = rb_define_module("Typelib"); 00569 rb_define_singleton_method(mTypelib, "do_copy", RUBY_METHOD_FUNC(typelib_do_copy), 2); 00570 rb_define_singleton_method(mTypelib, "compare", RUBY_METHOD_FUNC(typelib_compare), 2); 00571 00572 cType = rb_define_class_under(mTypelib, "Type", rb_cObject); 00573 rb_define_alloc_func(cType, value_alloc); 00574 rb_define_singleton_method(cType, "==", RUBY_METHOD_FUNC(type_equal_operator), 1); 00575 rb_define_singleton_method(cType, "size", RUBY_METHOD_FUNC(&type_size), 0); 00576 rb_define_singleton_method(cType, "do_memory_layout", RUBY_METHOD_FUNC(&type_memory_layout), 4); 00577 rb_define_singleton_method(cType, "do_dependencies", RUBY_METHOD_FUNC(&type_dependencies), 0); 00578 rb_define_singleton_method(cType, "casts_to?", RUBY_METHOD_FUNC(&type_can_cast_to), 1); 00579 rb_define_method(cType, "__initialize__", RUBY_METHOD_FUNC(&value_initialize), 1); 00580 rb_define_method(cType, "zero!", RUBY_METHOD_FUNC(&value_zero), 0); 00581 rb_define_method(cType, "memory_eql?", RUBY_METHOD_FUNC(&value_memory_eql_p), 1); 00582 rb_define_method(cType, "endian_swap", RUBY_METHOD_FUNC(&value_endian_swap), 0); 00583 rb_define_method(cType, "endian_swap!", RUBY_METHOD_FUNC(&value_endian_swap_b), 0); 00584 rb_define_method(cType, "zone_address", RUBY_METHOD_FUNC(&value_address), 0); 00585 rb_define_method(cType, "do_cast", RUBY_METHOD_FUNC(&value_do_cast), 1); 00586 rb_define_method(cType, "do_invalidate", RUBY_METHOD_FUNC(&value_invalidate), 0); 00587 00588 rb_define_singleton_method(mTypelib, "do_basename", RUBY_METHOD_FUNC(typelib_do_basename), 1); 00589 rb_define_singleton_method(mTypelib, "do_namespace", RUBY_METHOD_FUNC(typelib_do_namespace), 1); 00590 rb_define_singleton_method(mTypelib, "split_typename", RUBY_METHOD_FUNC(typelib_do_split_name), 1); 00591 00592 rb_define_singleton_method(cType, "to_csv", RUBY_METHOD_FUNC(type_to_csv), -1); 00593 rb_define_method(cType, "to_csv", RUBY_METHOD_FUNC(value_to_csv), -1); 00594 rb_define_method(cType, "do_byte_array", RUBY_METHOD_FUNC(value_do_byte_array), 4); 00595 rb_define_method(cType, "marshalling_size", RUBY_METHOD_FUNC(value_marshalling_size), 0); 00596 00597 Typelib_init_specialized_types(); 00598 } 00599