Fix #374: Support nested vector type conversions (#692)

vector_conversion's element conversion used Cast_Helper directly, which
only performs exact type matching. For nested vectors like
std::vector<std::vector<double>>, each inner element is a
std::vector<Boxed_Value> that needs its own conversion to
std::vector<double>. Added a recursive convert_vector_element helper
that detects std::vector value types and converts them from
std::vector<Boxed_Value> before falling back to Cast_Helper for
leaf types.

Co-authored-by: leftibot <leftibot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
leftibot 2026-04-18 12:57:20 -06:00 committed by GitHub
parent b64c930b16
commit d2ec2a82a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 53 additions and 1 deletions

View File

@ -498,6 +498,29 @@ namespace chaiscript {
func); func);
} }
namespace detail {
template<typename T>
struct is_std_vector : std::false_type {};
template<typename T, typename A>
struct is_std_vector<std::vector<T, A>> : std::true_type {};
template<typename T>
T convert_vector_element(const Boxed_Value &bv) {
if constexpr (is_std_vector<T>::value) {
const auto &inner = Cast_Helper<const std::vector<Boxed_Value> &>::cast(bv, nullptr);
T result;
result.reserve(inner.size());
for (const Boxed_Value &elem : inner) {
result.push_back(convert_vector_element<typename T::value_type>(elem));
}
return result;
} else {
return Cast_Helper<T>::cast(bv, nullptr);
}
}
} // namespace detail
template<typename To> template<typename To>
Type_Conversion vector_conversion() { Type_Conversion vector_conversion() {
auto func = [](const Boxed_Value &t_bv) -> Boxed_Value { auto func = [](const Boxed_Value &t_bv) -> Boxed_Value {
@ -506,7 +529,7 @@ namespace chaiscript {
To vec; To vec;
vec.reserve(from_vec.size()); vec.reserve(from_vec.size());
for (const Boxed_Value &bv : from_vec) { for (const Boxed_Value &bv : from_vec) {
vec.push_back(detail::Cast_Helper<typename To::value_type>::cast(bv, nullptr)); vec.push_back(detail::convert_vector_element<typename To::value_type>(bv));
} }
return Boxed_Value(std::move(vec)); return Boxed_Value(std::move(vec));

View File

@ -1806,6 +1806,35 @@ TEST_CASE("push_back on script vector with vector_conversion") {
== "hello,world"); == "hello,world");
} }
TEST_CASE("vector of vectors conversion (issue #374)") {
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
auto m = std::make_shared<chaiscript::Module>();
chaiscript::bootstrap::standard_library::vector_type<std::vector<double>>("VectorDouble", *m);
chaiscript::bootstrap::standard_library::vector_type<std::vector<std::vector<double>>>("VectorVectorDouble", *m);
m->add(chaiscript::vector_conversion<std::vector<double>>());
m->add(chaiscript::vector_conversion<std::vector<std::vector<double>>>());
chai.add(m);
chai.add(chaiscript::fun([](const std::vector<std::vector<double>> &v) -> double {
double sum = 0;
for (const auto &inner : v) {
for (const auto d : inner) {
sum += d;
}
}
return sum;
}), "sum_nested");
CHECK(chai.eval<double>("sum_nested([[1.0, 2.0], [3.0, 4.0]])") == Approx(10.0));
CHECK(chai.eval<bool>(
"auto v = VectorVectorDouble();"
"v = [[1.0, 2.0], [3.0, 4.0]];"
"v.size() == 2"
));
}
// Regression test for issue #607: AST_Node_Trace must be a complete type // Regression test for issue #607: AST_Node_Trace must be a complete type
// when used in eval_error's std::vector<AST_Node_Trace> call_stack member. // when used in eval_error's std::vector<AST_Node_Trace> call_stack member.
// This failed to compile with C++20 on clang/libc++ when AST_Node_Trace // This failed to compile with C++20 on clang/libc++ when AST_Node_Trace