diff --git a/include/behaviortree_cpp/utils/safe_any.hpp b/include/behaviortree_cpp/utils/safe_any.hpp index 7a9a9cb53..30ce11c66 100644 --- a/include/behaviortree_cpp/utils/safe_any.hpp +++ b/include/behaviortree_cpp/utils/safe_any.hpp @@ -453,12 +453,10 @@ inline nonstd::expected Any::stringToNumber() const #else try { - if constexpr(std::is_same_v) - { - return std::stoul(str.toStdString()); - } if constexpr(std::is_integral_v) { + // convert() rejects values that don't fit in T, matching the + // std::from_chars branch above. const int64_t val = std::stol(str.toStdString()); Any temp_any(val); return temp_any.convert(); diff --git a/tests/gtest_any.cpp b/tests/gtest_any.cpp index 74da3454a..f09a21280 100644 --- a/tests/gtest_any.cpp +++ b/tests/gtest_any.cpp @@ -269,3 +269,16 @@ TEST(Any, StringToNumberIgnoresLocale) EXPECT_EQ(Any(std::string("42")).cast(), 42); EXPECT_ANY_THROW(auto res = Any(std::string("fifty")).cast()); } + +TEST(Any, StringToUint16IsRangeChecked) +{ + // A string that doesn't fit in uint16_t must be rejected, not wrapped. + EXPECT_EQ(Any(std::string("65535")).cast(), 65535); + EXPECT_ANY_THROW(auto res = Any(std::string("65536")).cast()); + EXPECT_ANY_THROW(auto res = Any(std::string("70000")).cast()); + EXPECT_ANY_THROW(auto res = Any(std::string("-1")).cast()); + + auto bb = Blackboard::create(); + bb->set("value", std::string("70000")); + EXPECT_ANY_THROW(auto res = bb->get("value")); +}