Skip to content
Merged

Dev #394

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
# Changelog

## 4.11.3 (?)

### Enhancements

* Add support for `std::shared_ptr<const T>`
* Add support for `std::unique_ptr<const T>`


## 4.11.2 (2026-02-21)

### Enhancements

* Add support for `long double`
* Improve support for references to incomplete types

## 4.11.1 (2026-02-18)

### Enhancements

* Be more lenient on wrapp Qnil values in the C++ API


## 4.11.0 (2026-02-17)

This release focuses on improving memory management.
Expand Down
184 changes: 182 additions & 2 deletions include/rice/rice.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3797,6 +3797,17 @@ namespace Rice::detail
static inline std::string name = "Float";
};

template<>
class RubyType<long double>
{
public:
using FromRuby_T = double(*)(VALUE);

static inline FromRuby_T fromRuby = rb_num2dbl;
static inline std::string packTemplate = "d*";
static inline std::string name = "Float";
};

template<>
class RubyType<void>
{
Expand Down Expand Up @@ -6073,6 +6084,35 @@ namespace Rice::detail
}
};

template<>
struct Type<long double>
{
static bool verify()
{
return true;
}

static VALUE rubyKlass()
{
return rb_cFloat;
}
};

template<int N>
struct Type<long double[N]>
{
static bool verify()
{
define_buffer<long double>();
return true;
}

static VALUE rubyKlass()
{
return rb_cString;
}
};

template<>
struct Type<void>
{
Expand Down Expand Up @@ -6601,6 +6641,62 @@ namespace Rice
Arg* arg_ = nullptr;
};

// =========== long double ============
template<>
class To_Ruby<long double>
{
public:
To_Ruby() = default;

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

VALUE convert(const long double& native)
{
return protect(rb_float_new, native);
}

private:
Arg* arg_ = nullptr;
};

template<>
class To_Ruby<long double&>
{
public:
To_Ruby() = default;

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

VALUE convert(const long double& native)
{
return protect(rb_float_new, native);
}

private:
Arg* arg_ = nullptr;
};

template<int N>
class To_Ruby<long double[N]>
{
public:
To_Ruby() = default;

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

VALUE convert(long double data[N])
{
Buffer<long double> buffer(data, N);
Data_Object<Buffer<long double>> dataObject(std::move(buffer));
return dataObject.value();
}
private:
Arg* arg_ = nullptr;
};

// =========== float ============
template<>
class To_Ruby<float>
Expand Down Expand Up @@ -7879,6 +7975,86 @@ namespace Rice::detail
Reference<double> reference_;
};

// =========== long double ============
template<>
class From_Ruby<long double>
{
public:
From_Ruby() = default;

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

long double is_convertible(VALUE value)
{
return FromRubyFundamental<long double>::is_convertible(value);
}

long double convert(VALUE value)
{
return FromRubyFundamental<long double>::convert(value);
}

private:
Arg* arg_ = nullptr;
};

template<>
class From_Ruby<long double&>
{
public:
using Reference_T = Reference<long double>;

From_Ruby() = default;

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

long double is_convertible(VALUE value)
{
switch (rb_type(value))
{
case RUBY_T_DATA:
{
if (Data_Type<Reference_T>::is_descendant(value))
{
return Convertible::Exact;
}
[[fallthrough]];
}
default:
{
return FromRubyFundamental<long double>::is_convertible(value);
}
}
}

long double& convert(VALUE value)
{
switch (rb_type(value))
{
case RUBY_T_DATA:
{
if (Data_Type<Reference_T>::is_descendant(value))
{
Reference_T* reference = unwrap<Reference_T>(value, Data_Type<Reference_T>::ruby_data_type(), false);
return reference->get();
}
[[fallthrough]];
}
default:
{
this->reference_ = Reference<long double>(value);
return this->reference_.get();
}
}
}

private:
Arg* arg_ = nullptr;
Reference<long double> reference_;
};

