Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7ebdc80

Browse files
committed
make easier to create ports at run-time
1 parent 0ab3895 commit 7ebdc80

File tree

3 files changed

+184
-67
lines changed

3 files changed

+184
-67
lines changed

examples/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,7 @@ endif()
4242

4343
add_executable(t10_include_trees t10_include_trees.cpp )
4444
target_link_libraries(t10_include_trees ${BEHAVIOR_TREE_LIBRARY} bt_sample_nodes )
45+
46+
47+
add_executable(t11_runtime_ports t11_runtime_ports.cpp )
48+
target_link_libraries(t11_runtime_ports ${BEHAVIOR_TREE_LIBRARY} bt_sample_nodes )

examples/t11_runtime_ports.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include "behaviortree_cpp_v3/bt_factory.h"
2+
3+
#include "dummy_nodes.h"
4+
#include "movebase_node.h"
5+
6+
using namespace BT;
7+
8+
// clang-format off
9+
static const char* xml_text = R"(
10+
<root main_tree_to_execute = "MainTree" >
11+
<BehaviorTree ID="MainTree">
12+
<Sequence name="root">
13+
<ThinkRuntimePort text="{the_answer}"/>
14+
<SayRuntimePort message="{the_answer}" />
15+
</Sequence>
16+
</BehaviorTree>
17+
</root>
18+
)";
19+
// clang-format on
20+
21+
class ThinkRuntimePort: public BT::SyncActionNode
22+
{
23+
public:
24+
ThinkRuntimePort(const std::string& name,
25+
const BT::NodeConfiguration& config)
26+
: BT::SyncActionNode(name, config)
27+
{
28+
}
29+
30+
BT::NodeStatus tick() override {
31+
setOutput("text", "The answer is 42" );
32+
return NodeStatus::SUCCESS;
33+
}
34+
};
35+
36+
class SayRuntimePort : public BT::SyncActionNode
37+
{
38+
public:
39+
SayRuntimePort(const std::string& name, const BT::NodeConfiguration& config)
40+
: BT::SyncActionNode(name, config)
41+
{
42+
}
43+
44+
// You must override the virtual function tick()
45+
BT::NodeStatus tick() override
46+
{
47+
auto msg = getInput<std::string>("message");
48+
if (!msg){
49+
throw BT::RuntimeError( "missing required input [message]: ", msg.error() );
50+
}
51+
std::cout << "Robot says: " << msg.value() << std::endl;
52+
return BT::NodeStatus::SUCCESS;
53+
}
54+
};
55+
56+
57+
int main()
58+
{
59+
using namespace DummyNodes;
60+
61+
BehaviorTreeFactory factory;
62+
63+
//-------- register ports that might defined at runtime --------
64+
{
65+
// more verbose way
66+
PortsList ports = {BT::OutputPort<std::string>("text")};
67+
factory.registerBuilder(CreateManifest<ThinkRuntimePort>("ThinkRuntimePort", ports),
68+
CreateBuilder<ThinkRuntimePort>());
69+
}
70+
71+
{
72+
// less verbose way
73+
PortsList ports = {BT::InputPort<std::string>("message")};
74+
factory.registerNodeType<SayRuntimePort>("SayRuntimePort", ports);
75+
}
76+
77+
auto tree = factory.createTreeFromText(xml_text);
78+
79+
tree.root_node->executeTick();
80+
81+
return 0;
82+
}
83+
84+

include/behaviortree_cpp_v3/bt_factory.h

Lines changed: 96 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,58 @@ namespace BT
3232
typedef std::function<std::unique_ptr<TreeNode>(const std::string&, const NodeConfiguration&)>
3333
NodeBuilder;
3434

