Skip to content

Commit 8659c0e

Browse files
committed
Fix #948: parseString supports enums with convertFromString specializations
1 parent b3dbe30 commit 8659c0e

File tree

2 files changed

+81
-7
lines changed

2 files changed

+81
-7
lines changed

include/behaviortree_cpp/tree_node.h

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "behaviortree_cpp/utils/strcat.hpp"
2121
#include "behaviortree_cpp/utils/wakeup_signal.hpp"
2222

23+
#include <charconv>
2324
#include <exception>
2425
#include <map>
2526
#include <utility>
@@ -432,17 +433,25 @@ T TreeNode::parseString(const std::string& str) const
432433
{
433434
if constexpr(std::is_enum_v<T> && !std::is_same_v<T, NodeStatus>)
434435
{
435-
auto it = config().enums->find(str);
436-
// conversion available
437-
if(it != config().enums->end())
436+
// Check the ScriptingEnumsRegistry first, if available.
437+
if(config().enums)
438438
{
439-
return static_cast<T>(it->second);
439+
auto it = config().enums->find(str);
440+
if(it != config().enums->end())
441+
{
442+
return static_cast<T>(it->second);
443+
}
440444
}
441-
else
445+
// Try numeric conversion (e.g. "2" for an enum value).
446+
int tmp = 0;
447+
auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), tmp);
448+
if(ec == std::errc() && ptr == str.data() + str.size())
442449
{
443-
// hopefully str contains a number that can be parsed. May throw
444-
return static_cast<T>(convertFromString<int>(str));
450+
return static_cast<T>(tmp);
445451
}
452+
// Fall back to convertFromString<T>, which uses a user-provided
453+
// specialization if one exists. Issue #948.
454+
return convertFromString<T>(str);
446455
}
447456
return convertFromString<T>(str);
448457
}

tests/gtest_enums.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,68 @@ TEST(Enums, SubtreeRemapping)
240240
ASSERT_EQ(status, NodeStatus::SUCCESS);
241241
ASSERT_EQ(tree.rootBlackboard()->get<BatteryStatus>("fault_status"), LOW_BATTERY);
242242
}
243+
244+
// Issue #948: enums with a convertFromString<T> specialization should be parsed
245+
// correctly via getInput, without requiring ScriptingEnumsRegistry.
246+
class ActionWithNodeType : public SyncActionNode
247+
{
248+
public:
249+
ActionWithNodeType(const std::string& name, const NodeConfig& config)
250+
: SyncActionNode(name, config)
251+
{}
252+
253+
NodeStatus tick() override
254+
{
255+
auto res = getInput<NodeType>("type");
256+
if(!res)
257+
{
258+
throw RuntimeError("getInput failed: " + res.error());
259+
}
260+
parsed_type = res.value();
261+
return NodeStatus::SUCCESS;
262+
}
263+
264+
static PortsList providedPorts()
265+
{
266+
return { InputPort<NodeType>("type") };
267+
}
268+
269+
NodeType parsed_type = NodeType::UNDEFINED;
270+
};
271+
272+
TEST(Enums, ParseEnumWithConvertFromString_Issue948)
273+
{
274+
std::string xml_txt = R"(
275+
<root BTCPP_format="4" >
276+
<BehaviorTree ID="Main">
277+
<Sequence>
278+
<ActionWithNodeType name="test_action" type="Action"/>
279+
<ActionWithNodeType name="test_control" type="Control"/>
280+
</Sequence>
281+
</BehaviorTree>
282+
</root>)";
283+
284+
BehaviorTreeFactory factory;
285+
factory.registerNodeType<ActionWithNodeType>("ActionWithNodeType");
286+
// Deliberately NOT registering NodeType in ScriptingEnumsRegistry.
287+
// convertFromString<NodeType> exists and should be used as fallback.
288+
289+
auto tree = factory.createTreeFromText(xml_txt);
290+
NodeStatus status = tree.tickWhileRunning();
291+
ASSERT_EQ(status, NodeStatus::SUCCESS);
292+
293+
for(const auto& node : tree.subtrees.front()->nodes)
294+
{
295+
if(auto typed = dynamic_cast<ActionWithNodeType*>(node.get()))
296+
{
297+
if(typed->name() == "test_action")
298+
{
299+
ASSERT_EQ(NodeType::ACTION, typed->parsed_type);
300+
}
301+
else if(typed->name() == "test_control")
302+
{
303+
ASSERT_EQ(NodeType::CONTROL, typed->parsed_type);
304+
}
305+
}
306+
}
307+
}

0 commit comments

Comments
 (0)