Skip to content

Commit

Permalink
Merge pull request #226 from ruby-rice/dev
Browse files Browse the repository at this point in the history
Various Fixes / Updates
  • Loading branch information
cfis authored Nov 27, 2024
2 parents 3f0e8cc + 747c3ea commit f21098e
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 61 deletions.
14 changes: 11 additions & 3 deletions rice/Data_Type.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@

#include "traits/attribute_traits.hpp"
#include "traits/method_traits.hpp"
#include "detail/NativeRegistry.hpp"
#include "detail/NativeAttributeGet.hpp"
#include "detail/NativeAttributeSet.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 <stdexcept>
Expand Down Expand Up @@ -158,6 +155,17 @@ namespace Rice
return *this;
}

template<typename T>
template<typename Func_T>
Data_Type<T>& Data_Type<T>::define(Func_T func)
{
// The passed in this pointer is an RValue, so we need to keep it alive by
// assigning it to a const lvalue
const Data_Type<T>& dummy = *this;
func(*this);
return *this;
}

template<typename T>
template<typename Director_T>
inline Data_Type<T>& Data_Type<T>::define_director()
Expand Down
29 changes: 26 additions & 3 deletions rice/Data_Type_defn.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ namespace Rice
static_assert(std::is_same_v<detail::intrinsic_type<T>, T>);

public:
using type = T;

//! Default constructor which does not bind.
/*! No member functions must be called on this Data_Type except bind,
* until the type is bound.
Expand Down Expand Up @@ -65,7 +67,28 @@ namespace Rice
* \endcode
*/
template<typename Constructor_T, typename...Arg_Ts>
Data_Type<T> & define_constructor(Constructor_T constructor, Arg_Ts const& ...args);
Data_Type<T>& define_constructor(Constructor_T constructor, Arg_Ts const& ...args);

/*! Runs a function that should define this Data_Types methods and attributes.
* This is useful when creating classes from a C++ class template.
*
* \param builder A function that addes methods/attributes to this class
*
* For example:
* \code
* void builder(Data_Type<Matrix<T, R, C>>& klass)
* {
* klass.define_method...
* return klass;
* }
*
* define_class<<Matrix<T, R, C>>>("Matrix")
* .build(&builder);
*
* \endcode
*/
template<typename Func_T>
Data_Type<T>& define(Func_T func);

//! Register a Director class for this class.
/*! For any class that uses Rice::Director to enable polymorphism
Expand Down Expand Up @@ -133,7 +156,7 @@ namespace Rice
* \return *this
*/
template <typename Base_T = void>
static Data_Type bind(const Module& klass);
static Data_Type<T> bind(const Module& klass);

template<typename T_, typename Base_T_>
friend Rice::Data_Type<T_> define_class_under(Object module, char const * name);
Expand Down Expand Up @@ -183,7 +206,7 @@ namespace Rice
*/
template<typename T, typename Base_T = void>
Data_Type<T> define_class(char const* name);
} // namespace Rice
}

#include "Data_Type.ipp"

Expand Down
6 changes: 6 additions & 0 deletions rice/cpp_api/Class.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ namespace Rice
return *this;
}

template<typename ...Arg_Ts>
inline Object Class::create(Arg_Ts ...args)
{
return this->call("new", args...);
}

inline const std::string Class::name()
{
const char* buffer = rb_class2name(this->value());
Expand Down
4 changes: 4 additions & 0 deletions rice/cpp_api/Class_defn.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ namespace Rice
*/
Class & undef_creation_funcs();

// Create a new instance
template<typename ...Arg_Ts>
Object create(Arg_Ts ...args);

//! Class name
/*! \return std::string.
*/
Expand Down
81 changes: 39 additions & 42 deletions rice/detail/Native.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ namespace Rice::detail
A call from ruby of some_method(1) will exactly match both signatures, but the first one
will be chosen because the parameterMatch will be 1.0 for the first overload but 0.5
for the second. */

Native* native = nullptr;

ID methodId;
Expand All @@ -66,55 +67,51 @@ namespace Rice::detail
rb_raise(rb_eRuntimeError, "Cannot get method id and class for function");
}

const std::vector<std::unique_ptr<Native>>& natives = Registries::instance.natives.lookup(klass, methodId);

if (natives.size() == 1)
{
native = natives.front().get();
}
else if (natives.size() == 0)
{
Identifier identifier(methodId);
rb_raise(rb_eArgError, "Could not find method call for %s#%s", rb_class2name(klass), identifier.c_str());
}
else
// Execute the function but make sure to catch any C++ exceptions!
return cpp_protect([&]
{
// Loop over every native to see how well they match the Ruby parameters
std::vector<Resolved> resolves;
std::transform(natives.begin(), natives.end(),
std::back_inserter(resolves),
[&](const std::unique_ptr<Native>& native)
{
return native->matches(argc, argv, self);
});
const std::vector<std::unique_ptr<Native>>& natives = Registries::instance.natives.lookup(klass, methodId);

// Now sort from best to worst
std::sort(resolves.begin(), resolves.end(), std::greater{});

// Get the best one
Resolved resolved = resolves.front();

// Did it match?
if (resolved.convertible != Convertible::None)
if (natives.size() == 1)
{
native = resolved.native;
native = natives.front().get();
}
else
else if (natives.size() == 0)
{
Identifier identifier(methodId);
rb_raise(rb_eArgError, "Could not resolve method call for %s#%s", rb_class2name(klass), identifier.c_str());
rb_raise(rb_eArgError, "Could not find method call for %s#%s", rb_class2name(klass), identifier.c_str());
}
}

return native->call(argc, argv, self);
}