35+
template <typename T>
36+
using has_default_constructor = typename std::is_constructible<T, const std::string&>;
37+
38+
template <typename T>
39+
using has_params_constructor = typename std::is_constructible<T, const std::string&, const NodeConfiguration&>;
40+
41+
42+
template <typename T> inline
43+
NodeBuilder CreateBuilder(typename std::enable_if<has_default_constructor<T>::value &&
44+
has_params_constructor<T>::value >::type* = nullptr)
45+
{
46+
return [](const std::string& name, const NodeConfiguration& config)
47+
{
48+
// Special case. Use default constructor if parameters are empty
49+
if( config.input_ports.empty() &&
50+
config.output_ports.empty() &&
51+
has_default_constructor<T>::value)
52+
{
53+
return std::make_unique<T>(name);
54+
}
55+
return std::make_unique<T>(name, config);
56+
};
57+
}
58+
59+
template <typename T> inline
60+
NodeBuilder CreateBuilder(typename std::enable_if<!has_default_constructor<T>::value &&
61+
has_params_constructor<T>::value >::type* = nullptr)
62+
{
63+
return [](const std::string& name, const NodeConfiguration& params)
64+
{
65+
return std::unique_ptr<TreeNode>(new T(name, params));
66+
};
67+
}
68+
69+
template <typename T> inline
70+
NodeBuilder CreateBuilder(typename std::enable_if<has_default_constructor<T>::value &&
71+
!has_params_constructor<T>::value >::type* = nullptr)
72+
{
73+
return [](const std::string& name, const NodeConfiguration&)
74+
{
75+
return std::unique_ptr<TreeNode>(new T(name));
76+
};
77+
}
78+
79+
80+
template <typename T> inline
81+
TreeNodeManifest CreateManifest(const std::string& ID, PortsList portlist = getProvidedPorts<T>())
82+
{
83+
return { getType<T>(), ID, portlist };
84+
}
85+
86+
3587
constexpr const char* PLUGIN_SYMBOL = "BT_RegisterNodesFromPlugin";
3688

3789
#ifndef BT_PLUGIN_EXPORT
@@ -128,7 +180,7 @@ class BehaviorTreeFactory
128180
template <typename T>
129181
void registerBuilder(const std::string& ID, const NodeBuilder& builder )
130182
{
131-
auto manifest = BehaviorTreeFactory::buildManifest<T>(ID);
183+
auto manifest = CreateManifest<T>(ID);
132184
registerBuilder(manifest, builder);
133185
}
134186

@@ -196,11 +248,11 @@ class BehaviorTreeFactory
196248
std::is_base_of<ControlNode, T>::value ||
197249
std::is_base_of<DecoratorNode, T>::value ||
198250
std::is_base_of<ConditionNode, T>::value,
199-
"[registerBuilder]: accepts only classed derived from either ActionNodeBase, "
251+
"[registerNode]: accepts only classed derived from either ActionNodeBase, "
200252
"DecoratorNode, ControlNode or ConditionNode");
201253

202254
static_assert(!std::is_abstract<T>::value,
203-
"[registerBuilder]: Some methods are pure virtual. "
255+
"[registerNode]: Some methods are pure virtual. "
204256
"Did you override the methods tick() and halt()?");
205257

206258
constexpr bool default_constructable = std::is_constructible<T, const std::string&>::value;
@@ -210,20 +262,56 @@ class BehaviorTreeFactory
210262
has_static_method_providedPorts<T>::value;
211263

212264
static_assert(default_constructable || param_constructable,
213-
"[registerBuilder]: the registered class must have at least one of these two "
265+
"[registerNode]: the registered class must have at least one of these two "
214266
"constructors: "
215267
" (const std::string&, const NodeConfiguration&) or (const std::string&).");
216268

217269
static_assert(!(param_constructable && !has_static_ports_list),
218-
"[registerBuilder]: you MUST implement the static method: "
270+
"[registerNode]: you MUST implement the static method: "
219271
" PortsList providedPorts();\n");
220272

221273
static_assert(!(has_static_ports_list && !param_constructable),
222-
"[registerBuilder]: since you have a static method requiredNodeParameters(), "
274+
"[registerNode]: since you have a static method providedPorts(), "
223275
"you MUST add a constructor sign signature (const std::string&, const "
224276
"NodeParameters&)\n");
225277

