Skip to content

Commit 2ac047b

Browse files
Merge pull request #671 from BehaviorTree/4.4
4.4
2 parents 9f02535 + 3f63a6a commit 2ac047b

15 files changed

+834
-446
lines changed

examples/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ endif()
3636
CompileExample("ex01_wrap_legacy")
3737
CompileExample("ex02_runtime_ports")
3838
CompileExample("ex04_waypoints")
39+
CompileExample("ex05_subtree_model")
3940

4041
CompileExample("t13_plugin_executor")
4142

examples/ex05_subtree_model.cpp

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#include "behaviortree_cpp/bt_factory.h"
2+
#include "behaviortree_cpp/loggers/bt_cout_logger.h"
3+
4+
using namespace BT;
5+
6+
/*
7+
* Demonstrate how to use a SubTree Model (since version 4.4)
8+
*/
9+
10+
/**
11+
* You can optionally add a model to a SubTrees, in this case, "MySub".
12+
* We are telling the factory that the callee should remap
13+
* two mandatory inputs, and two outputs:
14+
*
15+
* - sub_in_value (that has the default value 42)
16+
* - sub_in_name (no default)
17+
* - sub_out_result (default remapping to port {output})
18+
* - sub_out_state (no default)
19+
*
20+
* The callee (parent tree, including the subtree) MUST specify those
21+
* remapping which have no default value.
22+
*/
23+
24+
// clang-format off
25+
static const char* xml_subtree = R"(
26+
<root BTCPP_format="4">
27+
28+
<TreeNodesModel>
29+
<SubTree ID="MySub">
30+
<input_port name="sub_in_value" default="42"/>
31+
<input_port name="sub_in_name"/>
32+
<output_port name="sub_out_result" default="{out_result}"/>
33+
<output_port name="sub_out_state"/>
34+
</SubTree>
35+
</TreeNodesModel>
36+
37+
<BehaviorTree ID="MySub">
38+
<Sequence>
39+
<ScriptCondition code="sub_in_value==42 && sub_in_name=='john'" />
40+
<Script code="sub_out_result:=69; sub_out_state:='ACTIVE'" />
41+
</Sequence>
42+
</BehaviorTree>
43+
</root>
44+
)";
45+
46+
/**
47+
* Here, when calling "MySub", only `sub_in_name` and `sub_out_state` are explicitly
48+
* remapped. We will use the default values for the other two.
49+
*/
50+
51+
static const char* xml_maintree = R"(
52+
<root BTCPP_format="4">
53+
54+
<BehaviorTree ID="MainTree">
55+
<Sequence>
56+
<Script code="in_name:= 'john' "/>
57+
<SubTree ID="MySub" sub_in_name="{in_name}"
58+
sub_out_state="{out_state}"/>
59+
<ScriptCondition code=" out_result==69 && out_state=='ACTIVE' " />
60+
</Sequence>
61+
</BehaviorTree>
62+
63+
</root>
64+
)";
65+
66+
// clang-format on
67+
68+
int main()
69+
{
70+
BehaviorTreeFactory factory;
71+
factory.registerBehaviorTreeFromText(xml_subtree);
72+
factory.registerBehaviorTreeFromText(xml_maintree);
73+
74+
auto tree = factory.createTree("MainTree");
75+
StdCoutLogger logger(tree);
76+
tree.tickWhileRunning();
77+
78+
// We expect the sequence to be successful.
79+
80+
// The full remapping was:
81+
//
82+
// - sub_in_name <-> {in_name} // specified by callee 'MainTree'
83+
// - sub_in_value <-> 42 // default remapping, see model
84+
// - sub_out_result <-> {out_result} // default remapping, see model
85+
// - sub_out_state <-> {out_state} // specified by callee 'MainTree'
86+
87+
return 0;
88+
}

include/behaviortree_cpp/basic_types.h