// =========== float ============
template<>
class From_Ruby<float>
Expand Down Expand Up @@ -8973,6 +9149,12 @@ namespace Rice::detail
{
return std::type_index(typeid(T));
}
else if constexpr (std::is_reference_v<T>)
{
// For incomplete reference types, strip the reference and use pointer.
// Can't form T* when T is a reference type (pointer-to-reference is illegal).
return std::type_index(typeid(std::remove_reference_t<T>*));
}
else
{
return std::type_index(typeid(T*));
Expand Down Expand Up @@ -14845,8 +15027,6 @@ namespace Rice
template <typename Attribute_T, typename Access_T, typename...Arg_Ts>
inline Data_Type<T>& Data_Type<T>::define_attr_internal(VALUE klass, std::string name, Attribute_T attribute, Access_T, const Arg_Ts&...args)
{
using Attr_T = typename detail::attribute_traits<Attribute_T>::attr_type;

// Define attribute getter
if constexpr (std::is_same_v<Access_T, AttrAccess::ReadWriteType> || std::is_same_v<Access_T, AttrAccess::ReadType>)
{
Expand Down
2 changes: 1 addition & 1 deletion lib/rice/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Rice
VERSION = "4.11.1"
VERSION = "4.11.2"
end
4 changes: 3 additions & 1 deletion rice/detail/Wrapper.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ namespace Rice::detail

if constexpr (is_complete_v<T>)
{
if constexpr (std::is_destructible_v<T>)
// is_abstract_v requires a complete type, so nest inside is_complete_v.
// Deleting an abstract class through a non-virtual destructor is UB.
if constexpr (std::is_destructible_v<T> && !std::is_abstract_v<T>)
{
if (this->isOwner_)
{
Expand Down
22 changes: 3 additions & 19 deletions rice/detail/from_ruby.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -1608,31 +1608,15 @@ namespace Rice::detail
}
}

void* convert(VALUE value)
std::nullptr_t convert(VALUE value)
{
if (value == Qnil)
{
return nullptr;
}

if (this->arg_ && this->arg_->isOpaque())
{
return (void*)value;
}

switch (rb_type(value))
{
case RUBY_T_NIL:
{
return nullptr;
break;
}
default:
{
throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
detail::protect(rb_obj_classname, value), "nil");
}
}
throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
detail::protect(rb_obj_classname, value), "nil");
}
private:
Arg* arg_ = nullptr;
Expand Down
4 changes: 2 additions & 2 deletions rice/stl/map.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ namespace Rice
}, Arg("key"))
.define_method("[]=", [](T& map, Key_T key, Mapped_Parameter_T value) -> Mapped_T
{
map[key] = value;
map.insert_or_assign(key, value);
return value;
}, Arg("key").keepAlive(), Arg("value").keepAlive());

Expand Down Expand Up @@ -278,7 +278,7 @@ namespace Rice
// exceptions propogate back to Ruby
return cpp_protect([&]
{
result->operator[](From_Ruby<T>().convert(key)) = From_Ruby<U>().convert(value);
result->insert_or_assign(From_Ruby<T>().convert(key), From_Ruby<U>().convert(value));
return ST_CONTINUE;
});
}
Expand Down
8 changes: 6 additions & 2 deletions rice/stl/pair.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ namespace Rice
private:
void define_constructors()
{
klass_.define_constructor(Constructor<T>())
.define_constructor(Constructor<T, First_Parameter_T, Second_Parameter_T>(), Arg("x").keepAlive(), Arg("y").keepAlive());
if constexpr (std::is_default_constructible_v<T>)
{
klass_.define_constructor(Constructor<T>());
}

klass_.define_constructor(Constructor<T, First_Parameter_T, Second_Parameter_T>(), Arg("x").keepAlive(), Arg("y").keepAlive());

if constexpr (std::is_copy_constructible_v<First_T> && std::is_copy_constructible_v<Second_T>)
{
Expand Down
8 changes: 6 additions & 2 deletions rice/stl/shared_ptr.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ namespace Rice

if constexpr (detail::is_complete_v<T> && !std::is_void_v<T>)
{
result.define_constructor(Constructor<SharedPtr_T, typename SharedPtr_T::element_type*>(), Arg("value").takeOwnership());
// is_abstract_v requires a complete type, so it must be nested inside the is_complete_v check
if constexpr (!std::is_abstract_v<T>)
{
result.define_constructor(Constructor<SharedPtr_T, typename SharedPtr_T::element_type*>(), Arg("value").takeOwnership());
}
}

// Forward methods to wrapped T
Expand Down Expand Up @@ -87,7 +91,7 @@ namespace Rice::detail
}
else if (rb_typeddata_inherited_p(this->inner_rb_data_type_, requestedType))
{
return this->data_.get();
return (void*)this->data_.get();
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion rice/stl/unique_ptr.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ namespace Rice::detail
}
else if (rb_typeddata_inherited_p(this->inner_rb_data_type_, requestedType))
{
return this->data_.get();
return (void*)this->data_.get();
}
else
{
Expand Down
4 changes: 2 additions & 2 deletions rice/stl/unordered_map.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ namespace Rice
}, Arg("key"))
.define_method("[]=", [](T& unordered_map, Key_T key, Mapped_Parameter_T value) -> Mapped_T
{
unordered_map[key] = value;
unordered_map.insert_or_assign(key, value);
return value;
}, Arg("key").keepAlive(), Arg("value").keepAlive());

Expand Down Expand Up @@ -278,7 +278,7 @@ namespace Rice
// exceptions propogate back to Ruby
return cpp_protect([&]
{
result->operator[](From_Ruby<T>().convert(key)) = From_Ruby<U>().convert(value);
result->insert_or_assign(From_Ruby<T>().convert(key), From_Ruby<U>().convert(value));
return ST_CONTINUE;
});
}
Expand Down
Loading