1- # Custom initialization and/or construction
1+ # Pass additional arguments during initialization and/or construction
22
33In every single example we explored so far we were "forced" to provide a
44constructor with the following signature
@@ -11,21 +11,25 @@ constructor with the following signature
1111In same cases, it is desirable to pass to the constructor of our class
1212additional arguments, parameters, pointers, references, etc.
1313
14- We will just use the word _"parameter"_ for the rest of the tutorial.
14+ **Many people use blackboards to do that: this is not recomendable.**
1515
16- Even if, theoretically, these parameters can be passed using Input Ports,
16+ We will just use the word _"arguments"_ for the rest of the tutorial.
17+
18+ Even if, theoretically, these arguments **could** be passed using Input Ports,
1719that would be the wrong way to do it if:
1820
19- - The parameters are known at _deployment-time_.
20- - The parameters don't change at _run-time_.
21- - The parameters don't need to be from the _XML_.
21+ - The arguments are known at _deployment-time_.
22+ - The arguments don't change at _run-time_.
23+ - The arguments don't need to be set from the _XML_.
24+
25+ If all these conditions are met, using ports or the blackboard is cumbersome and highly discouraged.
2226
23- If all these conditions are met, using ports is just cumbersome and highly discouraged.
27+ ## Method 1: register a custom builder
2428
25- ## The C++ example
29+ Consider the following custom node called **Action_A**.
2630
27- Next, we can see two alternative ways to pass parameters to a class:
28- either as arguments of the constructor of the class or in an `init()` method .
31+ We want to pass three additional arguments; they can be arbitrarily complex objects,
32+ you are not limited to built- in types .
2933
3034```C++
3135// Action_A has a different constructor than the default one.
@@ -41,114 +45,106 @@ public:
4145 _arg2(arg2),
4246 _arg3(arg3) {}
4347
44- NodeStatus tick() override
45- {
46- std::cout << "Action_A: " << _arg1 << " / " << _arg2 << " / "
47- << _arg3 << std::endl;
48- return NodeStatus::SUCCESS;
49- }
5048 // this example doesn't require any port
5149 static PortsList providedPorts() { return {}; }
5250
51+ // tick() can access the private members
52+ NodeStatus tick() override;
53+
5354private:
5455 int _arg1;
5556 double _arg2;
5657 std::string _arg3;
5758};
59+ ```
60+
61+ This node should be registered as shown further:
62+
63+ ``` C++
64+ BehaviorTreeFactory factory;
65+
66+ // A node builder is a functor that creates a std::unique_ptr<TreeNode>.
67+ // Using lambdas or std::bind, we can easily "inject" additional arguments.
68+ NodeBuilder builder_A =
69+ [](const std::string& name, const NodeConfiguration& config)
70+ {
71+ return std::make_unique<Action_A>( name, config, 42, 3.14, "hello world" );
72+ };
73+
74+ // BehaviorTreeFactory::registerBuilder is a more general way to
75+ // register a custom node.
76+ factory.registerBuilder<Action_A>( " Action_A" , builder_A);
77+
78+ // Register more custom nodes, if needed.
79+ // ....
80+
81+ // The rest of your code, where you create and tick the tree, goes here.
82+ // ....
83+ ```
84+
85+ ## Method 2: use an init method
86+
87+ Alternatively, you may call an init method before ticking the tree.
88+
89+ ``` C++
5890
59- // Action_B implements an init(...) method that must be called once
60- // before the first tick()
6191class Action_B : public SyncActionNode
6292{
6393
6494public:
95+ // The constructor looks as usual.
6596 Action_B(const std::string& name, const NodeConfiguration& config):
6697 SyncActionNode(name, config) {}
6798
68- // we want this method to be called ONCE and BEFORE the first tick()
69- void init( int arg1, double arg2, std::string arg3 )
99+ // We want this method to be called ONCE and BEFORE the first tick()
100+ void init( int arg1, double arg2, const std::string& arg3 )
70101 {
71102 _arg1 = (arg1);
72103 _arg2 = (arg2);
73104 _arg3 = (arg3);
74105 }
75106
76- NodeStatus tick() override
77- {
78- std::cout << "Action_B: " << _arg1 << " / " << _arg2 << " / "
79- << _arg3 << std::endl;
80- return NodeStatus::SUCCESS;
81- }
82107 // this example doesn't require any port
83108 static PortsList providedPorts() { return {}; }
84109
110+ // tick() can access the private members
111+ NodeStatus tick() override;
112+
85113private:
86114 int _ arg1;
87115 double _ arg2;
88116 std::string _ arg3;
89117};
90118```
91119
92- The way we register and initialize them in our ` main ` is slightly different.
93-
120+ The way we register and initialize Action_B is slightly different:
94121
95122``` C++
96- static const char * xml_text = R"(
97-
98- <root >
99- <BehaviorTree>
100- <Sequence>
101- <Action_A/>
102- <Action_B/>
103- </Sequence>
104- </BehaviorTree>
105- </root>
106- )" ;
107-
108- int main ()
109- {
110- BehaviorTreeFactory factory;
111123
112- // A node builder is nothing more than a function pointer to create a
113- // std::unique_ptr<TreeNode>.
114- // Using lambdas or std::bind, we can easily "inject" additional arguments.
115- NodeBuilder builder_A =
116- [](const std::string& name, const NodeConfiguration& config)
117- {
118- return std::make_unique<Action_A>( name, config, 42, 3.14, "hello world" );
119- };
124+ BehaviorTreeFactory factory;
120125
121- // BehaviorTreeFactory::registerBuilder is a more general way to
122- // register a custom node.
123- factory.registerBuilder<Action_A >( "Action_A", builder_A );
126+ // The regitration of Action_B is done as usual, but remember
127+ // that we still need to call Action_B::init()
128+ factory.registerNodeType<Action_B >( " Action_B " );
124129
125- // The regitration of Action_B is done as usual, but remember
126- // that we still need to call Action_B::init()
127- factory.registerNodeType<Action_B>( "Action_B" );
130+ // Register more custom nodes, if needed.
131+ // ....
128132
129- auto tree = factory.createTreeFromText(xml_text);
133+ // Create the whole tree
134+ auto tree = factory.createTreeFromText(xml_text);
130135
131- // Iterate through all the nodes and call init() if it is an Action_B
132- for( auto& node: tree.nodes )
136+ // Iterate through all the nodes and call init() if it is an Action_B
137+ for ( auto & node: tree.nodes )
138+ {
139+ // Not a typo: it is "=", not "=="
140+ if( auto action_B = dynamic_cast<Action_B*>( node.get() ))
133141 {
134- if( auto action_B_node = dynamic_cast<Action_B*>( node.get() ))
135- {
136- action_B_node->init( 69, 9.99, "interesting_value" );
137- }
142+ action_B->init( 42, 3.14, "hello world");
138143 }
139-
140- tree.tickRoot();
141-
142- return 0;
143144}
144145
145-
146- /* Expected output:
147-
148- Action_A: 42 / 3.14 / hello world
149- Action_B: 69 / 9.99 / interesting_value
150- */
151-
146+ // The rest of your code, where you tick the tree, goes here.
147+ // ....
152148```
153149
154150
0 commit comments