From ce843bc068331d4239ace6ae5f71b15dbe60cdd2 Mon Sep 17 00:00:00 2001 From: David Sobek Date: Mon, 27 Apr 2026 14:39:56 -0600 Subject: [PATCH 1/2] Revert "Preserve numeric types for literal subtree port values" This reverts commit d248867772523cfce322bc36e84578b42f5bebb6. --- src/xml_parsing.cpp | 55 ++--------------------------------------- tests/gtest_subtree.cpp | 38 ---------------------------- 2 files changed, 2 insertions(+), 91 deletions(-) diff --git a/src/xml_parsing.cpp b/src/xml_parsing.cpp index ba1bb9f26..a25a53a6f 100644 --- a/src/xml_parsing.cpp +++ b/src/xml_parsing.cpp @@ -12,12 +12,10 @@ #include #include -#include #include #include #include #include -#include #include #include #include @@ -1008,59 +1006,10 @@ void BT::XMLParser::PImpl::recursivelyCreateSubtree(const std::string& tree_ID, } else { - // constant value: set it into the BB with appropriate type + // constant string: just set that constant value into the BB // IMPORTANT: this must not be autoremapped!!! new_bb->enableAutoRemapping(false); - const std::string str_value(port_value); - - // Try to preserve numeric types so that Script expressions - // can perform arithmetic without type-mismatch errors. - // Use std::from_chars with strict full-string validation to avoid - // false positives on compound strings like "1;2;3" or "2.2;2.4". - bool stored = false; - if(!str_value.empty()) - { - const char* begin = str_value.data(); - const char* end = begin + str_value.size(); - // Try integer first (no decimal point, no exponent notation). - // Use int when the value fits, to match the most common port - // declarations. Fall back to int64_t for larger values. - if(str_value.find('.') == std::string::npos && - str_value.find('e') == std::string::npos && - str_value.find('E') == std::string::npos) - { - int64_t int_val = 0; - auto [ptr, ec] = std::from_chars(begin, end, int_val); - if(ec == std::errc() && ptr == end) - { - if(int_val >= std::numeric_limits::min() && - int_val <= std::numeric_limits::max()) - { - new_bb->set(port_name, static_cast(int_val)); - } - else - { - new_bb->set(port_name, int_val); - } - stored = true; - } - } - // Try double - if(!stored) - { - double dbl_val = 0; - auto [ptr, ec] = std::from_chars(begin, end, dbl_val); - if(ec == std::errc() && ptr == end) - { - new_bb->set(port_name, dbl_val); - stored = true; - } - } - } - if(!stored) - { - new_bb->set(port_name, str_value); - } + new_bb->set(port_name, static_cast(port_value)); new_bb->enableAutoRemapping(do_autoremap); } } diff --git a/tests/gtest_subtree.cpp b/tests/gtest_subtree.cpp index 8d15893ed..479b3ffc1 100644 --- a/tests/gtest_subtree.cpp +++ b/tests/gtest_subtree.cpp @@ -799,41 +799,3 @@ TEST(SubTree, SubtreeNameNotRegistered) ASSERT_ANY_THROW(auto tree = factory.createTreeFromText(xml_text)); ASSERT_ANY_THROW(factory.registerBehaviorTreeFromText(xml_text)); } - -// Regression test: literal numeric values passed to subtrees should preserve -// their numeric type so that Script expressions can do arithmetic. -TEST(SubTree, LiteralNumericPortsPreserveType) -{ - // clang-format off - static const char* xml_text = R"( - - - - - - - - - - - - - - - - - -)"; - // clang-format on - - BehaviorTreeFactory factory; - - auto tree = factory.createTreeFromText(xml_text); - - // Set the remapped parent value as an integer - tree.rootBlackboard()->set("from_parent", 100); - - const auto status = tree.tickWhileRunning(); - ASSERT_EQ(status, NodeStatus::SUCCESS); -} From 959730c4615df1e19b30a0adbbf16780a59f4ddd Mon Sep 17 00:00:00 2001 From: David Sobek Date: Mon, 27 Apr 2026 16:47:48 -0600 Subject: [PATCH 2/2] Allow for strongly typed numbers to be added with non-strongly typed numbers --- .../behaviortree_cpp/scripting/operators.hpp | 6 +- tests/gtest_subtree.cpp | 70 +++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/include/behaviortree_cpp/scripting/operators.hpp b/include/behaviortree_cpp/scripting/operators.hpp index 4d41b6a88..5f121da20 100644 --- a/include/behaviortree_cpp/scripting/operators.hpp +++ b/include/behaviortree_cpp/scripting/operators.hpp @@ -213,7 +213,11 @@ struct ExprBinaryArithmetic : ExprBase throw RuntimeError(ErrorNotInit("right", opStr())); } - if(rhs_v.isNumber() && lhs_v.isNumber()) + // If just one value is strongly typed as a number, and the other can be casted to one, do arithmetic. If both are not strongly typed, continue. In the case of `+`, non-strongly typed values will result in concatenation. + bool are_numbers = (rhs_v.isNumber() && lhs_v.isNumber()) || + ((rhs_v.tryCast().has_value() && lhs_v.isNumber()) != + (rhs_v.isNumber() && lhs_v.tryCast().has_value())); + if(are_numbers) { auto lv = lhs_v.cast(); auto rv = rhs_v.cast(); diff --git a/tests/gtest_subtree.cpp b/tests/gtest_subtree.cpp index 479b3ffc1..0bf48f8ae 100644 --- a/tests/gtest_subtree.cpp +++ b/tests/gtest_subtree.cpp @@ -332,6 +332,76 @@ TEST(SubTree, StringConversions_Issue530) tree.tickOnce(); } +// Regression test for https://github.com/PickNikRobotics/moveit_pro/issues/18587. +TEST(SubTree, ScriptingTypingIssue) +{ + static const char* xml_text = R"( + + + + + + + + + +