226-
registerNodeTypeImpl<T>(ID);
278+
registerBuilder( CreateManifest<T>(ID), CreateBuilder<T>());
279+
}
280+
281+
template <typename T>
282+
void registerNodeType(const std::string& ID, PortsList ports)
283+
{
284+
static_assert(std::is_base_of<ActionNodeBase, T>::value ||
285+
std::is_base_of<ControlNode, T>::value ||
286+
std::is_base_of<DecoratorNode, T>::value ||
287+
std::is_base_of<ConditionNode, T>::value,
288+
"[registerNode]: accepts only classed derived from either ActionNodeBase, "
289+
"DecoratorNode, ControlNode or ConditionNode");
290+
291+
static_assert(!std::is_abstract<T>::value,
292+
"[registerNode]: Some methods are pure virtual. "
293+
"Did you override the methods tick() and halt()?");
294+
295+
constexpr bool default_constructable = std::is_constructible<T, const std::string&>::value;
296+
constexpr bool param_constructable =
297+
std::is_constructible<T, const std::string&, const NodeConfiguration&>::value;
298+
constexpr bool has_static_ports_list =
299+
has_static_method_providedPorts<T>::value;
300+
301+
static_assert(default_constructable || param_constructable,
302+
"[registerNode]: the registered class must have at least one of these two "
303+
"constructors: (const std::string&, const NodeConfiguration&) or (const std::string&).");
304+
305+
static_assert(!has_static_ports_list,
306+
"[registerNode]: ports are passed to this node explicitly. The static method"
307+
"providedPorts() should be removed to avoid ambiguities\n");
308+
309+
static_assert(param_constructable,
310+
"[registerNode]: since this node has ports, "
311+
"you MUST add a constructor sign signature (const std::string&, const "
312+
"NodeParameters&)\n");
313+
314+
registerBuilder( CreateManifest<T>(ID, ports), CreateBuilder<T>());
227315
}
228316

229317
/// All the builders. Made available mostly for debug purposes.
@@ -241,74 +329,15 @@ class BehaviorTreeFactory
241329
Tree createTreeFromFile(const std::string& file_path,
242330
Blackboard::Ptr blackboard = Blackboard::create());
243331

244-
template <typename T> static
245-
TreeNodeManifest buildManifest(const std::string& ID)
246-
{
247-
return { getType<T>(), ID, getProvidedPorts<T>() };
248-
}
249-
250332
private:
251333
std::unordered_map<std::string, NodeBuilder> builders_;
252334
std::unordered_map<std::string, TreeNodeManifest> manifests_;
253335
std::set<std::string> builtin_IDs_;
254336

255-
// template specialization = SFINAE + black magic
256-
257-
// clang-format off
258-
template <typename T>
259-
using has_default_constructor = typename std::is_constructible<T, const std::string&>;
260-
261-
template <typename T>
262-
using has_params_constructor = typename std::is_constructible<T, const std::string&, const NodeConfiguration&>;
263-
264-
template <typename T>
265-
void registerNodeTypeImpl(const std::string& ID)
266-
{
267-
NodeBuilder builder = getBuilder<T>();
268-
registerBuilder( buildManifest<T>(ID), builder);
269-
}
270-
271-
template <typename T> static
272-
NodeBuilder getBuilder(typename std::enable_if<has_default_constructor<T>::value &&
273-
has_params_constructor<T>::value >::type* = nullptr)
274-
{
275-
return [](const std::string& name, const NodeConfiguration& config)
276-
{
277-
//TODO FIXME
278-
279-
// Special case. Use default constructor if parameters are empty
280-
if( config.input_ports.empty() &&
281-
config.output_ports.empty() &&
282-
has_default_constructor<T>::value)
283-
{
284-
return std::make_unique<T>(name);
285-
}
286-
return std::make_unique<T>(name, config);
287-
};
288-
}
289-
290-
template <typename T> static
291-
NodeBuilder getBuilder(typename std::enable_if<!has_default_constructor<T>::value &&
292-
has_params_constructor<T>::value >::type* = nullptr)
293-
{
294-
return [](const std::string& name, const NodeConfiguration& params)
295-
{
296-
return std::unique_ptr<TreeNode>(new T(name, params));
297-
};
298-
}
299-
300-
template <typename T> static
301-
NodeBuilder getBuilder(typename std::enable_if<has_default_constructor<T>::value &&
302-
!has_params_constructor<T>::value >::type* = nullptr)
303-
{
304-
return [](const std::string& name, const NodeConfiguration&)
305-
{
306-
return std::unique_ptr<TreeNode>(new T(name));
307-
};
308-
}
309337
// clang-format on
310338
};
311339

340+
312341
} // end namespace
313342

314343
#endif // BT_FACTORY_H

0 commit comments

Comments
 (0)