ext/dl/cptr.c

Go to the documentation of this file.
00001 /* -*- C -*-
00002  * $Id: cptr.c 47036 2014-08-02 15:25:17Z nagachika $
00003  */
00004 
00005 #include <ruby/ruby.h>
00006 #include <ruby/io.h>
00007 #include <ctype.h>
00008 #include "dl.h"
00009 
00010 VALUE rb_cDLCPtr;
00011 
00012 static inline freefunc_t
00013 get_freefunc(VALUE func, volatile VALUE *wrap)
00014 {
00015     VALUE addrnum;
00016     if (NIL_P(func)) {
00017         *wrap = 0;
00018         return NULL;
00019     }
00020     if (rb_dlcfunc_kind_p(func)) {
00021         *wrap = func;
00022         return (freefunc_t)(VALUE)RCFUNC_DATA(func)->ptr;
00023     }
00024     addrnum = rb_Integer(func);
00025     *wrap = (addrnum != func) ? func : 0;
00026     return (freefunc_t)(VALUE)NUM2PTR(addrnum);
00027 }
00028 
00029 static ID id_to_ptr;
00030 
00031 static void
00032 dlptr_mark(void *ptr)
00033 {
00034     struct ptr_data *data = ptr;
00035     if (data->wrap[0]) {
00036         rb_gc_mark(data->wrap[0]);
00037     }
00038     if (data->wrap[1]) {
00039         rb_gc_mark(data->wrap[1]);
00040     }
00041 }
00042 
00043 static void
00044 dlptr_free(void *ptr)
00045 {
00046     struct ptr_data *data = ptr;
00047     if (data->ptr) {
00048         if (data->free) {
00049             (*(data->free))(data->ptr);
00050         }
00051     }
00052     xfree(ptr);
00053 }
00054 
00055 static size_t
00056 dlptr_memsize(const void *ptr)
00057 {
00058     const struct ptr_data *data = ptr;
00059     return data ? sizeof(*data) + data->size : 0;
00060 }
00061 
00062 static const rb_data_type_t dlptr_data_type = {
00063     "dl/ptr",
00064     {dlptr_mark, dlptr_free, dlptr_memsize,},
00065 };
00066 
00067 VALUE
00068 rb_dlptr_new2(VALUE klass, void *ptr, long size, freefunc_t func)
00069 {
00070     struct ptr_data *data;
00071     VALUE val;
00072 
00073     val = TypedData_Make_Struct(klass, struct ptr_data, &dlptr_data_type, data);
00074     data->ptr = ptr;
00075     data->free = func;
00076     data->size = size;
00077     OBJ_TAINT(val);
00078 
00079     return val;
00080 }
00081 
00082 VALUE
00083 rb_dlptr_new(void *ptr, long size, freefunc_t func)
00084 {
00085     return rb_dlptr_new2(rb_cDLCPtr, ptr, size, func);
00086 }
00087 
00088 VALUE
00089 rb_dlptr_malloc(long size, freefunc_t func)
00090 {
00091     void *ptr;
00092 
00093     ptr = ruby_xmalloc((size_t)size);
00094     memset(ptr,0,(size_t)size);
00095     return rb_dlptr_new(ptr, size, func);
00096 }
00097 
00098 void *
00099 rb_dlptr2cptr(VALUE val)
00100 {
00101     struct ptr_data *data;
00102     void *ptr;
00103 
00104     if (rb_obj_is_kind_of(val, rb_cDLCPtr)) {
00105         TypedData_Get_Struct(val, struct ptr_data, &dlptr_data_type, data);
00106         ptr = data->ptr;
00107     }
00108     else if (val == Qnil) {
00109         ptr = NULL;
00110     }
00111     else{
00112         rb_raise(rb_eTypeError, "DL::PtrData was expected");
00113     }
00114 
00115     return ptr;
00116 }
00117 
00118 static VALUE
00119 rb_dlptr_s_allocate(VALUE klass)
00120 {
00121     VALUE obj;
00122     struct ptr_data *data;
00123 
00124     obj = TypedData_Make_Struct(klass, struct ptr_data, &dlptr_data_type, data);
00125     data->ptr = 0;
00126     data->size = 0;
00127     data->free = 0;
00128 
00129     return obj;
00130 }
00131 
00132 /*
00133  * call-seq:
00134  *    DL::CPtr.new(address)                   => dl_cptr
00135  *    DL::CPtr.new(address, size)             => dl_cptr
00136  *    DL::CPtr.new(address, size, freefunc)   => dl_cptr
00137  *
00138  * Create a new pointer to +address+ with an optional +size+ and +freefunc+.
00139  * +freefunc+ will be called when the instance is garbage collected.
00140  */
00141 static VALUE
00142 rb_dlptr_initialize(int argc, VALUE argv[], VALUE self)
00143 {
00144     VALUE ptr, sym, size, wrap = 0, funcwrap = 0;
00145     struct ptr_data *data;
00146     void *p = NULL;
00147     freefunc_t f = NULL;
00148     long s = 0;
00149 
00150     if (rb_scan_args(argc, argv, "12", &ptr, &size, &sym) >= 1) {
00151         VALUE addrnum = rb_Integer(ptr);
00152         if (addrnum != ptr) wrap = ptr;
00153         p = NUM2PTR(addrnum);
00154     }
00155     if (argc >= 2) {
00156         s = NUM2LONG(size);
00157     }
00158     if (argc >= 3) {
00159         f = get_freefunc(sym, &funcwrap);
00160     }
00161 
00162     if (p) {
00163         TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00164         if (data->ptr && data->free) {
00165             /* Free previous memory. Use of inappropriate initialize may cause SEGV. */
00166             (*(data->free))(data->ptr);
00167         }
00168         data->wrap[0] = wrap;
00169         data->wrap[1] = funcwrap;
00170         data->ptr  = p;
00171         data->size = s;
00172         data->free = f;
00173     }
00174 
00175     return Qnil;
00176 }
00177 
00178 /*
00179  * call-seq:
00180  *
00181  *    DL::CPtr.malloc(size, freefunc = nil)  => dl cptr instance
00182  *
00183  * Allocate +size+ bytes of memory and associate it with an optional
00184  * +freefunc+ that will be called when the pointer is garbage collected.
00185  * +freefunc+ must be an address pointing to a function or an instance of
00186  * DL::CFunc
00187  */
00188 static VALUE
00189 rb_dlptr_s_malloc(int argc, VALUE argv[], VALUE klass)
00190 {
00191     VALUE size, sym, obj, wrap = 0;
00192     long s;
00193     freefunc_t f;
00194 
00195     switch (rb_scan_args(argc, argv, "11", &size, &sym)) {
00196       case 1:
00197         s = NUM2LONG(size);
00198         f = NULL;
00199         break;
00200       case 2:
00201         s = NUM2LONG(size);
00202         f = get_freefunc(sym, &wrap);
00203         break;
00204       default:
00205         rb_bug("rb_dlptr_s_malloc");
00206     }
00207 
00208     obj = rb_dlptr_malloc(s,f);
00209     if (wrap) RPTR_DATA(obj)->wrap[1] = wrap;
00210 
00211     return obj;
00212 }
00213 
00214 /*
00215  * call-seq: to_i
00216  *
00217  * Returns the integer memory location of this DL::CPtr.
00218  */
00219 static VALUE
00220 rb_dlptr_to_i(VALUE self)
00221 {
00222     struct ptr_data *data;
00223 
00224     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00225     return PTR2NUM(data->ptr);
00226 }
00227 
00228 /*
00229  * call-seq: to_value
00230  *
00231  * Cast this CPtr to a ruby object.
00232  */
00233 static VALUE
00234 rb_dlptr_to_value(VALUE self)
00235 {
00236     struct ptr_data *data;
00237     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00238     return (VALUE)(data->ptr);
00239 }
00240 
00241 /*
00242  * call-seq: ptr
00243  *
00244  * Returns a DL::CPtr that is a dereferenced pointer for this DL::CPtr.
00245  * Analogous to the star operator in C.
00246  */
00247 VALUE
00248 rb_dlptr_ptr(VALUE self)
00249 {
00250     struct ptr_data *data;
00251 
00252     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00253     return rb_dlptr_new(*((void**)(data->ptr)),0,0);
00254 }
00255 
00256 /*
00257  * call-seq: ref
00258  *
00259  * Returns a DL::CPtr that is a reference pointer for this DL::CPtr.
00260  * Analogous to the ampersand operator in C.
00261  */
00262 VALUE
00263 rb_dlptr_ref(VALUE self)
00264 {
00265     struct ptr_data *data;
00266 
00267     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00268     return rb_dlptr_new(&(data->ptr),0,0);
00269 }
00270 
00271 /*
00272  * call-seq: null?
00273  *
00274  * Returns true if this is a null pointer.
00275  */
00276 VALUE
00277 rb_dlptr_null_p(VALUE self)
00278 {
00279     struct ptr_data *data;
00280 
00281     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00282     return data->ptr ? Qfalse : Qtrue;
00283 }
00284 
00285 /*
00286  * call-seq: free=(function)
00287  *
00288  * Set the free function for this pointer to the DL::CFunc in +function+.
00289  */
00290 static VALUE
00291 rb_dlptr_free_set(VALUE self, VALUE val)
00292 {
00293     struct ptr_data *data;
00294 
00295     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00296     data->free = get_freefunc(val, &data->wrap[1]);
00297 
00298     return Qnil;
00299 }
00300 
00301 /*
00302  * call-seq: free
00303  *
00304  * Get the free function for this pointer.  Returns  DL::CFunc or nil.
00305  */
00306 static VALUE
00307 rb_dlptr_free_get(VALUE self)
00308 {
00309     struct ptr_data *pdata;
00310 
00311     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, pdata);
00312 
00313     return rb_dlcfunc_new(pdata->free, DLTYPE_VOID, "free<anonymous>", CFUNC_CDECL);
00314 }
00315 
00316 /*
00317  * call-seq:
00318  *
00319  *    ptr.to_s        => string
00320  *    ptr.to_s(len)   => string
00321  *
00322  * Returns the pointer contents as a string.  When called with no arguments,
00323  * this method will return the contents until the first NULL byte.  When
00324  * called with +len+, a string of +len+ bytes will be returned.
00325  */
00326 static VALUE
00327 rb_dlptr_to_s(int argc, VALUE argv[], VALUE self)
00328 {
00329     struct ptr_data *data;
00330     VALUE arg1, val;
00331     int len;
00332 
00333     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00334     switch (rb_scan_args(argc, argv, "01", &arg1)) {
00335       case 0:
00336         val = rb_tainted_str_new2((char*)(data->ptr));
00337         break;
00338       case 1:
00339         len = NUM2INT(arg1);
00340         val = rb_tainted_str_new((char*)(data->ptr), len);
00341         break;
00342       default:
00343         rb_bug("rb_dlptr_to_s");
00344     }
00345 
00346     return val;
00347 }
00348 
00349 /*
00350  * call-seq:
00351  *
00352  *    ptr.to_str        => string
00353  *    ptr.to_str(len)   => string
00354  *
00355  * Returns the pointer contents as a string.  When called with no arguments,
00356  * this method will return the contents with the length of this pointer's
00357  * +size+. When called with +len+, a string of +len+ bytes will be returned.
00358  */
00359 static VALUE
00360 rb_dlptr_to_str(int argc, VALUE argv[], VALUE self)
00361 {
00362     struct ptr_data *data;
00363     VALUE arg1, val;
00364     int len;
00365 
00366     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00367     switch (rb_scan_args(argc, argv, "01", &arg1)) {
00368       case 0:
00369         val = rb_tainted_str_new((char*)(data->ptr),data->size);
00370         break;
00371       case 1:
00372         len = NUM2INT(arg1);
00373         val = rb_tainted_str_new((char*)(data->ptr), len);
00374         break;
00375       default:
00376         rb_bug("rb_dlptr_to_str");
00377     }
00378 
00379     return val;
00380 }
00381 
00382 /*
00383  * call-seq: inspect
00384  *
00385  * Returns a string formatted with an easily readable representation of the
00386  * internal state of the DL::CPtr
00387  */
00388 static VALUE
00389 rb_dlptr_inspect(VALUE self)
00390 {
00391     struct ptr_data *data;
00392 
00393     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00394     return rb_sprintf("#<%"PRIsVALUE":%p ptr=%p size=%ld free=%p>",
00395                       rb_obj_class(self), data, data->ptr, data->size, data->free);
00396 }
00397 
00398 /*
00399  *  call-seq:
00400  *    ptr == other    => true or false
00401  *    ptr.eql?(other) => true or false
00402  *
00403  * Returns true if +other+ wraps the same pointer, otherwise returns
00404  * false.
00405  */
00406 VALUE
00407 rb_dlptr_eql(VALUE self, VALUE other)
00408 {
00409     void *ptr1, *ptr2;
00410 
00411     if(!rb_obj_is_kind_of(other, rb_cDLCPtr)) return Qfalse;
00412 
00413     ptr1 = rb_dlptr2cptr(self);
00414     ptr2 = rb_dlptr2cptr(other);
00415 
00416     return ptr1 == ptr2 ? Qtrue : Qfalse;
00417 }
00418 
00419 /*
00420  *  call-seq:
00421  *    ptr <=> other   => -1, 0, 1, or nil
00422  *
00423  * Returns -1 if less than, 0 if equal to, 1 if greater than +other+.  Returns
00424  * nil if +ptr+ cannot be compared to +other+.
00425  */
00426 static VALUE
00427 rb_dlptr_cmp(VALUE self, VALUE other)
00428 {
00429     void *ptr1, *ptr2;
00430     SIGNED_VALUE diff;
00431 
00432     if(!rb_obj_is_kind_of(other, rb_cDLCPtr)) return Qnil;
00433 
00434     ptr1 = rb_dlptr2cptr(self);
00435     ptr2 = rb_dlptr2cptr(other);
00436     diff = (SIGNED_VALUE)ptr1 - (SIGNED_VALUE)ptr2;
00437     if (!diff) return INT2FIX(0);
00438     return diff > 0 ? INT2NUM(1) : INT2NUM(-1);
00439 }
00440 
00441 /*
00442  * call-seq:
00443  *    ptr + n   => new cptr
00444  *
00445  * Returns a new DL::CPtr that has been advanced +n+ bytes.
00446  */
00447 static VALUE
00448 rb_dlptr_plus(VALUE self, VALUE other)
00449 {
00450     void *ptr;
00451     long num, size;
00452 
00453     ptr = rb_dlptr2cptr(self);
00454     size = RPTR_DATA(self)->size;
00455     num = NUM2LONG(other);
00456     return rb_dlptr_new((char *)ptr + num, size - num, 0);
00457 }
00458 
00459 /*
00460  * call-seq:
00461  *    ptr - n   => new cptr
00462  *
00463  * Returns a new DL::CPtr that has been moved back +n+ bytes.
00464  */
00465 static VALUE
00466 rb_dlptr_minus(VALUE self, VALUE other)
00467 {
00468     void *ptr;
00469     long num, size;
00470 
00471     ptr = rb_dlptr2cptr(self);
00472     size = RPTR_DATA(self)->size;
00473     num = NUM2LONG(other);
00474     return rb_dlptr_new((char *)ptr - num, size + num, 0);
00475 }
00476 
00477 /*
00478  *  call-seq:
00479  *     ptr[index]                -> an_integer
00480  *     ptr[start, length]        -> a_string
00481  *
00482  * Returns integer stored at _index_.  If _start_ and _length_ are given,
00483  * a string containing the bytes from _start_ of length _length_ will be
00484  * returned.
00485  */
00486 VALUE
00487 rb_dlptr_aref(int argc, VALUE argv[], VALUE self)
00488 {
00489     VALUE arg0, arg1;
00490     VALUE retval = Qnil;
00491     size_t offset, len;
00492     struct ptr_data *data;
00493 
00494     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00495     if (!data->ptr) rb_raise(rb_eDLError, "NULL pointer dereference");
00496     switch( rb_scan_args(argc, argv, "11", &arg0, &arg1) ){
00497       case 1:
00498         offset = NUM2ULONG(arg0);
00499         retval = INT2NUM(*((char *)data->ptr + offset));
00500         break;
00501       case 2:
00502         offset = NUM2ULONG(arg0);
00503         len    = NUM2ULONG(arg1);
00504         retval = rb_tainted_str_new((char *)data->ptr + offset, len);
00505         break;
00506       default:
00507         rb_bug("rb_dlptr_aref()");
00508     }
00509     return retval;
00510 }
00511 
00512 /*
00513  *  call-seq:
00514  *     ptr[index]         = int                    ->  int
00515  *     ptr[start, length] = string or cptr or addr ->  string or dl_cptr or addr
00516  *
00517  * Set the value at +index+ to +int+.  Or, set the memory at +start+ until
00518  * +length+ with the contents of +string+, the memory from +dl_cptr+, or the
00519  * memory pointed at by the memory address +addr+.
00520  */
00521 VALUE
00522 rb_dlptr_aset(int argc, VALUE argv[], VALUE self)
00523 {
00524     VALUE arg0, arg1, arg2;
00525     VALUE retval = Qnil;
00526     size_t offset, len;
00527     void *mem;
00528     struct ptr_data *data;
00529 
00530     TypedData_Get_Struct(self, struct ptr_data, &dlptr_data_type, data);
00531     if (!data->ptr) rb_raise(rb_eDLError, "NULL pointer dereference");
00532     switch( rb_scan_args(argc, argv, "21", &arg0, &arg1, &arg2) ){
00533       case 2:
00534         offset = NUM2ULONG(arg0);
00535         ((char*)data->ptr)[offset] = NUM2UINT(arg1);
00536         retval = arg1;
00537         break;
00538       case 3:
00539         offset = NUM2ULONG(arg0);
00540         len    = NUM2ULONG(arg1);
00541         if (RB_TYPE_P(arg2, T_STRING)) {
00542             mem = StringValuePtr(arg2);
00543         }
00544         else if( rb_obj_is_kind_of(arg2, rb_cDLCPtr) ){
00545             mem = rb_dlptr2cptr(arg2);
00546         }
00547         else{
00548             mem    = NUM2PTR(arg2);
00549         }
00550         memcpy((char *)data->ptr + offset, mem, len);
00551         retval = arg2;
00552         break;
00553       default:
00554         rb_bug("rb_dlptr_aset()");
00555     }
00556     return retval;
00557 }
00558 
00559 /*
00560  * call-seq: size=(size)
00561  *
00562  * Set the size of this pointer to +size+
00563  */
00564 static VALUE
00565 rb_dlptr_size_set(VALUE self, VALUE size)
00566 {
00567     RPTR_DATA(self)->size = NUM2LONG(size);
00568     return size;
00569 }
00570 
00571 /*
00572  * call-seq: size
00573  *
00574  * Get the size of this pointer.
00575  */
00576 static VALUE
00577 rb_dlptr_size_get(VALUE self)
00578 {
00579     return LONG2NUM(RPTR_DATA(self)->size);
00580 }
00581 
00582 /*
00583  * call-seq:
00584  *    DL::CPtr.to_ptr(val)  => cptr
00585  *    DL::CPtr[val]         => cptr
00586  *
00587  * Get the underlying pointer for ruby object +val+ and return it as a
00588  * DL::CPtr object.
00589  */
00590 static VALUE
00591 rb_dlptr_s_to_ptr(VALUE self, VALUE val)
00592 {
00593     VALUE ptr, wrap = val, vptr;
00594 
00595     if (RTEST(rb_obj_is_kind_of(val, rb_cIO))){
00596         rb_io_t *fptr;
00597         FILE *fp;
00598         GetOpenFile(val, fptr);
00599         fp = rb_io_stdio_file(fptr);
00600         ptr = rb_dlptr_new(fp, 0, NULL);
00601     }
00602     else if (RTEST(rb_obj_is_kind_of(val, rb_cString))){
00603         char *str = StringValuePtr(val);
00604         ptr = rb_dlptr_new(str, RSTRING_LEN(val), NULL);
00605     }
00606     else if ((vptr = rb_check_funcall(val, id_to_ptr, 0, 0)) != Qundef){
00607         if (rb_obj_is_kind_of(vptr, rb_cDLCPtr)){
00608             ptr = vptr;
00609             wrap = 0;
00610         }
00611         else{
00612             rb_raise(rb_eDLError, "to_ptr should return a CPtr object");
00613         }
00614     }
00615     else{
00616         VALUE num = rb_Integer(val);
00617         if (num == val) wrap = 0;
00618         ptr = rb_dlptr_new(NUM2PTR(num), 0, NULL);
00619     }
00620     OBJ_INFECT(ptr, val);
00621     if (wrap) RPTR_DATA(ptr)->wrap[0] = wrap;
00622     return ptr;
00623 }
00624 
00625 void
00626 Init_dlptr(void)
00627 {
00628     id_to_ptr = rb_intern("to_ptr");
00629 
00630     /* Document-class: DL::CPtr
00631      *
00632      * CPtr is a class to handle C pointers
00633      *
00634      */
00635     rb_cDLCPtr = rb_define_class_under(rb_mDL, "CPtr", rb_cObject);
00636     rb_define_alloc_func(rb_cDLCPtr, rb_dlptr_s_allocate);
00637     rb_define_singleton_method(rb_cDLCPtr, "malloc", rb_dlptr_s_malloc, -1);
00638     rb_define_singleton_method(rb_cDLCPtr, "to_ptr", rb_dlptr_s_to_ptr, 1);
00639     rb_define_singleton_method(rb_cDLCPtr, "[]", rb_dlptr_s_to_ptr, 1);
00640     rb_define_method(rb_cDLCPtr, "initialize", rb_dlptr_initialize, -1);
00641     rb_define_method(rb_cDLCPtr, "free=", rb_dlptr_free_set, 1);
00642     rb_define_method(rb_cDLCPtr, "free",  rb_dlptr_free_get, 0);
00643     rb_define_method(rb_cDLCPtr, "to_i",  rb_dlptr_to_i, 0);
00644     rb_define_method(rb_cDLCPtr, "to_int",  rb_dlptr_to_i, 0);
00645     rb_define_method(rb_cDLCPtr, "to_value",  rb_dlptr_to_value, 0);
00646     rb_define_method(rb_cDLCPtr, "ptr",   rb_dlptr_ptr, 0);
00647     rb_define_method(rb_cDLCPtr, "+@", rb_dlptr_ptr, 0);
00648     rb_define_method(rb_cDLCPtr, "ref",   rb_dlptr_ref, 0);
00649     rb_define_method(rb_cDLCPtr, "-@", rb_dlptr_ref, 0);
00650     rb_define_method(rb_cDLCPtr, "null?", rb_dlptr_null_p, 0);
00651     rb_define_method(rb_cDLCPtr, "to_s", rb_dlptr_to_s, -1);
00652     rb_define_method(rb_cDLCPtr, "to_str", rb_dlptr_to_str, -1);
00653     rb_define_method(rb_cDLCPtr, "inspect", rb_dlptr_inspect, 0);
00654     rb_define_method(rb_cDLCPtr, "<=>", rb_dlptr_cmp, 1);
00655     rb_define_method(rb_cDLCPtr, "==", rb_dlptr_eql, 1);
00656     rb_define_method(rb_cDLCPtr, "eql?", rb_dlptr_eql, 1);
00657     rb_define_method(rb_cDLCPtr, "+", rb_dlptr_plus, 1);
00658     rb_define_method(rb_cDLCPtr, "-", rb_dlptr_minus, 1);
00659     rb_define_method(rb_cDLCPtr, "[]", rb_dlptr_aref, -1);
00660     rb_define_method(rb_cDLCPtr, "[]=", rb_dlptr_aset, -1);
00661     rb_define_method(rb_cDLCPtr, "size", rb_dlptr_size_get, 0);
00662     rb_define_method(rb_cDLCPtr, "size=", rb_dlptr_size_set, 1);
00663 
00664     /*  Document-const: NULL
00665      *
00666      * A NULL pointer
00667      */
00668     rb_define_const(rb_mDL, "NULL", rb_dlptr_new(0, 0, 0));
00669 }
00670 

Generated on 19 Jul 2016 for Ruby by  doxygen 1.4.7