#ifndef Rice__Data_Type__ipp_ #define Rice__Data_Type__ipp_ #include "traits/attribute_traits.hpp" #include "traits/method_traits.hpp" #include "detail/NativeRegistry.hpp" #include "detail/NativeAttribute.hpp" #include "detail/default_allocation_func.hpp" #include "detail/TypeRegistry.hpp" #include "detail/Wrapper.hpp" #include "detail/NativeIterator.hpp" #include "cpp_api/Class.hpp" #include "cpp_api/String.hpp" #include "ruby_mark.hpp" #include namespace Rice { template void ruby_mark_internal(detail::Wrapper* wrapper) { // Tell the wrapper to mark the objects its keeping alive wrapper->ruby_mark(); // Get the underlying data and call custom mark function (if any) T* data = static_cast(wrapper->get()); ruby_mark(data); } template void ruby_free_internal(detail::Wrapper* wrapper) { delete wrapper; } template size_t ruby_size_internal(const T* data) { return sizeof(T); } template template inline Data_Type Data_Type::bind(const Module& klass) { if (is_bound()) { std::string message = "Type " + detail::typeName(typeid(T)) + " is already bound to a different type"; throw std::runtime_error(message.c_str()); } klass_ = klass; rb_data_type_ = new rb_data_type_t(); rb_data_type_->wrap_struct_name = strdup(Rice::detail::protect(rb_class2name, klass_)); rb_data_type_->function.dmark = reinterpret_cast(&Rice::ruby_mark_internal); rb_data_type_->function.dfree = reinterpret_cast(&Rice::ruby_free_internal); rb_data_type_->function.dsize = reinterpret_cast(&Rice::ruby_size_internal); rb_data_type_->data = nullptr; rb_data_type_->flags = RUBY_TYPED_FREE_IMMEDIATELY; if constexpr (!std::is_void_v) { rb_data_type_->parent = Data_Type::ruby_data_type(); } // Now register with the type registry detail::Registries::instance.types.add(klass_, rb_data_type_); for (typename Instances::iterator it = unbound_instances().begin(), end = unbound_instances().end(); it != end; unbound_instances().erase(it++)) { (*it)->set_value(klass); } return Data_Type(); } template inline void Data_Type::unbind() { detail::Registries::instance.types.remove(); if (klass_ != Qnil) { klass_ = Qnil; } // There could be objects floating around using the existing rb_type so // do not delete it. This is of course a memory leak. rb_data_type_ = nullptr; } template inline Data_Type::Data_Type() : Class(klass_ == Qnil ? rb_cObject : klass_) { if (!is_bound()) { unbound_instances().insert(this); } } template inline Data_Type::Data_Type(Module const& klass) : Class(klass) { this->bind(klass); } template inline Data_Type::~Data_Type() { unbound_instances().erase(this); } template inline rb_data_type_t* Data_Type::ruby_data_type() { check_is_bound(); return rb_data_type_; } template inline Class Data_Type::klass() { check_is_bound(); return klass_; } template inline Data_Type& Data_Type::operator=(Module const& klass) { this->bind(klass); return *this; } template template inline Data_Type& Data_Type::define_constructor(Constructor_T constructor, Arg_Ts const& ...args) { check_is_bound(); // Define a Ruby allocator which creates the Ruby object detail::protect(rb_define_alloc_func, static_cast(*this), detail::default_allocation_func); // Define an initialize function that will create the C++ object this->define_method("initialize", &Constructor_T::construct, args...); return *this; } template template inline Data_Type& Data_Type::define_director() { if (!detail::Registries::instance.types.isDefined()) { Data_Type::bind(*this); } // TODO - hack to fake Ruby into thinking that a Director is // the same as the base data type Data_Type::rb_data_type_ = Data_Type::rb_data_type_; return *this; } template inline bool Data_Type::is_bound() { return klass_ != Qnil; } template inline bool Data_Type::is_descendant(VALUE value) { check_is_bound(); return detail::protect(rb_obj_is_kind_of, value, klass_) == Qtrue; } template inline void Data_Type::check_is_bound() { if (!is_bound()) { std::string message = "Type " + detail::typeName(typeid(T)) + " is not bound"; throw std::runtime_error(message.c_str()); } } template inline Data_Type define_class_under(Object module, char const* name) { if (detail::Registries::instance.types.isDefined()) { return Data_Type(); } Class superKlass; if constexpr (std::is_void_v) { superKlass = rb_cObject; } else { superKlass = Data_Type::klass(); } Class c = define_class_under(module, name, superKlass); c.undef_creation_funcs(); return Data_Type::template bind(c); } template inline Data_Type define_class(char const* name) { if (detail::Registries::instance.types.isDefined()) { return Data_Type(); } Class superKlass; if constexpr (std::is_void_v) { superKlass = rb_cObject; } else { superKlass = Data_Type::klass(); } Class c = define_class(name, superKlass); c.undef_creation_funcs(); return Data_Type::template bind(c); } template template inline Data_Type& Data_Type::define_iterator(Iterator_Func_T begin, Iterator_Func_T end, std::string name) { // Define a NativeIterator to bridge Ruby to C++ detail::NativeIterator::define(Data_Type::klass(), name, begin, end); // Include enumerable support this->klass().include_module(rb_mEnumerable); return *this; } template template inline Data_Type& Data_Type::define_attr(std::string name, Attribute_T attribute, AttrAccess access) { // Make sure the Attribute type has been previously seen by Rice detail::verifyType::attr_type>(); // Define native attribute detail::NativeAttribute::define(klass_, name, std::forward(attribute), access); return *this; } template template inline Data_Type& Data_Type::define_singleton_attr(std::string name, Attribute_T attribute, AttrAccess access) { // Make sure the Attribute type has been previously seen by Rice detail::verifyType::attr_type>(); // Define native attribute VALUE singleton = detail::protect(rb_singleton_class, this->value()); detail::NativeAttribute::define(singleton, name, std::forward(attribute), access); return *this; } template template inline void Data_Type::wrap_native_call(VALUE klass, std::string name, Function_T&& function, MethodInfo* methodInfo) { // Make sure the return type and arguments have been previously seen by Rice using traits = detail::method_traits; detail::verifyType(); detail::verifyTypes(); // Define a NativeFunction to bridge Ruby to C++ detail::NativeFunction::define(klass, name, std::forward(function), methodInfo); } } #endif