inline VALUE Native::call( int argc, VALUE* argv, VALUE self)
{
// Execute the function but make sure to catch any C++ exceptions!
return cpp_protect([&]
else
{
return this->operator()(argc, argv, self);
});
// Loop over every native to see how well they match the Ruby parameters
std::vector<Resolved> resolves;
std::transform(natives.begin(), natives.end(),
std::back_inserter(resolves),
[&](const std::unique_ptr<Native>& native)
{
return native->matches(argc, argv, self);
});

// Now sort from best to worst
std::sort(resolves.begin(), resolves.end(), std::greater{});

// Get the best one
Resolved resolved = resolves.front();

// Did it match?
if (resolved.convertible != Convertible::None)
{
native = resolved.native;
}
else
{
Identifier identifier(methodId);
rb_raise(rb_eArgError, "Could not resolve method call for %s#%s", rb_class2name(klass), identifier.c_str());
}
}

// Call the C++ function
return (*native)(argc, argv, self);
});
}
}
22 changes: 11 additions & 11 deletions rice/detail/NativeFunction.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -44,31 +44,31 @@ namespace Rice::detail
}

template<typename Class_T, typename Function_T, bool IsMethod>
template<typename T, std::size_t I>
From_Ruby<T> NativeFunction<Class_T, Function_T, IsMethod>::createFromRuby()
To_Ruby<typename NativeFunction<Class_T, Function_T, IsMethod>::To_Ruby_T> NativeFunction<Class_T, Function_T, IsMethod>::createToRuby()
{
// Does the From_Ruby instantiation work with Arg?
if constexpr (std::is_constructible_v<From_Ruby<T>, Arg*>)
// Does the From_Ruby instantiation work with ReturnInfo?
if constexpr (std::is_constructible_v<To_Ruby<To_Ruby_T>, Return*>)
{
return From_Ruby<T>(&this->methodInfo_->arg(I));
return To_Ruby<To_Ruby_T>(&this->methodInfo_->returnInfo);
}
else
{
return From_Ruby<T>();
return To_Ruby<To_Ruby_T>();
}
}

template<typename Class_T, typename Function_T, bool IsMethod>
To_Ruby<typename NativeFunction<Class_T, Function_T, IsMethod>::To_Ruby_T> NativeFunction<Class_T, Function_T, IsMethod>::createToRuby()
template<typename T, std::size_t I>
From_Ruby<T> NativeFunction<Class_T, Function_T, IsMethod>::createFromRuby()
{
// Does the From_Ruby instantiation work with ReturnInfo?
if constexpr (std::is_constructible_v<To_Ruby<To_Ruby_T>, Return*>)
// Does the From_Ruby instantiation work with Arg?
if constexpr (std::is_constructible_v<From_Ruby<T>, Arg*>)
{
return To_Ruby<To_Ruby_T>(&this->methodInfo_->returnInfo);
return From_Ruby<T>(&this->methodInfo_->arg(I));
}
else
{
return To_Ruby<To_Ruby_T>();
return From_Ruby<T>();
}
}

Expand Down
2 changes: 1 addition & 1 deletion rice/detail/Wrapper.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ namespace Rice::detail
{
Registries::instance.instances.remove(this->get());

if constexpr (!std::is_void_v<T>)
if constexpr (std::is_destructible_v<T>)
{
if (this->isOwner_)
{
Expand Down
46 changes: 45 additions & 1 deletion rice/detail/from_ruby.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -1319,9 +1319,53 @@ namespace Rice::detail
return charFromRuby<unsigned char>(value);
}
}


private:
Arg* arg_ = nullptr;
};

template<>
class From_Ruby<unsigned char&>
{
public:
From_Ruby() = default;

explicit From_Ruby(Arg* arg) : arg_(arg)
{
}

Convertible is_convertible(VALUE value)
{
switch (rb_type(value))
{
case RUBY_T_STRING:
return Convertible::Exact;
break;
// This is for C++ chars which are converted to Ruby integers
case RUBY_T_FIXNUM:
return Convertible::TypeCast;
break;
default:
return Convertible::None;
}
}

unsigned char& convert(VALUE value)
{
if (value == Qnil && this->arg_ && this->arg_->hasDefaultValue())
{
return this->arg_->defaultValue<unsigned char>();
}
else
{
this->converted_ = charFromRuby<unsigned char>(value);
return this->converted_;
}
}

private:
Arg* arg_ = nullptr;
unsigned char converted_ = 0;
};

template<>
Expand Down

0 comments on commit f21098e

Please sign in to comment.