+58-34
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,16 @@ inline StringConverter GetAnyFromStringFunctor<void>()
158158
template<typename T> [[nodiscard]]
159159
std::string toStr(const T& value)
160160
{
161-
if constexpr(!std::is_arithmetic_v<T>)
161+
if constexpr (std::is_convertible_v<T, std::string> ||
162+
std::is_convertible_v<T, std::string_view>)
163+
{
164+
return value;
165+
}
166+
else if constexpr(!std::is_arithmetic_v<T>)
162167
{
163168
throw LogicError(
164169
StrCat("Function BT::toStr<T>() not specialized for type [",
165-
BT::demangle(typeid(T)), "],",
166-
"Implement it consistently with BT::convertFromString<T>(), "
167-
"or provide at dummy version that returns an empty string.")
170+
BT::demangle(typeid(T)), "]")
168171
);
169172
} else {
170173
return std::to_string(value);
@@ -255,25 +258,27 @@ using Result = Expected<std::monostate>;
255258
[[nodiscard]]
256259
bool IsAllowedPortName(StringView str);
257260

258-
class PortInfo
261+
struct AnyTypeAllowed
262+
{};
263+
264+
class TypeInfo
259265
{
260266
public:
261-
struct AnyTypeAllowed
262-
{
263-
};
264267

265-
PortInfo(PortDirection direction = PortDirection::INOUT) :
266-
type_(direction), type_info_(typeid(AnyTypeAllowed)),
268+
template <typename T>
269+
static TypeInfo Create() {
270+
return TypeInfo{typeid(T), GetAnyFromStringFunctor<T>()};
271+
}
272+
273+
TypeInfo(): type_info_(typeid(AnyTypeAllowed)),
267274
type_str_("AnyTypeAllowed")
268275
{}
269276

270-
PortInfo(PortDirection direction, std::type_index type_info, StringConverter conv) :
271-
type_(direction), type_info_(type_info), converter_(conv),
277+
TypeInfo(std::type_index type_info, StringConverter conv) :
278+
type_info_(type_info), converter_(conv),
272279
type_str_(BT::demangle(type_info))
273280
{}
274281

275-
[[nodiscard]] PortDirection direction() const;
276-
277282
[[nodiscard]] const std::type_index& type() const;
278283

279284
[[nodiscard]] const std::string& typeName() const;
@@ -289,6 +294,38 @@ class PortInfo
289294
return {};
290295
}
291296

297+
[[nodiscard]] bool isStronglyTyped() const
298+
{
299+
return type_info_ != typeid(AnyTypeAllowed);
300+
}
301+
302+
[[nodiscard]] const StringConverter& converter() const
303+
{
304+
return converter_;
305+
}
306+
307+
private:
308+
309+
std::type_index type_info_;
310+
StringConverter converter_;
311+
std::string type_str_;
312+
};
313+
314+
315+
class PortInfo: public TypeInfo
316+
{
317+
public:
318+
319+
PortInfo(PortDirection direction = PortDirection::INOUT) :
320+
TypeInfo(), direction_(direction)
321+
{}
322+
323+
PortInfo(PortDirection direction, std::type_index type_info, StringConverter conv) :
324+
TypeInfo(type_info, conv), direction_(direction)
325+
{}
326+
327+
[[nodiscard]] PortDirection direction() const;
328+
292329
void setDescription(StringView description);
293330

294331
template <typename T>
@@ -306,27 +343,14 @@ class PortInfo
306343

307344
[[nodiscard]] const std::string& defaultValueString() const;
308345

309-
[[nodiscard]] bool isStronglyTyped() const
310-
{
311-
return type_info_ != typeid(AnyTypeAllowed);
312-
}
313-
314-
[[nodiscard]] const StringConverter& converter() const
315-
{
316-
return converter_;
317-
}
318-
319346
private:
320-
PortDirection type_;
321-
std::type_index type_info_;
322-
StringConverter converter_;
347+
PortDirection direction_;
323348
std::string description_;
324349
Any default_value_;
325350
std::string default_value_str_;
326-
std::string type_str_;
327351
};
328352

329-
template <typename T = PortInfo::AnyTypeAllowed> [[nodiscard]]
353+
template <typename T = AnyTypeAllowed> [[nodiscard]]
330354
std::pair<std::string, PortInfo> CreatePort(PortDirection direction,
331355
StringView name,
332356
StringView description = {})
@@ -357,28 +381,28 @@ std::pair<std::string, PortInfo> CreatePort(PortDirection direction,
357381
}
358382

359383
//----------
360-
template <typename T = PortInfo::AnyTypeAllowed> [[nodiscard]]
384+
template <typename T = AnyTypeAllowed> [[nodiscard]]
361385
inline std::pair<std::string, PortInfo> InputPort(StringView name,
362386
StringView description = {})
363387
{
364388
return CreatePort<T>(PortDirection::INPUT, name, description);
365389
}
366390

367-
template <typename T = PortInfo::AnyTypeAllowed> [[nodiscard]]
391+
template <typename T = AnyTypeAllowed> [[nodiscard]]
368392
inline std::pair<std::string, PortInfo> OutputPort(StringView name,
369393
StringView description = {})
370394
{
371395
return CreatePort<T>(PortDirection::OUTPUT, name, description);
372396
}
373397

