Skip to content

Commit cdf8c14

Browse files
committed
improved support for default values
1 parent b367c70 commit cdf8c14

File tree

6 files changed

+162
-52
lines changed

6 files changed

+162
-52
lines changed

include/behaviortree_cpp/basic_types.h

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,9 @@ NodeType convertFromString<NodeType>(StringView str);
130130
template <> [[nodiscard]]
131131
PortDirection convertFromString<PortDirection>(StringView str);
132132

133-
typedef std::function<Any(StringView)> StringConverter;
133+
using StringConverter = std::function<Any(StringView)>;
134134

135-
typedef std::unordered_map<const std::type_info*, StringConverter> StringConvertersMap;
135+
using StringConvertersMap = std::unordered_map<const std::type_info*, StringConverter>;
136136

137137
// helper function
138138
template <typename T> [[nodiscard]]
@@ -152,10 +152,17 @@ inline StringConverter GetAnyFromStringFunctor<void>()
152152
template<typename T> [[nodiscard]]
153153
std::string toStr(const T& value)
154154
{
155-
static_assert(std::is_arithmetic_v<T>,
156-
"You need a template specialization of BT::toStr() and "
157-
"it must be consistent with the implementation of BT::convertFromString");
158-
return std::to_string(value);
155+
if constexpr(!std::is_arithmetic_v<T>)
156+
{
157+
throw LogicError(
158+
StrCat("Function BT::toStr<T>() not specialized for type [",
159+
BT::demangle(typeid(T)), "],",
160+
"Implement it consistently with BT::convertFromString<T>(), "
161+
"or provide at dummy version that returns an empty string.")
162+
);
163+
} else {
164+
return std::to_string(value);
165+
}
159166
}
160167

161168
template <> [[nodiscard]]
@@ -250,11 +257,11 @@ class PortInfo
250257
};
251258

252259
PortInfo(PortDirection direction = PortDirection::INOUT) :
253-
_type(direction), _type_info(typeid(AnyTypeAllowed))
260+
type_(direction), type_info_(typeid(AnyTypeAllowed))
254261
{}
255262

256263
PortInfo(PortDirection direction, std::type_index type_info, StringConverter conv) :
257-
_type(direction), _type_info(type_info), _converter(conv)
264+
type_(direction), type_info_(type_info), converter_(conv)
258265
{}
259266

260267
[[nodiscard]] PortDirection direction() const;
@@ -274,28 +281,38 @@ class PortInfo
274281

275282
void setDescription(StringView description);
276283

277-
void setDefaultValue(StringView default_value_as_string);
284+
template <typename T>
285+
void setDefaultValue(const T& default_value) {
286+
default_value_ = Any(default_value);
287+
try{
288+
default_value_str_ = BT::toStr(default_value);
289+
}
290+
catch(LogicError&) {}
291+
}
278292

279293
[[nodiscard]] const std::string& description() const;
280294

281-
[[nodiscard]] std::optional<std::string> defaultValue() const;
295+
[[nodiscard]] const Any& defaultValue() const;
296+
297+
[[nodiscard]] const std::string& defaultValueString() const;
282298

283299
[[nodiscard]] bool isStronglyTyped() const
284300
{
285-
return _type_info != typeid(AnyTypeAllowed);
301+
return type_info_ != typeid(AnyTypeAllowed);
286302
}
287303

288304
[[nodiscard]] const StringConverter& converter() const
289305
{
290-
return _converter;
306+
return converter_;
291307
}
292308

293309
private:
294-
PortDirection _type;
295-
std::type_index _type_info;
296-
StringConverter _converter;
310+
PortDirection type_;
311+
std::type_index type_info_;
312+
StringConverter converter_;
297313
std::string description_;
298-
std::optional<std::string> default_value_;
314+
Any default_value_;
315+
std::string default_value_str_;
299316
};
300317

301318
template <typename T = PortInfo::AnyTypeAllowed> [[nodiscard]]
@@ -355,7 +372,7 @@ inline std::pair<std::string, PortInfo> InputPort(StringView name, const T& defa
355372
StringView description)
356373
{
357374
auto out = CreatePort<T>(PortDirection::INPUT, name, description);
358-
out.second.setDefaultValue(BT::toStr(default_value));
375+
out.second.setDefaultValue(default_value);
359376
return out;
360377
}
361378

@@ -365,12 +382,12 @@ inline std::pair<std::string, PortInfo> BidirectionalPort(StringView name,
365382
StringView description)
366383
{
367384
auto out = CreatePort<T>(PortDirection::INOUT, name, description);
368-
out.second.setDefaultValue(BT::toStr(default_value));
385+
out.second.setDefaultValue(default_value);
369386
return out;
370387
}
371388
//----------
372389

