// // Copyright (c) ZeroC, Inc. All rights reserved. // #ifndef ICE_RUBY_UTIL_H #define ICE_RUBY_UTIL_H #include #include // // Avoid clang conversion warnings in "callRuby" calls // #if defined(__clang__) # pragma clang diagnostic ignored "-Wconversion" # pragma clang diagnostic ignored "-Wsign-conversion" #endif namespace IceRuby { void initUtil(VALUE); class RubyException { public: // // This constructor uses the interpreter's last error result as the exception. // RubyException(); // // The Ruby exception object is supplied. // RubyException(VALUE); // // The Ruby exception object is supplied along with a message. // RubyException(VALUE, const char*, ...); std::ostream& operator<<(std::ostream&) const; VALUE ex; }; // // Returns true if the value is a string or can be converted into a string. // bool isString(VALUE); // // Returns true if the value is an array or can be converted into an array. // bool isArray(VALUE); // // Returns true if the value is a hash or can be converted into a hash. // bool isHash(VALUE); // // Convert a Ruby value into a string. May raise RubyException. // std::string getString(VALUE); // // Create a Ruby string. May raise RubyException. // VALUE createString(const std::string&); // // Convert a Ruby value into a long. May raise RubyException. // long getInteger(VALUE); // // Convert a Ruby value into an Ice::Long. May raise RubyException. // Ice::Long getLong(VALUE); // // Convert a Ruby array into a vector. Returns true on // success and false if the value is not an array. May raise // RubyException. // bool arrayToStringSeq(VALUE, std::vector&); // // Convert a vector of strings into a Ruby array. May raise // RubyException. // VALUE stringSeqToArray(const std::vector&); // // Convert a vector of Ice::Byte into a Ruby array of numbers. // May raise RubyException. // VALUE createNumSeq(const std::vector&); // // Convert a Ruby hash to Ice::Context. Returns true on success // and false if the value is not a hash. May raise RubyException. // bool hashToContext(VALUE, Ice::Context&); // // Convert Ice::Context to a hash. May raise RubyException. // VALUE contextToHash(const Ice::Context&); // // Abstract class representing an iterator for a Ruby hash collection. // class HashIterator { public: virtual ~HashIterator() {} virtual void element(VALUE, VALUE) = 0; }; // // Iterate over the elements in a Ruby hash. The iterator's // element method is invoked for each entry. May raise // RubyException. // void hashIterate(VALUE, HashIterator&); // // Convert a Ruby value into Ice::Identity. May raise RubyException. // Ice::Identity getIdentity(VALUE); // // Create an instance of Ice::Identity. May raise RubyException. // VALUE createIdentity(const Ice::Identity&); // // Create a Ruby instance of Ice.ProtocolVersion. // VALUE createProtocolVersion(const Ice::ProtocolVersion&); // // Create a Ruby instance of Ice.EncodingVersion. // VALUE createEncodingVersion(const Ice::EncodingVersion&); // // Extracts the members of an encoding version. // bool getEncodingVersion(VALUE, Ice::EncodingVersion&); // // The callRuby functions are used to invoke Ruby C API functions // while translating any Ruby exception into RubyException so that // C++ objects are cleaned up properly. Overloadings are provided // to support API functions that accept multiple arguments. // template VALUE callRuby(Fun fun); template VALUE callRuby(Fun fun, T1 t1); template VALUE callRuby(Fun fun, T1 t1, T2 t2); template VALUE callRuby(Fun fun, T1 t1, T2 t2, T3 t3); template VALUE callRuby(Fun fun, T1 t1, T2 t2, T3 t3, T4 t4); template VALUE callRuby(Fun fun, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6); extern "C" typedef VALUE (*RubyFunction)(VALUE); VALUE callProtected(RubyFunction, VALUE); template class RF_0 { public: RF_0(Fun f) : _f(f) {} inline VALUE operator()() { return _f(); } static inline VALUE call(VALUE f) { return (*reinterpret_cast(f))(); } private: Fun _f; }; template inline VALUE callRuby(Fun fun) { typedef RF_0 RF; RF f(fun); return callProtected(RF::call, reinterpret_cast(&f)); } template class RF_1 { public: RF_1(Fun f, T1 t1) : _f(f), _t1(t1) {} inline VALUE operator()() { return _f(_t1); } static inline VALUE call(VALUE f) { return (*reinterpret_cast(f))(); } private: Fun _f; T1 _t1; }; template inline VALUE callRuby(Fun fun, T1 t1) { typedef RF_1 RF; RF f(fun, t1); return callProtected(RF::call, reinterpret_cast(&f)); } template class RF_2 { public: RF_2(Fun f, T1 t1, T2 t2) : _f(f), _t1(t1), _t2(t2) {} inline VALUE operator()(){ return _f(_t1, _t2); } static inline VALUE call(VALUE f) { return (*reinterpret_cast(f))(); } private: Fun _f; T1 _t1; T2 _t2; }; template inline VALUE callRuby(Fun fun, T1 t1, T2 t2) { typedef RF_2 RF; RF f(fun, t1, t2); return callProtected(RF::call, reinterpret_cast(&f)); } template class RF_3 { public: RF_3(Fun f, T1 t1, T2 t2, T3 t3) : _f(f), _t1(t1), _t2(t2), _t3(t3) {} inline VALUE operator()() { return _f(_t1, _t2, _t3); } static inline VALUE call(VALUE f) { return (*reinterpret_cast(f))(); } private: Fun _f; T1 _t1; T2 _t2; T3 _t3; }; template inline VALUE callRuby(Fun fun, T1 t1, T2 t2, T3 t3) { typedef RF_3 RF; RF f(fun, t1, t2, t3); return callProtected(RF::call, reinterpret_cast(&f)); } template class RF_4 { public: RF_4(Fun f, T1 t1, T2 t2, T3 t3, T4 t4) : _f(f), _t1(t1), _t2(t2), _t3(t3), _t4(t4) {} inline VALUE operator()() { return _f(_t1, _t2, _t3, _t4); } static inline VALUE call(VALUE f) { return (*reinterpret_cast(f))(); } private: Fun _f; T1 _t1; T2 _t2; T3 _t3; T4 _t4; }; template inline VALUE callRuby(Fun fun, T1 t1, T2 t2, T3 t3, T4 t4) { typedef RF_4 RF; RF f(fun, t1, t2, t3, t4); return callProtected(RF::call, reinterpret_cast(&f)); } template class RF_6 { public: RF_6(Fun f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) : _f(f), _t1(t1), _t2(t2), _t3(t3), _t4(t4), _t5(t5), _t6(t6) { } inline VALUE operator()() { return _f(_t1, _t2, _t3, _t4, _t5, _t6); } static inline VALUE call(VALUE f) { return (*reinterpret_cast(f))(); } private: Fun _f; T1 _t1; T2 _t2; T3 _t3; T4 _t4; T5 _t5; T6 _t6; }; template inline VALUE callRuby(Fun fun, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) { typedef RF_6 RF; RF f(fun, t1, t2, t3, t4, t5, t6); return callProtected(RF::call, reinterpret_cast(&f)); } // // The callRubyVoid functions are used to invoke Ruby C API functions // while translating any Ruby exception into RubyException so that // C++ objects are cleaned up properly. Overloadings are provided // to support API functions that accept multiple arguments. // template void callRubyVoid(Fun fun); template void callRubyVoid(Fun fun, T1 t1); template void callRubyVoid(Fun fun, T1 t1, T2 t2); template void callRubyVoid(Fun fun, T1 t1, T2 t2, T3 t3); template void callRubyVoid(Fun fun, T1 t1, T2 t2, T3 t3, T4 t4); template class RFV_0 { public: RFV_0(Fun f) : _f(f) {} inline void operator()() { _f(); } static inline VALUE call(VALUE f) { (*reinterpret_cast(f))(); return Qnil; } private: Fun _f; }; template inline void callRubyVoid(Fun fun) { typedef RFV_0 RF; RF f(fun); callProtected(RF::call, reinterpret_cast(&f)); } template class RFV_1 { public: RFV_1(Fun f, T1 t1) : _f(f), _t1(t1) {} inline void operator()() { _f(_t1); } static inline VALUE call(VALUE f) { (*reinterpret_cast(f))(); return Qnil; } private: Fun _f; T1 _t1; }; template inline void callRubyVoid(Fun fun, T1 t1) { typedef RFV_1 RF; RF f(fun, t1); callProtected(RF::call, reinterpret_cast(&f)); } template class RFV_2 { public: RFV_2(Fun f, T1 t1, T2 t2) : _f(f), _t1(t1), _t2(t2) {} inline void operator()() { _f(_t1, _t2); } static inline VALUE call(VALUE f) { (*reinterpret_cast(f))(); return Qnil; } private: Fun _f; T1 _t1; T2 _t2; }; template inline void callRubyVoid(Fun fun, T1 t1, T2 t2) { typedef RFV_2 RF; RF f(fun, t1, t2); callProtected(RF::call, reinterpret_cast(&f)); } template class RFV_3 { public: RFV_3(Fun f, T1 t1, T2 t2, T3 t3) : _f(f), _t1(t1), _t2(t2), _t3(t3) {} inline void operator()() { _f(_t1, _t2, _t3); } static inline VALUE call(VALUE f) { (*reinterpret_cast(f))(); return Qnil; } private: Fun _f; T1 _t1; T2 _t2; T3 _t3; }; template inline void callRubyVoid(Fun fun, T1 t1, T2 t2, T3 t3) { typedef RFV_3 RF; RF f(fun, t1, t2, t3); callProtected(RF::call, reinterpret_cast(&f)); } template class RFV_4 { public: RFV_4(Fun f, T1 t1, T2 t2, T3 t3, T4 t4) : _f(f), _t1(t1), _t2(t2), _t3(t3), _t4(t4) {} inline void operator()() { _f(_t1, _t2, _t3, _t4); } static inline VALUE call(VALUE f) { (*reinterpret_cast(f))(); return Qnil; } private: Fun _f; T1 _t1; T2 _t2; T3 _t3; T4 _t4; }; template inline void callRubyVoid(Fun fun, T1 t1, T2 t2, T3 t3, T4 t4) { typedef RFV_4 RF; RF f(fun, t1, t2, t3, t4); callProtected(RubyFunction(RF::call), reinterpret_cast(&f)); } // // Create an array with the given size. May raise RubyException. // // Note that the length of the array returned by this function is already // set to the requested size. This prevents the array's elements from being // prematurely garbage-collected, but it means the array must be populated // via direct access to its buffer and not by pushing elements onto the // array using rb_ary_push: // // VALUE arr = createArray(size); // for(long i = 0; i < size; ++i) // { // RARRAY_ASET(arr, i, val); // } // VALUE createArray(long); // // Create the Ruby equivalent of an Ice local exception. // VALUE convertLocalException(const Ice::LocalException&); } // // The macros ICE_RUBY_TRY and ICE_RUBY_CATCH must be used in place of try/catch in // every entry point into the extension. They handle the translation of C++ // exceptions into Ruby exceptions and ensure that C++ objects are cleaned up properly. // #define ICE_RUBY_TRY \ volatile VALUE ex_ = Qnil; \ \ goto ice_start; \ \ ice_handle_exception: \ rb_exc_raise(ex_); \ \ ice_start: \ try #define ICE_RUBY_RETHROW(ex) \ ex_ = ex; \ goto ice_handle_exception; #define ICE_RUBY_CATCH \ catch(const ::IceRuby::RubyException& ex) \ { \ ICE_RUBY_RETHROW(ex.ex); \ } \ catch(const ::Ice::LocalException& ex) \ { \ ICE_RUBY_RETHROW(convertLocalException(ex)); \ } \ catch(const ::Ice::Exception& ex) \ { \ string msg_ = "unknown Ice exception: " + ex.ice_id(); \ ICE_RUBY_RETHROW(rb_exc_new2(rb_eRuntimeError, msg_.c_str())); \ } \ catch(const std::bad_alloc& ex) \ { \ ICE_RUBY_RETHROW(rb_exc_new2(rb_eNoMemError, ex.what())); \ } \ catch(const std::exception& ex) \ { \ ICE_RUBY_RETHROW(rb_exc_new2(rb_eRuntimeError, ex.what())); \ } \ catch(...) \ { \ ICE_RUBY_RETHROW(rb_exc_new2(rb_eRuntimeError, "caught unknown C++ exception")); \ } #endif