BehaviorTree
Core Library to create and execute Behavior Trees
Loading...
Searching...
No Matches
basic_types.h
1#pragma once
2
3#include "behaviortree_cpp/contrib/expected.hpp"
4#include "behaviortree_cpp/exceptions.h"
5#include "behaviortree_cpp/utils/safe_any.hpp"
6
7#include <chrono>
8#include <functional>
9#include <iostream>
10#include <string_view>
11#include <typeinfo>
12#include <unordered_map>
13#include <utility>
14#include <variant>
15#include <vector>
16
17namespace BT
18{
19/// Enumerates the possible types of nodes
20enum class NodeType
21{
22 UNDEFINED = 0,
23 ACTION,
24 CONDITION,
25 CONTROL,
26 DECORATOR,
27 SUBTREE
28};
29
30/// Enumerates the states every node can be in after execution during a particular
31/// time step.
32/// IMPORTANT: Your custom nodes should NEVER return IDLE.
33enum class NodeStatus
34{
35 IDLE = 0,
36 RUNNING = 1,
37 SUCCESS = 2,
38 FAILURE = 3,
39 SKIPPED = 4,
40};
41
42inline bool isStatusActive(const NodeStatus& status)
43{
44 return status != NodeStatus::IDLE && status != NodeStatus::SKIPPED;
45}
46
47inline bool isStatusCompleted(const NodeStatus& status)
48{
49 return status == NodeStatus::SUCCESS || status == NodeStatus::FAILURE;
50}
51
52enum class PortDirection
53{
54 INPUT,
55 OUTPUT,
56 INOUT
57};
58
60
61bool StartWith(StringView str, StringView prefix);
62
63bool StartWith(StringView str, char prefix);
64
65// vector of key/value pairs
67
68/** Usage: given a function/method like this:
69 *
70 * Expected<double> getAnswer();
71 *
72 * User code can check result and error message like this:
73 *
74 * auto res = getAnswer();
75 * if( res )
76 * {
77 * std::cout << "answer was: " << res.value() << std::endl;
78 * }
79 * else{
80 * std::cerr << "failed to get the answer: " << res.error() << std::endl;
81 * }
82 *
83 * */
84template <typename T>
85using Expected = nonstd::expected<T, std::string>;
86
87struct AnyTypeAllowed
88{
89};
90
91/**
92 * @brief convertFromJSON will parse a json string and use JsonExporter
93 * to convert its content to a given type. It will work only if
94 * the type was previously registered. May throw if it fails.
95 *
96 * @param json_text a valid JSON string
97 * @param type you must specify the typeid()
98 * @return the object, wrapped in Any.
99 */
100[[nodiscard]] Any convertFromJSON(StringView json_text, std::type_index type);
101
102/// Same as the non template version, but with automatic casting
103template <typename T>
104[[nodiscard]] inline T convertFromJSON(StringView str)
105{
106 return convertFromJSON(str, typeid(T)).cast<T>();
107}
108
109/**
110 * convertFromString is used to convert a string into a custom type.
111 *
112 * This function is invoked under the hood by TreeNode::getInput(), but only when the
113 * input port contains a string.
114 *
115 * If you have a custom type, you need to implement the corresponding
116 * template specialization.
117 *
118 * If the string starts with the prefix "json:", it will
119 * fall back to convertFromJSON()
120 */
121template <typename T>
122[[nodiscard]] inline T convertFromString(StringView str)
123{
124 // if string starts with "json:{", try to parse it as json
125 if(StartWith(str, "json:"))
126 {
127 str.remove_prefix(5);
128 return convertFromJSON<T>(str);
129 }
130
131 auto type_name = BT::demangle(typeid(T));
132
133 std::cerr << "You (maybe indirectly) called BT::convertFromString() for type ["
134 << type_name << "], but I can't find the template specialization.\n"
135 << std::endl;
136
137 throw LogicError(std::string("You didn't implement the template specialization of "
138 "convertFromString for this type: ") +
139 type_name);
140}
141
142template <>
143[[nodiscard]] std::string convertFromString<std::string>(StringView str);
144
145template <>
146[[nodiscard]] const char* convertFromString<const char*>(StringView str);
147
148template <>
149[[nodiscard]] int8_t convertFromString<int8_t>(StringView str);
150
151template <>
152[[nodiscard]] int16_t convertFromString<int16_t>(StringView str);
153
154template <>
155[[nodiscard]] int32_t convertFromString<int32_t>(StringView str);
156
157template <>
158[[nodiscard]] int64_t convertFromString<int64_t>(StringView str);
159
160template <>
161[[nodiscard]] uint8_t convertFromString<uint8_t>(StringView str);
162
163template <>
164[[nodiscard]] uint16_t convertFromString<uint16_t>(StringView str);
165
166template <>
167[[nodiscard]] uint32_t convertFromString<uint32_t>(StringView str);
168
169template <>
170[[nodiscard]] uint64_t convertFromString<uint64_t>(StringView str);
171
172template <>
173[[nodiscard]] float convertFromString<float>(StringView str);
174
175template <>
176[[nodiscard]] double convertFromString<double>(StringView str);
177
178// Integer numbers separated by the character ";"
179template <>
180[[nodiscard]] std::vector<int> convertFromString<std::vector<int>>(StringView str);
181
182// Real numbers separated by the character ";"
183template <>
184[[nodiscard]] std::vector<double> convertFromString<std::vector<double>>(StringView str);
185
186// Boolean values separated by the character ";"
187template <>
188[[nodiscard]] std::vector<bool> convertFromString<std::vector<bool>>(StringView str);
189
190// Strings separated by the character ";"
191template <>
192[[nodiscard]] std::vector<std::string>
193convertFromString<std::vector<std::string>>(StringView str);
194
195// This recognizes either 0/1, true/false, TRUE/FALSE
196template <>
197[[nodiscard]] bool convertFromString<bool>(StringView str);
198
199// Names with all capital letters
200template <>
201[[nodiscard]] NodeStatus convertFromString<NodeStatus>(StringView str);
202
203// Names with all capital letters
204template <>
205[[nodiscard]] NodeType convertFromString<NodeType>(StringView str);
206
207template <>
208[[nodiscard]] PortDirection convertFromString<PortDirection>(StringView str);
209
211
213
214// helper function
215template <typename T>
216[[nodiscard]] inline StringConverter GetAnyFromStringFunctor()
217{
218 if constexpr(std::is_constructible_v<StringView, T>)
219 {
220 return [](StringView str) { return Any(str); };
221 }
222 else if constexpr(std::is_same_v<BT::AnyTypeAllowed, T> || std::is_enum_v<T>)
223 {
224 return {};
225 }
226 else
227 {
228 return [](StringView str) { return Any(convertFromString<T>(str)); };
229 }
230}
231
232template <>
233[[nodiscard]] inline StringConverter GetAnyFromStringFunctor<void>()
234{
235 return {};
236}
237
238//------------------------------------------------------------------
239
240template <typename T>
241constexpr bool IsConvertibleToString()
242{
243 return std::is_convertible_v<T, std::string> ||
244 std::is_convertible_v<T, std::string_view>;
245}
246
247Expected<std::string> toJsonString(const Any& value);
248
249/**
250 * @brief toStr is the reverse operation of convertFromString.
251 *
252 * If T is a custom type and there is no template specialization,
253 * it will try to fall back to toJsonString()
254 */
255template <typename T>
256[[nodiscard]] std::string toStr(const T& value)
257{
258 if constexpr(IsConvertibleToString<T>())
259 {
260 return static_cast<std::string>(value);
261 }
262 else if constexpr(!std::is_arithmetic_v<T>)
263 {
264 if(auto str = toJsonString(Any(value)))
265 {
266 return *str;
267 }
268
269 throw LogicError(StrCat("Function BT::toStr<T>() not specialized for type [",
270 BT::demangle(typeid(T)), "]"));
271 }
272 else
273 {
274 return std::to_string(value);
275 }
276}
277
278template <>
279[[nodiscard]] std::string toStr<bool>(const bool& value);
280
281template <>
282[[nodiscard]] std::string toStr<std::string>(const std::string& value);
283
284template <>
285[[nodiscard]] std::string toStr<BT::NodeStatus>(const BT::NodeStatus& status);
286
287/**
288 * @brief toStr converts NodeStatus to string. Optionally colored.
289 */
290[[nodiscard]] std::string toStr(BT::NodeStatus status, bool colored);
291
292std::ostream& operator<<(std::ostream& os, const BT::NodeStatus& status);
293
294template <>
295[[nodiscard]] std::string toStr<BT::NodeType>(const BT::NodeType& type);
296
297std::ostream& operator<<(std::ostream& os, const BT::NodeType& type);
298
299template <>
300[[nodiscard]] std::string toStr<BT::PortDirection>(const BT::PortDirection& direction);
301
302std::ostream& operator<<(std::ostream& os, const BT::PortDirection& type);
303
304// Small utility, unless you want to use <boost/algorithm/string.hpp>
305[[nodiscard]] std::vector<StringView> splitString(const StringView& strToSplit,
306 char delimeter);
307
308template <typename Predicate>
309using enable_if = typename std::enable_if<Predicate::value>::type*;
310
311template <typename Predicate>
312using enable_if_not = typename std::enable_if<!Predicate::value>::type*;
313
314#ifdef USE_BTCPP3_OLD_NAMES
315// note: we also use the name Optional instead of expected because it is more intuitive
316// for users that are not up to date with "modern" C++
317template <typename T>
318using Optional = nonstd::expected<T, std::string>;
319#endif
320
321/** Usage: given a function/method like:
322 *
323 * Result DoSomething();
324 *
325 * User code can check result and error message like this:
326 *
327 * auto res = DoSomething();
328 * if( res )
329 * {
330 * std::cout << "DoSomething() done " << std::endl;
331 * }
332 * else{
333 * std::cerr << "DoSomething() failed with message: " << res.error() << std::endl;
334 * }
335 *
336 * */
337using Result = Expected<std::monostate>;
338
339struct Timestamp
340{
341 // Number being incremented every time a new value is written
342 uint64_t seq = 0;
343 // Last update time. Nanoseconds since epoch
344 std::chrono::nanoseconds time = std::chrono::nanoseconds(0);
345};
346
347[[nodiscard]] bool IsAllowedPortName(StringView str);
348
349[[nodiscard]] bool IsReservedAttribute(StringView str);
350
351/// Returns the first forbidden character found in the name, or '\0' if valid.
352/// Forbidden characters include: space, tab, newline, CR, < > & " ' / \ : * ? | .
353/// and control characters (ASCII 0-31, 127). UTF-8 multibyte sequences are allowed.
354[[nodiscard]] char findForbiddenChar(StringView name);
355
356class TypeInfo
357{
358public:
359 template <typename T>
360 static TypeInfo Create()
361 {
362 return TypeInfo{ typeid(T), GetAnyFromStringFunctor<T>() };
363 }
364
365 TypeInfo() : type_info_(typeid(AnyTypeAllowed)), type_str_("AnyTypeAllowed")
366 {}
367
368 TypeInfo(std::type_index type_info, StringConverter conv)
369 : type_info_(type_info), converter_(conv), type_str_(BT::demangle(type_info))
370 {}
371
372 [[nodiscard]] const std::type_index& type() const;
373
374 [[nodiscard]] const std::string& typeName() const;
375
376 [[nodiscard]] Any parseString(const char* str) const;
377
378 [[nodiscard]] Any parseString(const std::string& str) const;
379
380 template <typename T>
381 [[nodiscard]] Any parseString(const T&) const
382 {
383 // avoid compilation errors
384 return {};
385 }
386
387 [[nodiscard]] bool isStronglyTyped() const
388 {
389 return type_info_ != typeid(AnyTypeAllowed) && type_info_ != typeid(BT::Any);
390 }
391
392 [[nodiscard]] const StringConverter& converter() const
393 {
394 return converter_;
395 }
396
397private:
398 std::type_index type_info_;
399 StringConverter converter_;
400 std::string type_str_;
401};
402
403class PortInfo : public TypeInfo
404{
405public:
406 PortInfo(PortDirection direction = PortDirection::INOUT)
407 : TypeInfo(), direction_(direction)
408 {}
409
410 PortInfo(PortDirection direction, std::type_index type_info, StringConverter conv)
411 : TypeInfo(type_info, conv), direction_(direction)
412 {}
413
414 [[nodiscard]] PortDirection direction() const;
415
416 void setDescription(StringView description);
417
418 template <typename T>
419 void setDefaultValue(const T& default_value)
420 {
421 default_value_ = Any(default_value);
422 try
423 {
424 default_value_str_ = BT::toStr(default_value);
425 }
426 // NOLINTNEXTLINE(bugprone-empty-catch)
427 catch(LogicError&)
428 {
429 // conversion to string not available for this type, ignore
430 }
431 }
432
433 [[nodiscard]] const std::string& description() const;
434
435 [[nodiscard]] const Any& defaultValue() const;
436
437 [[nodiscard]] const std::string& defaultValueString() const;
438
439private:
440 PortDirection direction_;
441 std::string description_;
442 Any default_value_;
443 std::string default_value_str_;
444};
445
446template <typename T = AnyTypeAllowed>
447[[nodiscard]] std::pair<std::string, PortInfo> CreatePort(PortDirection direction,
448 StringView name,
449 StringView description = {})
450{
451 auto sname = static_cast<std::string>(name);
452 if(!IsAllowedPortName(sname))
453 {
454 throw RuntimeError("The name of a port must not be `name` or `ID` "
455 "and must start with an alphabetic character. "
456 "Underscore is reserved.");
457 }
458
459 std::pair<std::string, PortInfo> out;
460
461 if(std::is_same<T, void>::value)
462 {
463 out = { sname, PortInfo(direction) };
464 }
465 else
466 {
467 out = { sname, PortInfo(direction, typeid(T), GetAnyFromStringFunctor<T>()) };
468 }
469 if(!description.empty())
470 {
471 out.second.setDescription(description);
472 }
473 return out;
474}
475
476//----------
477/** Syntactic sugar to invoke CreatePort<T>(PortDirection::INPUT, ...)
478 *
479 * @param name the name of the port
480 * @param description optional human-readable description
481 */
482template <typename T = AnyTypeAllowed>
483[[nodiscard]] inline std::pair<std::string, PortInfo>
484InputPort(StringView name, StringView description = {})
485{
486 return CreatePort<T>(PortDirection::INPUT, name, description);
487}
488
489/** Syntactic sugar to invoke CreatePort<T>(PortDirection::OUTPUT,...)
490 *
491 * @param name the name of the port
492 * @param description optional human-readable description
493 */
494template <typename T = AnyTypeAllowed>
495[[nodiscard]] inline std::pair<std::string, PortInfo>
496OutputPort(StringView name, StringView description = {})
497{
498 return CreatePort<T>(PortDirection::OUTPUT, name, description);
499}
500
501/** Syntactic sugar to invoke CreatePort<T>(PortDirection::INOUT,...)
502 *
503 * @param name the name of the port
504 * @param description optional human-readable description
505 */
506template <typename T = AnyTypeAllowed>
507[[nodiscard]] inline std::pair<std::string, PortInfo>
508BidirectionalPort(StringView name, StringView description = {})
509{
510 return CreatePort<T>(PortDirection::INOUT, name, description);
511}
512//----------
513
514namespace details
515{
516
517template <typename T = AnyTypeAllowed, typename DefaultT = T>
518[[nodiscard]] inline std::pair<std::string, PortInfo>
519PortWithDefault(PortDirection direction, StringView name, const DefaultT& default_value,
520 StringView description)
521{
522 static_assert(IsConvertibleToString<DefaultT>() || std::is_convertible_v<T, DefaultT> ||
523 std::is_constructible_v<T, DefaultT>,
524 "The default value must be either the same of the port or string");
525
526 auto out = CreatePort<T>(direction, name, description);
527
528 if constexpr(std::is_constructible_v<T, DefaultT>)
529 {
530 out.second.setDefaultValue(T(default_value));
531 }
532 else if constexpr(IsConvertibleToString<DefaultT>())
533 {
534 out.second.setDefaultValue(std::string(default_value));
535 }
536 else
537 {
538 out.second.setDefaultValue(default_value);
539 }
540 return out;
541}
542
543} // end namespace details
544
545/** Syntactic sugar to invoke CreatePort<T>(PortDirection::INPUT,...)
546 * It also sets the PortInfo::defaultValue()
547 *
548 * @param name the name of the port
549 * @param default_value default value of the port, either type T of BlackboardKey
550 * @param description optional human-readable description
551 */
552template <typename T = AnyTypeAllowed, typename DefaultT = T>
553[[nodiscard]] inline std::pair<std::string, PortInfo>
554InputPort(StringView name, const DefaultT& default_value, StringView description)
555{
556 return details::PortWithDefault<T, DefaultT>(PortDirection::INPUT, name, default_value,
557 description);
558}
559
560/** Syntactic sugar to invoke CreatePort<T>(PortDirection::INOUT,...)
561 * It also sets the PortInfo::defaultValue()
562 *
563 * @param name the name of the port
564 * @param default_value default value of the port, either type T of BlackboardKey
565 * @param description optional human-readable description
566 */
567template <typename T = AnyTypeAllowed, typename DefaultT = T>
568[[nodiscard]] inline std::pair<std::string, PortInfo>
569BidirectionalPort(StringView name, const DefaultT& default_value, StringView description)
570{
571 return details::PortWithDefault<T, DefaultT>(PortDirection::INOUT, name, default_value,
572 description);
573}
574
575/** Syntactic sugar to invoke CreatePort<T>(PortDirection::OUTPUT,...)
576 * It also sets the PortInfo::defaultValue()
577 *
578 * @param name the name of the port
579 * @param default_value default blackboard entry where the output is written
580 * @param description optional human-readable description
581 */
582template <typename T = AnyTypeAllowed>
583[[nodiscard]] inline std::pair<std::string, PortInfo> OutputPort(StringView name,
584 StringView default_value,
585 StringView description)
586{
587 if(default_value.empty() || default_value.front() != '{' || default_value.back() != '}')
588 {
589 throw LogicError("Output port can only refer to blackboard entries, i.e. use the "
590 "syntax '{port_name}'");
591 }
592 auto out = CreatePort<T>(PortDirection::OUTPUT, name, description);
593 out.second.setDefaultValue(default_value);
594 return out;
595}
596
597//----------
598
600
601template <typename T, typename = void>
603{
604};
605
606template <typename T>
608 T, typename std::enable_if<
609 std::is_same<decltype(T::providedPorts()), PortsList>::value>::type>
610 : std::true_type
611{
612};
613
614template <typename T, typename = void>
616{
617};
618
619template <typename T>
621 T, typename std::enable_if<
622 std::is_same<decltype(T::metadata()), KeyValueVector>::value>::type>
623 : std::true_type
624{
625};
626
627template <typename T>
628[[nodiscard]] inline PortsList
629getProvidedPorts(enable_if<has_static_method_providedPorts<T>> = nullptr)
630{
631 return T::providedPorts();
632}
633
634template <typename T>
635[[nodiscard]] inline PortsList
636getProvidedPorts(enable_if_not<has_static_method_providedPorts<T>> = nullptr)
637{
638 return {};
639}
640
641using TimePoint = std::chrono::high_resolution_clock::time_point;
642using Duration = std::chrono::high_resolution_clock::duration;
643
644} // namespace BT
Definition: safe_any.hpp:50
Definition: exceptions.h:48
Definition: basic_types.h:404
Definition: basic_types.h:357
The SwitchNode is equivalent to a switch statement, where a certain branch (child) is executed accord...
Definition: basic_types.h:515
Definition: action_node.h:24
std::pair< std::string, PortInfo > BidirectionalPort(StringView name, StringView description={})
Definition: basic_types.h:508
NodeStatus
Definition: basic_types.h:34
Any convertFromJSON(StringView json_text, std::type_index type)
convertFromJSON will parse a json string and use JsonExporter to convert its content to a given type....
char findForbiddenChar(StringView name)
std::pair< std::string, PortInfo > OutputPort(StringView name, StringView default_value, StringView description)
Definition: basic_types.h:583
std::string toStr(BT::NodeStatus status, bool colored)
toStr converts NodeStatus to string. Optionally colored.
T convertFromJSON(StringView str)
Same as the non template version, but with automatic casting.
Definition: basic_types.h:104
NodeType
Enumerates the possible types of nodes.
Definition: basic_types.h:21
std::pair< std::string, PortInfo > InputPort(StringView name, const DefaultT &default_value, StringView description)
Definition: basic_types.h:554
std::pair< std::string, PortInfo > OutputPort(StringView name, StringView description={})
Definition: basic_types.h:496
std::pair< std::string, PortInfo > InputPort(StringView name, StringView description={})
Definition: basic_types.h:484
std::pair< std::string, PortInfo > BidirectionalPort(StringView name, const DefaultT &default_value, StringView description)
Definition: basic_types.h:569
std::string toStr(const T &value)
toStr is the reverse operation of convertFromString.
Definition: basic_types.h:256
T convertFromString(StringView str)
Definition: basic_types.h:122
Definition: basic_types.h:88
Definition: basic_types.h:340
Definition: basic_types.h:616
Definition: basic_types.h:603