374-
template <typename T = PortInfo::AnyTypeAllowed> [[nodiscard]]
398+
template <typename T = AnyTypeAllowed> [[nodiscard]]
375399
inline std::pair<std::string, PortInfo> BidirectionalPort(StringView name,
376400
StringView description = {})
377401
{
378402
return CreatePort<T>(PortDirection::INOUT, name, description);
379403
}
380404
//----------
381-
template <typename T = PortInfo::AnyTypeAllowed> [[nodiscard]]
405+
template <typename T = AnyTypeAllowed> [[nodiscard]]
382406
inline std::pair<std::string, PortInfo> InputPort(StringView name, const T& default_value,
383407
StringView description)
384408
{
@@ -387,7 +411,7 @@ inline std::pair<std::string, PortInfo> InputPort(StringView name, const T& defa
387411
return out;
388412
}
389413

390-
template <typename T = PortInfo::AnyTypeAllowed> [[nodiscard]]
414+
template <typename T = AnyTypeAllowed> [[nodiscard]]
391415
inline std::pair<std::string, PortInfo> BidirectionalPort(StringView name,
392416
const T& default_value,
393417
StringView description)

include/behaviortree_cpp/blackboard.h

+14-19
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,11 @@ class Blackboard
4040
struct Entry
4141
{
4242
Any value;
43-
PortInfo port_info;
43+
TypeInfo info;
44+
StringConverter string_converter;
4445
mutable std::mutex entry_mutex;
45-
46-
Entry(const PortInfo& info) : port_info(info)
47-
{}
48-
49-
Entry(Any&& other_any, const PortInfo& info) :
50-
value(std::move(other_any)), port_info(info)
46+
47+
Entry(const TypeInfo& _info) : info(_info)
5148
{}
5249
};
5350

@@ -149,21 +146,19 @@ class Blackboard
149146
std::scoped_lock lock(entry.entry_mutex);
150147

151148
Any& previous_any = entry.value;
152-
const PortInfo& port_info = entry.port_info;
153149

154150
Any new_value(value);
155151

156152
// special case: entry exists but it is not strongly typed... yet
157-
if (!port_info.isStronglyTyped())
153+
if (!entry.info.isStronglyTyped())
158154
{
159155
// Use the new type to create a new entry that is strongly typed.
160-
entry.port_info =
161-
PortInfo(port_info.direction(), new_value.type(), port_info.converter());
156+
entry.info = TypeInfo::Create<T>();
162157
previous_any = std::move(new_value);
163158
return;
164159
}
165160

166-
std::type_index previous_type = port_info.type();
161+
std::type_index previous_type = entry.info.type();
167162

168163
// check type mismatch
169164
if (previous_type != std::type_index(typeid(T)) &&
@@ -172,7 +167,7 @@ class Blackboard
172167
bool mismatching = true;
173168
if (std::is_constructible<StringView, T>::value)
174169
{
175-
Any any_from_string = port_info.parseString(value);
170+
Any any_from_string = entry.info.parseString(value);
176171
if (any_from_string.empty() == false)
177172
{
178173
mismatching = false;
@@ -204,8 +199,8 @@ class Blackboard
204199
new_value.copyInto(previous_any);
205200
}
206201
}
207-
208-
[[nodiscard]] const PortInfo* portInfo(const std::string& key);
202+
203+
[[nodiscard]] const TypeInfo* entryInfo(const std::string& key);
209204

210205
void addSubtreeRemapping(StringView internal, StringView external);
211206

@@ -217,17 +212,17 @@ class Blackboard
217212

218213
[[deprecated("Use getAnyLocked to access safely an Entry")]]
219214
std::recursive_mutex& entryMutex() const;
220-
221-
void createEntry(const std::string& key, const PortInfo& info);
215+
216+
void createEntry(const std::string& key, const TypeInfo& info);
222217

223218
private:
224219
mutable std::mutex mutex_;
225220
mutable std::recursive_mutex entry_mutex_;
226221
std::unordered_map<std::string, std::shared_ptr<Entry>> storage_;
227222
std::weak_ptr<Blackboard> parent_bb_;
228223
std::unordered_map<std::string, std::string> internal_to_external_;
229-
230-
std::shared_ptr<Entry> createEntryImpl(const std::string &key, const PortInfo& info);
224+
225+
std::shared_ptr<Entry> createEntryImpl(const std::string &key, const TypeInfo& info);
231226

232227
bool autoremapping_ = false;
233228
};

include/behaviortree_cpp/scripting/operators.hpp

+13-3
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,16 @@ struct ExprComparison : ExprBase
380380
return False;
381381
}
382382
}
383+
else if ((lhs_v.isString() && rhs_v.isNumber()) ||
384+
(lhs_v.isNumber() && rhs_v.isString()))
385+
{
386+
auto lv = lhs_v.cast<double>();
387+
auto rv = lhs_v.cast<double>();
388+
if (!SwitchImpl(lv, rv, ops[i]))
389+
{
390+
return False;
391+
}
392+
}
383393
else
384394
{
385395
throw RuntimeError(StrCat("Can't mix different types in Comparison. "
@@ -504,7 +514,7 @@ struct ExprAssignment : ExprBase
504514
{
505515
// the very fist assignment can come from any type.
506516
// In the future, type check will be done by Any::copyInto
507-
if (dst_ptr->empty() && entry->port_info.type() == typeid(PortInfo::AnyTypeAllowed))
517+
if (dst_ptr->empty() && entry->info.type() == typeid(AnyTypeAllowed))
508518
{
509519
*dst_ptr = value;
510520
}
@@ -513,8 +523,8 @@ struct ExprAssignment : ExprBase
513523
// special case: string to other type.
514524
// Check if we can use the StringConverter
515525
auto const str = value.cast<std::string>();
516-
const auto& port_info = env.vars->portInfo(key);
517-
if (auto converter = port_info->converter())
526+
const auto& entry_info = env.vars->entryInfo(key);
527+
if (auto converter = entry_info->converter())
518528
{
519529
*dst_ptr = converter(str);
520530
}

0 commit comments

Comments
 (0)