Skip to content

Commit d248867

Browse files
L4co77claude
andcommitted
Preserve numeric types for literal subtree port values
When literal values are passed to SubTree ports (not blackboard remapping), detect numeric types (int64_t, double) before storing them in the child blackboard. Previously all literals were stored as std::string, which caused type-mismatch errors in Script expressions that tried to do arithmetic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c852132 commit d248867

2 files changed

Lines changed: 91 additions & 2 deletions

File tree

src/xml_parsing.cpp

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1111
*/
1212

13+
#include <charconv>
1314
#include <cstdio>
1415
#include <cstring>
1516
#include <functional>
1617
#include <iostream>
18+
#include <limits>
1719
#include <list>
1820
#include <sstream>
1921
#include <string>
@@ -1000,10 +1002,59 @@ void BT::XMLParser::PImpl::recursivelyCreateSubtree(const std::string& tree_ID,
10001002
}
10011003
else
10021004
{
1003-
// constant string: just set that constant value into the BB
1005+
// constant value: set it into the BB with appropriate type
10041006
// IMPORTANT: this must not be autoremapped!!!
10051007
new_bb->enableAutoRemapping(false);
1006-
new_bb->set(port_name, static_cast<std::string>(port_value));
1008+
const std::string str_value(port_value);
1009+
1010+
// Try to preserve numeric types so that Script expressions
1011+
// can perform arithmetic without type-mismatch errors.
1012+
// Use std::from_chars with strict full-string validation to avoid
1013+
// false positives on compound strings like "1;2;3" or "2.2;2.4".
1014+
bool stored = false;
1015+
if(!str_value.empty())
1016+
{
1017+
const char* begin = str_value.data();
1018+
const char* end = begin + str_value.size();
1019+
// Try integer first (no decimal point, no exponent notation).
1020+
// Use int when the value fits, to match the most common port
1021+
// declarations. Fall back to int64_t for larger values.
1022+
if(str_value.find('.') == std::string::npos &&
1023+
str_value.find('e') == std::string::npos &&
1024+
str_value.find('E') == std::string::npos)
1025+
{
1026+
int64_t int_val = 0;
1027+
auto [ptr, ec] = std::from_chars(begin, end, int_val);
1028+
if(ec == std::errc() && ptr == end)
1029+
{
1030+
if(int_val >= std::numeric_limits<int>::min() &&
1031+
int_val <= std::numeric_limits<int>::max())
1032+
{
1033+
new_bb->set(port_name, static_cast<int>(int_val));
1034+
}
1035+
else
1036+
{
1037+
new_bb->set(port_name, int_val);
1038+
}
1039+
stored = true;
1040+
}
1041+
}
1042+
// Try double
1043+
if(!stored)
1044+
{
1045+
double dbl_val = 0;
1046+
auto [ptr, ec] = std::from_chars(begin, end, dbl_val);
1047+
if(ec == std::errc() && ptr == end)
1048+
{
1049+
new_bb->set(port_name, dbl_val);
1050+
stored = true;
1051+
}
1052+
}
1053+
}
1054+
if(!stored)
1055+
{
1056+
new_bb->set(port_name, str_value);
1057+
}
10071058
new_bb->enableAutoRemapping(do_autoremap);
10081059
}
10091060
}

tests/gtest_subtree.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,3 +757,41 @@ TEST(SubTree, SubtreeNameNotRegistered)
757757
ASSERT_ANY_THROW(auto tree = factory.createTreeFromText(xml_text));
758758
ASSERT_ANY_THROW(factory.registerBehaviorTreeFromText(xml_text));
759759
}
760+
761+
// Regression test: literal numeric values passed to subtrees should preserve
762+
// their numeric type so that Script expressions can do arithmetic.
763+
TEST(SubTree, LiteralNumericPortsPreserveType)
764+
{
765+
// clang-format off
766+
static const char* xml_text = R"(
767+
<root BTCPP_format="4" main_tree_to_execute="MainTree">
768+
769+
<BehaviorTree ID="MainTree">
770+
<Sequence>
771+
<SubTree ID="DoMath" int_val="42" dbl_val="3.14" str_val="hello"
772+
remapped_val="{from_parent}" />
773+
</Sequence>
774+
</BehaviorTree>
775+
776+
<BehaviorTree ID="DoMath">
777+
<Sequence>
778+
<ScriptCondition code=" int_val + 1 == 43 " />
779+
<ScriptCondition code=" dbl_val > 3.0 " />
780+
<ScriptCondition code=" remapped_val + 1 == 101 " />
781+
</Sequence>
782+
</BehaviorTree>
783+
784+
</root>
785+
)";
786+
// clang-format on
787+
788+
BehaviorTreeFactory factory;
789+
790+
auto tree = factory.createTreeFromText(xml_text);
791+
792+
// Set the remapped parent value as an integer
793+
tree.rootBlackboard()->set("from_parent", 100);
794+
795+
const auto status = tree.tickWhileRunning();
796+
ASSERT_EQ(status, NodeStatus::SUCCESS);
797+
}

0 commit comments

Comments
 (0)