#include "Jump_Tag.hpp" #include "../Exception_defn.hpp" #include namespace Rice::detail { template inline RubyFunction::RubyFunction(Function_T func, const Arg_Ts&... args) : func_(func), args_(std::forward_as_tuple(args...)) { } template inline Return_T RubyFunction::operator()() { const int TAG_RAISE = 0x6; // From Ruby header files int state = 0; // Setup a thread local variable to capture the result of the Ruby function call. // We use thread_local because the lambda has to be captureless so it can // be converted to a function pointer callable by C. // The thread local variable avoids having to cast the result to VALUE and then // back again to Return_T. The problem with that is the translation is not lossless // in some cases - for example a double with value of -1.0 does not roundrip. // thread_local std::any result; // Callback that will invoke the Ruby function using Functor_T = RubyFunction; auto callback = [](VALUE value) { Functor_T* functor = (Functor_T*)value; if constexpr (std::is_same_v) { std::apply(functor->func_, functor->args_); } else { result = std::apply(functor->func_, functor->args_); } return Qnil; }; // Now call rb_protect which will invoke the callback lambda above rb_protect(callback, (VALUE)this, &state); // Did anything go wrong? if (state == 0) { if constexpr (!std::is_same_v) { return std::any_cast(result); } } else { VALUE err = rb_errinfo(); if (state == TAG_RAISE && RTEST(err)) { rb_set_errinfo(Qnil); throw Rice::Exception(err); } else { throw Jump_Tag(state); } } } // Create a functor for calling a Ruby function and define some aliases for readability. template inline Return_T protect(Return_T(*func)(Arg_Ts...), Arg_Ts...args) { using Function_T = Return_T(*)(Arg_Ts...); auto rubyFunction = RubyFunction(func, args...); return rubyFunction(); } } namespace Rice { template inline Return_T protect(Return_T(*func)(Arg_Ts...), Arg_Ts...args) { using Function_T = Return_T(*)(Arg_Ts...); auto rubyFunction = detail::RubyFunction(func, args...); return rubyFunction(); } }