373-
typedef std::unordered_map<std::string, PortInfo> PortsList;
390+
using PortsList = std::unordered_map<std::string, PortInfo>;
374391

375392
template <typename T, typename = void>
376393
struct has_static_method_providedPorts : std::false_type
@@ -398,8 +415,8 @@ inline PortsList
398415
return {};
399416
}
400417

401-
typedef std::chrono::high_resolution_clock::time_point TimePoint;
402-
typedef std::chrono::high_resolution_clock::duration Duration;
418+
using TimePoint = std::chrono::high_resolution_clock::time_point;
419+
using Duration = std::chrono::high_resolution_clock::duration;
403420

404421
} // namespace BT
405422

include/behaviortree_cpp/tree_node.h

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ struct TreeNodeManifest
4141
std::string description;
4242
};
4343

44-
typedef std::unordered_map<std::string, std::string> PortsRemapping;
44+
using PortsRemapping = std::unordered_map<std::string, std::string>;
4545

4646
enum class PreCond
4747
{
@@ -85,6 +85,8 @@ struct NodeConfig
8585
// output ports
8686
PortsRemapping output_ports;
8787

88+
const TreeNodeManifest* manifest = nullptr;
89+
8890
// Numberic unique identifier
8991
uint16_t uid = 0;
9092
// Unique human-readable name, that encapsulate the subtree
@@ -339,7 +341,7 @@ class TreeNode
339341
/**
340342
* @brief setStatus changes the status of the node.
341343
* it will throw if you try to change the status to IDLE, because
342-
* your parent node should do that, not the user!.
344+
* your parent node should do that, not the user!
343345
*/
344346
void setStatus(NodeStatus new_status);
345347

@@ -410,13 +412,30 @@ inline Result TreeNode::getInput(const std::string& key, T& destination) const
410412
"does not contain the key: [",
411413
key, "]"));
412414
}
413-
auto remapped_res = getRemappedKey(key, remap_it->second);
415+
416+
// special case. Empty port value, we should use the default value,
417+
// if available in the model.
418+
// BUT, it the port type is a string, then an empty string might be
419+
// a valid value
420+
const std::string& port_value_str = remap_it->second;
421+
if(port_value_str.empty() && config_.manifest)
422+
{
423+
const auto& port_manifest = config_.manifest->ports.at(key);
424+
const auto& default_value = port_manifest.defaultValue();
425+
if(!default_value.empty() && !default_value.isString())
426+
{
427+
destination = default_value.cast<T>();
428+
return {};
429+
}
430+
}
431+
432+
auto remapped_res = getRemappedKey(key, port_value_str);
414433
try
415434
{
416435
// pure string, not a blackboard key
417436
if (!remapped_res)
418437
{
419-
destination = ParseString(remap_it->second);
438+
destination = ParseString(port_value_str);
420439
return {};
421440
}
422441
const auto& remapped_key = remapped_res.value();

src/basic_types.cpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -324,28 +324,28 @@ std::vector<StringView> splitString(const StringView& strToSplit, char delimeter
324324

325325
PortDirection PortInfo::direction() const
326326
{
327-
return _type;
327+
return type_;
328328
}
329329

330330
const std::type_index& PortInfo::type() const
331331
{
332-
return _type_info;
332+
return type_info_;
333333
}
334334

335335
Any PortInfo::parseString(const char* str) const
336336
{
337-
if (_converter)
337+
if (converter_)
338338
{
339-
return _converter(str);
339+
return converter_(str);
340340
}
341341
return {};
342342
}
343343

344344
Any PortInfo::parseString(const std::string& str) const
345345
{
346-
if (_converter)
346+
if (converter_)
347347
{
348-
return _converter(str);
348+
return converter_(str);
349349
}
350350
return {};
351351
}
@@ -355,21 +355,21 @@ void PortInfo::setDescription(StringView description)
355355
description_ = static_cast<std::string>(description);
356356
}
357357

358-
void PortInfo::setDefaultValue(StringView default_value_as_string)
359-
{
360-
default_value_ = static_cast<std::string>(default_value_as_string);
361-
}
362-
363358
const std::string& PortInfo::description() const
364359
{
365360
return description_;
366361
}
367362

368-
std::optional<std::string> PortInfo::defaultValue() const
363+
const Any& PortInfo::defaultValue() const
369364
{
370365
return default_value_;
371366
}
372367

368+
const std::string& PortInfo::defaultValueString() const
369+
{
370+
return default_value_str_;
371+
}
372+
373373
bool IsAllowedPortName(StringView str)
374374
{
375375
if( str == "_autoremap")

src/decorators/subtree_node.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ BT::PortsList BT::SubTreeNode::providedPorts()
1111
{
1212
auto port = PortInfo(PortDirection::INPUT, typeid(bool),
1313
GetAnyFromStringFunctor<bool>());
14-
port.setDefaultValue("false");
14+
port.setDefaultValue(false);
1515
port.setDescription("If true, all the ports with the same name "
1616
"will be remapped");
1717

src/xml_parsing.cpp

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,14 @@ TreeNode::Ptr XMLParser::Pimpl::createNodeFromXML(const XMLElement* element,
529529
const char* attr_name = element->Attribute("name");
530530
std::string instance_name = attr_name ? attr_name : type_ID;
531531

532+
const TreeNodeManifest* manifest = nullptr;
533+
534+
auto manifest_it = factory.manifests().find(type_ID);
535+
if(manifest_it != factory.manifests().end())
536+
{
537+
manifest = &manifest_it->second;
538+
}
539+
532540
PortsRemapping port_remap;
533541
for (const XMLAttribute* att = element->FirstAttribute(); att; att = att->Next())
534542
{
@@ -543,6 +551,7 @@ TreeNode::Ptr XMLParser::Pimpl::createNodeFromXML(const XMLElement* element,
543551
config.blackboard = blackboard;
544552
config.path = prefix_path + instance_name;
545553
config.uid = output_tree.getUID();
554+
config.manifest = manifest;
546555

547556
if(type_ID == instance_name)
548557
{
@@ -581,12 +590,15 @@ TreeNode::Ptr XMLParser::Pimpl::createNodeFromXML(const XMLElement* element,
581590
}
582591
else
583592
{
584-
const auto& manifest = factory.manifests().at(type_ID);
593+
if(!manifest)
594+
{
595+
throw RuntimeError("Missing manifest. It shouldn't happen. Please report this issue");
596+
}
585597

586598
//Check that name in remapping can be found in the manifest
587599
for (const auto& remap_it : port_remap)
588600
{
589-
if (manifest.ports.count(remap_it.first) == 0)
601+
if (manifest->ports.count(remap_it.first) == 0)
590602
{
591603
throw RuntimeError("Possible typo? In the XML, you tried to remap port \"",
592604
remap_it.first, "\" in node [", type_ID, " / ", instance_name,
@@ -596,7 +608,7 @@ TreeNode::Ptr XMLParser::Pimpl::createNodeFromXML(const XMLElement* element,
596608
}
597609

598610
// Initialize the ports in the BB to set the type
599-
for (const auto& [port_name, port_info] : manifest.ports)
611+
for (const auto& [port_name, port_info] : manifest->ports)
600612
{
601613
auto remap_it = port_remap.find(port_name);
602614
if (remap_it == port_remap.end())
@@ -643,8 +655,8 @@ TreeNode::Ptr XMLParser::Pimpl::createNodeFromXML(const XMLElement* element,
643655
for (const auto& remap_it : port_remap)
644656
{
645657
const auto& port_name = remap_it.first;
646-
auto port_it = manifest.ports.find(port_name);
647-
if (port_it != manifest.ports.end())
658+
auto port_it = manifest->ports.find(port_name);
659+
if (port_it != manifest->ports.end())
648660
{
649661
auto direction = port_it->second.direction();
650662
if (direction != PortDirection::OUTPUT)
@@ -659,17 +671,21 @@ TreeNode::Ptr XMLParser::Pimpl::createNodeFromXML(const XMLElement* element,
659671
}
660672

661673
// use default value if available for empty ports. Only inputs
662-
for (const auto& port_it : manifest.ports)
674+
for (const auto& port_it : manifest->ports)
663675
{
664676
const std::string& port_name = port_it.first;
665677
const PortInfo& port_info = port_it.second;
666678

667679
auto direction = port_info.direction();
680+
668681
if (direction != PortDirection::OUTPUT &&
669682
config.input_ports.count(port_name) == 0 &&
670-
port_info.defaultValue())
683+
!port_info.defaultValue().empty())
671684
{
672-
config.input_ports.insert({port_name, *port_info.defaultValue()});
685+
try {
686+
config.input_ports.insert({port_name, port_info.defaultValueString()});
687+
}
688+
catch(LogicError&) {}
673689
}
674690
}
675691

@@ -886,9 +902,9 @@ void addNodeModelToXML(const TreeNodeManifest& model,
886902
{
887903
port_element->SetAttribute("type", BT::demangle(port_info.type()).c_str());
888904
}
889-
if (port_info.defaultValue().has_value())
905+
if (!port_info.defaultValue().empty())
890906
{
891-
port_element->SetAttribute("default", port_info.defaultValue()->c_str());
907+
port_element->SetAttribute("default", port_info.defaultValueString().c_str());
892908
}
893909

894910
if (!port_info.description().empty())

0 commit comments

Comments
 (0)