functions.cc
Go to the documentation of this file.
00001 #include "typelib.hh"
00002 
00003 #include <dyncall.h>
00004 #include <dynload.h>
00005 #include <boost/static_assert.hpp>
00006 
00007 static VALUE cCallVM;
00008 static VALUE cLibrary;
00009 static VALUE cFunction;
00010 
00011 using namespace Typelib;
00012 using namespace typelib_ruby;
00013 
00014 static VALUE
00015 library_wrap(VALUE self, VALUE name, VALUE auto_unload)
00016 {
00017     void* libhandle = dlLoadLibrary(StringValuePtr(name));
00018     if (!libhandle)
00019         rb_raise(rb_eArgError, "cannot load library %s", StringValuePtr(name));
00020 
00021     if (RTEST(auto_unload))
00022         return Data_Wrap_Struct(cLibrary, 0, dlFreeLibrary, libhandle);
00023     else
00024         return Data_Wrap_Struct(cLibrary, 0, 0, libhandle);
00025 }
00026 
00027 /* call-seq:
00028  *   lib.find(function_name) => function object
00029  *
00030  * Searches for the function named +function_name+ in +lib+ and returns
00031  * the corresponding Function object. If the symbol is not available in
00032  * the library, raises ArgumentError.
00033  */
00034 static VALUE
00035 library_find(VALUE self, VALUE name)
00036 {
00037     //void* libhandle;
00038     DLLib* libhandle; 
00039     Data_Get_Struct(self, DLLib, libhandle);
00040 
00041     void* symhandle = dlFindSymbol(libhandle, StringValuePtr(name));
00042     if (!symhandle)
00043     {
00044         VALUE libname = rb_iv_get(self, "@name");
00045         rb_raise(rb_eArgError, "cannot find symbol '%s' in library '%s'",
00046                 StringValuePtr(name), StringValuePtr(libname));
00047     }
00048 
00049     VALUE function = Data_Wrap_Struct(cFunction, 0, 0, symhandle);
00050     rb_funcall(function, rb_intern("initialize"), 2, self, name);
00051     return function;
00052 }
00053 
00054 class PrepareVM : public TypeVisitor
00055 {
00056     DCCallVM* m_vm;
00057     VALUE m_data;
00058 
00059     virtual bool visit_ (Numeric const& type)
00060     {
00061         if (type.getNumericCategory() == Numeric::Float)
00062         {
00063             double value = NUM2DBL(m_data);
00064             switch(type.getSize())
00065             {
00066                 case 4: dcArgFloat(m_vm,  static_cast<float>(value)); break;
00067                 case 8: dcArgDouble(m_vm, static_cast<double>(value)); break;
00068             }
00069         }
00070         else
00071         {
00072             switch(type.getSize())
00073             {
00074                 case 1: dcArgChar    (m_vm, static_cast<char>(NUM2INT(m_data))); break;
00075                 case 2: dcArgShort   (m_vm, static_cast<short>(NUM2INT(m_data))); break;
00076                 case 4: dcArgInt         (m_vm, NUM2INT(m_data)); break;
00077                 case 8: dcArgLongLong(m_vm, NUM2LL(m_data)); break;
00078             }
00079         }
00080         return false;
00081     }
00082     virtual bool visit_ (Enum const& type)      
00083     { 
00084         BOOST_STATIC_ASSERT(( sizeof(Enum::integral_type) == sizeof(int) ));
00085         dcArgInt(m_vm, NUM2INT(m_data));
00086         return false;
00087     }
00088 
00089     virtual bool visit_ (Pointer const& type)
00090     {
00091         dcArgPointer(m_vm, memory_cptr(m_data));
00092         return false;
00093     }
00094     virtual bool visit_ (Array const& type)
00095     { 
00096         dcArgPointer(m_vm, memory_cptr(m_data));
00097         return false;
00098     }
00099 
00100     virtual bool visit_ (Compound const& type)
00101     { throw UnsupportedType(type); }
00102 
00103 public:
00104     PrepareVM(DCCallVM* vm, VALUE data)
00105         : m_vm(vm), m_data(data) {}
00106 
00107     static void append(DCCallVM* vm, Type const& type, VALUE data)
00108     {
00109         PrepareVM visitor(vm, data);
00110         visitor.apply(type);
00111     }
00112 };
00113 
00114 static VALUE
00115 function_compile(VALUE self, VALUE filtered_args)
00116 {
00117     DCCallVM* vm = dcNewCallVM(4096);
00118     VALUE rb_vm  = Data_Wrap_Struct(cCallVM, 0, dcFree, vm);
00119     rb_iv_set(rb_vm, "@arguments", filtered_args);
00120 
00121     VALUE argument_types = rb_iv_get(self, "@arguments");
00122     size_t argc = RARRAY_LEN(argument_types);
00123     VALUE* argv = RARRAY_PTR(argument_types);
00124 
00125     for (size_t i = 0; i < argc; ++i)
00126         PrepareVM::append(vm, rb2cxx::object<Type>(argv[i]), RARRAY_PTR(filtered_args)[i]);
00127 
00128     return rb_vm;
00129 }
00130 
00131 class VMCall : public TypeVisitor
00132 {
00133     DCCallVM* m_vm;
00134     void*     m_handle;
00135     VALUE     m_return_type;
00136     VALUE     m_return;
00137 
00138     virtual bool visit_ (NullType const& type)
00139     {
00140         dcCallVoid(m_vm, m_handle);
00141         return true;
00142     }
00143 
00144     virtual bool visit_ (Numeric const& type)
00145     {
00146         if (type.getNumericCategory() == Numeric::Float)
00147         {
00148             switch(type.getSize())
00149             {
00150                 case 4: m_return = rb_float_new(dcCallFloat(m_vm,  m_handle)); break;
00151                 case 8: m_return = rb_float_new(dcCallDouble(m_vm, m_handle)); break;
00152             }
00153         }
00154         else
00155         {
00156             switch(type.getSize())
00157             {
00158                 case 1: m_return = INT2NUM (dcCallChar    (m_vm, m_handle)); break;
00159                 case 2: m_return = INT2NUM (dcCallShort   (m_vm, m_handle)); break;
00160                 case 4: m_return = INT2NUM (dcCallInt     (m_vm, m_handle)); break;
00161                 case 8: m_return = LL2NUM  (dcCallLongLong(m_vm, m_handle)); break;
00162             }
00163         }
00164         return false;
00165     }
00166     virtual bool visit_ (Enum const& type)      
00167     { 
00168         BOOST_STATIC_ASSERT(( sizeof(Enum::integral_type) == sizeof(int) ));
00169         m_return = INT2NUM(dcCallInt(m_vm, m_handle));
00170         return false;
00171     }
00172 
00173     virtual bool visit_ (Pointer const& type)
00174     {
00175 #ifdef  VERBOSE
00176         fprintf(stderr, "wrapping dcCallPointer with type=%s\n", type.getName().c_str());
00177 #endif
00178         m_return = memory_wrap(new void*(dcCallPointer(m_vm, m_handle)), true, 0);
00179         return false;
00180     }
00181     virtual bool visit_ (Array const& type)
00182     { 
00183 #ifdef  VERBOSE
00184         fprintf(stderr, "wrapping dcCallPointer with type=%s\n", type.getName().c_str());
00185 #endif
00186         m_return = memory_wrap(new void*(dcCallPointer(m_vm, m_handle)), true, 0);
00187         return false;
00188     }
00189 
00190     virtual bool visit_ (Compound const& type)
00191     { throw UnsupportedType(type); }
00192 
00193 public:
00194     VMCall(DCCallVM* vm, void* handle, VALUE return_type)
00195         : m_vm(vm), m_handle(handle), m_return_type(return_type), 
00196         m_return(Qnil) {}
00197 
00198     VALUE getReturnedValue() const { return m_return; }
00199 
00200     static VALUE call(DCCallVM* vm, void* handle, VALUE return_type)
00201     {
00202         if (NIL_P(return_type))
00203         {
00204             dcCallVoid(vm, handle);
00205             return Qnil;
00206         }
00207 
00208         VMCall visitor(vm, handle, return_type);
00209         visitor.apply(rb2cxx::object<Type>(return_type));
00210         return visitor.getReturnedValue();
00211     }
00212 };
00213 
00214 static VALUE
00215 vm_call(VALUE self, VALUE function)
00216 {
00217     DCCallVM* vm;
00218     Data_Get_Struct(self, DCCallVM, vm);
00219 
00220     void* symhandle;
00221     Data_Get_Struct(function, void, symhandle);
00222 
00223     return VMCall::call(vm, symhandle, rb_iv_get(function, "@return_type"));
00224 }
00225 
00226 static
00227 VALUE filter_numeric_arg(VALUE self, VALUE arg, VALUE rb_expected_type)
00228 {
00229     Type const& expected_type = rb2cxx::object<Type>(rb_expected_type);
00230 
00231     if (expected_type.getCategory() == Type::Enum)
00232         return INT2FIX( rb2cxx::enum_value(arg, static_cast<Enum const&>(expected_type)) );
00233     else if (expected_type.getCategory() == Type::Pointer)
00234     {
00235         // Build directly a DL::Ptr object, no need to build a Ruby Value wrapper
00236         Pointer const& ptr_type  = static_cast<Pointer const&>(expected_type);
00237         Type const& pointed_type = ptr_type.getIndirection();
00238         VALUE ptr = memory_allocate(pointed_type.getSize());
00239 
00240         Value typelib_value(memory_cptr(ptr), pointed_type);
00241         typelib_from_ruby(typelib_value, arg);
00242         return ptr;
00243     }
00244     return arg;
00245 }
00246 
00247 static 
00248 VALUE filter_value_arg(VALUE self, VALUE arg, VALUE rb_expected_type)
00249 {
00250     Type const& expected_type   = rb2cxx::object<Type>(rb_expected_type);
00251     Value const& arg_value      = rb2cxx::object<Value>(arg);
00252     Type const& arg_type        = arg_value.getType();     
00253 
00254     if (arg_type == expected_type)
00255     {
00256         if (arg_type.getCategory() == Type::Pointer)
00257         {
00258 #ifdef      VERBOSE
00259             fprintf(stderr, "wrapping filtered argument with arg_type=%s\n", arg_type.getName().c_str());
00260 #endif
00261             return memory_wrap(*reinterpret_cast<void**>(arg_value.getData()), false, 0);
00262         }
00263         else if (arg_type.getCategory() == Type::Array)
00264             return rb_funcall(arg, rb_intern("to_memory_ptr"), 0);
00265         else if (arg_type.getCategory() == Type::Numeric)
00266             return rb_funcall(arg, rb_intern("to_ruby"), 0);
00267         else
00268             return Qnil;
00269     }
00270 
00271     if (expected_type.getCategory() != Type::Pointer && expected_type.getCategory() != Type::Array)
00272         return Qnil;
00273 
00274     Indirect const& expected_indirect_type   = static_cast<Indirect const&>(expected_type);
00275     Type const& expected_pointed_type  = expected_indirect_type.getIndirection();
00276 
00277     // /void == /nil, so that if expected_type is null, then 
00278     // it is because the argument can hold any kind of pointers
00279     if (expected_pointed_type.isNull() || expected_pointed_type == arg_type)
00280         return rb_funcall(arg, rb_intern("to_memory_ptr"), 0);
00281     
00282     // There is only array <-> pointer conversion left to handle
00283     Indirect const& arg_indirect = static_cast<Indirect const&>(arg_type);
00284     if (arg_indirect.getIndirection() != expected_pointed_type)
00285         return Qnil;
00286 
00287     if (expected_type.getCategory() == Type::Pointer)
00288     {
00289         if (arg_type.getCategory() == Type::Pointer)
00290         {
00291             // if it was OK, then arg_type == expected_type which is already handled
00292             return Qnil;
00293         }
00294         return rb_funcall(arg, rb_intern("to_memory_ptr"), 0);
00295     }
00296     else
00297     {
00298         if (arg_type.getCategory() == Type::Pointer)
00299             return memory_wrap(*reinterpret_cast<void**>(arg_value.getData()), false, 0);
00300         // check sizes
00301         Array const& expected_array = static_cast<Array const&>(expected_type);
00302         Array const& arg_array = static_cast<Array const&>(arg_type);
00303         if (expected_array.getDimension() > arg_array.getDimension())
00304             return Qnil;
00305         return rb_funcall(arg, rb_intern("to_memory_ptr"), 0);
00306     }
00307 }
00308 
00309 void typelib_ruby::Typelib_init_functions()
00310 {
00311     VALUE mTypelib  = rb_define_module("Typelib");
00312     rb_define_singleton_method(mTypelib, "filter_numeric_arg", RUBY_METHOD_FUNC(filter_numeric_arg), 2);
00313     rb_define_singleton_method(mTypelib, "filter_value_arg", RUBY_METHOD_FUNC(filter_value_arg), 2);
00314 
00315     cLibrary = rb_define_class_under(mTypelib, "Library", rb_cObject);
00316     rb_define_singleton_method(cLibrary, "wrap", RUBY_METHOD_FUNC(library_wrap), 2);
00317     rb_define_method(cLibrary, "find", RUBY_METHOD_FUNC(library_find), 1);
00318 
00319     cFunction = rb_define_class_under(mTypelib, "Function", rb_cObject);
00320     rb_define_private_method(cFunction, "prepare_vm", RUBY_METHOD_FUNC(function_compile), 1);
00321 
00322     cCallVM  = rb_define_class_under(mTypelib, "CallVM", rb_cObject);
00323     rb_define_method(cCallVM, "call", RUBY_METHOD_FUNC(vm_call), 1);
00324 }
00325 


typelib
Author(s): Sylvain Joyeux/sylvain.joyeux@m4x.org
autogenerated on Thu Jan 2 2014 11:38:41