Fix #524: Support std::vector of non-copyable types like std::unique_ptr (#648)

Several STL bootstrap functions unconditionally instantiated copy-dependent
operations (copy constructor, assignment, push_back/push_front by const ref,
insert_at, and resize with fill value), causing compilation failures when
registering containers of move-only types like std::unique_ptr. Guard these
operations with if constexpr(std::is_copy_constructible_v<value_type>) so they
are only compiled when the element type supports copying.

Co-authored-by: leftibot <leftibot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Jason Turner <jason@emptycrate.com>
This commit is contained in:
leftibot 2026-04-11 16:44:06 -06:00 committed by GitHub
parent 11fec25112
commit 005f18feb2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 88 additions and 52 deletions

View File

@ -18,6 +18,7 @@
#include <functional>
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <typeinfo>
#include <vector>
@ -170,18 +171,22 @@ namespace chaiscript::bootstrap::standard_library {
/// http://www.sgi.com/tech/stl/Assignable.html
template<typename ContainerType>
void assignable_type(const std::string &type, Module &m) {
copy_constructor<ContainerType>(type, m);
operators::assign<ContainerType>(m);
if constexpr (std::is_copy_constructible_v<typename ContainerType::value_type>) {
copy_constructor<ContainerType>(type, m);
operators::assign<ContainerType>(m);
}
}
/// Add container resize concept to the given ContainerType
/// http://www.cplusplus.com/reference/stl/
template<typename ContainerType>
void resizable_type(const std::string & /*type*/, Module &m) {
m.add(fun([](ContainerType *a, typename ContainerType::size_type n, const typename ContainerType::value_type &val) {
return a->resize(n, val);
}),
"resize");
if constexpr (std::is_copy_constructible_v<typename ContainerType::value_type>) {
m.add(fun([](ContainerType *a, typename ContainerType::size_type n, const typename ContainerType::value_type &val) {
return a->resize(n, val);
}),
"resize");
}
m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { return a->resize(n); }), "resize");
}
@ -213,13 +218,15 @@ namespace chaiscript::bootstrap::standard_library {
/// http://www.sgi.com/tech/stl/Sequence.html
template<typename ContainerType>
void sequence_type(const std::string & /*type*/, Module &m) {
m.add(fun(&detail::insert_at<ContainerType>), []() -> std::string {
if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
return "insert_ref_at";
} else {
return "insert_at";
}
}());
if constexpr (std::is_copy_constructible_v<typename ContainerType::value_type>) {
m.add(fun(&detail::insert_at<ContainerType>), []() -> std::string {
if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
return "insert_ref_at";
} else {
return "insert_at";
}
}());
}
m.add(fun(&detail::erase_at<ContainerType>), "erase_at");
}
@ -245,27 +252,29 @@ namespace chaiscript::bootstrap::standard_library {
}),
"back");
using push_back = void (ContainerType::*)(const typename ContainerType::value_type &);
m.add(fun(static_cast<push_back>(&ContainerType::push_back)), [&]() -> std::string {
if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
m.eval("# Pushes the second value onto the container while making a clone of the value\n"
"def push_back("
+ type
+ " container, x)\n"
"{ \n"
" if (x.is_var_return_value()) {\n"
" x.reset_var_return_value() \n"
" container.push_back_ref(x) \n"
" } else { \n"
" container.push_back_ref(clone(x)); \n"
" }\n"
"} \n");
if constexpr (std::is_copy_constructible_v<typename ContainerType::value_type>) {
using push_back = void (ContainerType::*)(const typename ContainerType::value_type &);
m.add(fun(static_cast<push_back>(&ContainerType::push_back)), [&]() -> std::string {
if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
m.eval("# Pushes the second value onto the container while making a clone of the value\n"
"def push_back("
+ type
+ " container, x)\n"
"{ \n"
" if (x.is_var_return_value()) {\n"
" x.reset_var_return_value() \n"
" container.push_back_ref(x) \n"
" } else { \n"
" container.push_back_ref(clone(x)); \n"
" }\n"
"} \n");
return "push_back_ref";
} else {
return "push_back";
}
}());
return "push_back_ref";
} else {
return "push_back";
}
}());
}
m.add(fun(&ContainerType::pop_back), "pop_back");
}
@ -295,25 +304,27 @@ namespace chaiscript::bootstrap::standard_library {
}),
"front");
m.add(fun(static_cast<push_ptr>(&ContainerType::push_front)), [&]() -> std::string {
if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
m.eval("# Pushes the second value onto the front of container while making a clone of the value\n"
"def push_front("
+ type
+ " container, x)\n"
"{ \n"
" if (x.is_var_return_value()) {\n"
" x.reset_var_return_value() \n"
" container.push_front_ref(x) \n"
" } else { \n"
" container.push_front_ref(clone(x)); \n"
" }\n"
"} \n");
return "push_front_ref";
} else {
return "push_front";
}
}());
if constexpr (std::is_copy_constructible_v<typename ContainerType::value_type>) {
m.add(fun(static_cast<push_ptr>(&ContainerType::push_front)), [&]() -> std::string {
if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
m.eval("# Pushes the second value onto the front of container while making a clone of the value\n"
"def push_front("
+ type
+ " container, x)\n"
"{ \n"
" if (x.is_var_return_value()) {\n"
" x.reset_var_return_value() \n"
" container.push_front_ref(x) \n"
" } else { \n"
" container.push_front_ref(clone(x)); \n"
" }\n"
"} \n");
return "push_front_ref";
} else {
return "push_front";
}
}());
}
m.add(fun(static_cast<pop_ptr>(&ContainerType::pop_front)), "pop_front");
}

View File

@ -1437,6 +1437,31 @@ TEST_CASE("Issue #421 - Switch with type_conversion does not compare destroyed o
})") == 0);
}
// Issue #524: A std::vector of std::unique_ptrs can't be added
// vector_type should compile with non-copyable value types by
// skipping copy-dependent operations via if constexpr.
struct Issue524_Foo {
int value = 42;
};
TEST_CASE("Issue #524 - vector of unique_ptr can be registered") {
using VecType = std::vector<std::unique_ptr<Issue524_Foo>>;
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
// This should compile and not throw - previously failed to compile
// because vector_type tried to instantiate copy constructor, assignment,
// push_back(const T&), insert_at(const T&), and resize(n, const T&)
// for the non-copyable std::unique_ptr<Issue524_Foo>.
chaiscript::ModulePtr m = std::make_shared<chaiscript::Module>();
chaiscript::bootstrap::standard_library::vector_type<VecType>("UniqueVec", *m);
CHECK_NOTHROW(chai.add(m));
// Verify basic operations still work
CHECK(chai.eval<size_t>("var v = UniqueVec(); v.size()") == 0);
CHECK(chai.eval<bool>("var v2 = UniqueVec(); v2.empty()") == true);
}
// Issue #625: function_less_than comparator must satisfy strict-weak ordering.
// Registering overloaded functions with different arities triggered a
// std::stable_sort assertion on macOS 15.2 (hardened libc++) because the