From 0c9fe42485197be00d07b292b3237fc3d530f8b3 Mon Sep 17 00:00:00 2001 From: redvinaa Date: Thu, 31 Jul 2025 15:18:40 +0200 Subject: [PATCH 001/147] Make tick method protected Signed-off-by: redvinaa --- include/behaviortree_cpp/controls/sequence_node.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/behaviortree_cpp/controls/sequence_node.h b/include/behaviortree_cpp/controls/sequence_node.h index 0bb085705..575db29d5 100644 --- a/include/behaviortree_cpp/controls/sequence_node.h +++ b/include/behaviortree_cpp/controls/sequence_node.h @@ -43,11 +43,11 @@ class SequenceNode : public ControlNode protected: size_t current_child_idx_; + virtual BT::NodeStatus tick() override; + private: size_t skipped_count_ = 0; bool asynch_ = false; - - virtual BT::NodeStatus tick() override; }; } // namespace BT From 7fc24c52fb6210c2916ca868b0917ca019c18476 Mon Sep 17 00:00:00 2001 From: redvinaa Date: Thu, 31 Jul 2025 15:19:16 +0200 Subject: [PATCH 002/147] Propagate node config to parent Signed-off-by: redvinaa --- include/behaviortree_cpp/controls/sequence_node.h | 4 +++- src/controls/sequence_node.cpp | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/behaviortree_cpp/controls/sequence_node.h b/include/behaviortree_cpp/controls/sequence_node.h index 575db29d5..c4d24d056 100644 --- a/include/behaviortree_cpp/controls/sequence_node.h +++ b/include/behaviortree_cpp/controls/sequence_node.h @@ -34,7 +34,9 @@ namespace BT class SequenceNode : public ControlNode { public: - SequenceNode(const std::string& name, bool make_async = false); + SequenceNode( + const std::string& name, bool make_async = false, + const NodeConfiguration& conf = NodeConfiguration()); virtual ~SequenceNode() override = default; diff --git a/src/controls/sequence_node.cpp b/src/controls/sequence_node.cpp index 483e66c5b..76371146b 100644 --- a/src/controls/sequence_node.cpp +++ b/src/controls/sequence_node.cpp @@ -15,8 +15,8 @@ namespace BT { -SequenceNode::SequenceNode(const std::string& name, bool make_async) - : ControlNode::ControlNode(name, {}), current_child_idx_(0), asynch_(make_async) +SequenceNode::SequenceNode(const std::string& name, bool make_async, const NodeConfiguration& conf) + : ControlNode::ControlNode(name, conf), current_child_idx_(0), asynch_(make_async) { if(asynch_) setRegistrationID("AsyncSequence"); From 2e8b13e4dd8da98d156028a5cf48654cbcfcc70f Mon Sep 17 00:00:00 2001 From: redvinaa Date: Thu, 31 Jul 2025 15:29:22 +0200 Subject: [PATCH 003/147] Lint Signed-off-by: redvinaa --- include/behaviortree_cpp/controls/sequence_node.h | 5 ++--- src/controls/sequence_node.cpp | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/behaviortree_cpp/controls/sequence_node.h b/include/behaviortree_cpp/controls/sequence_node.h index c4d24d056..cb6ab9bfc 100644 --- a/include/behaviortree_cpp/controls/sequence_node.h +++ b/include/behaviortree_cpp/controls/sequence_node.h @@ -34,9 +34,8 @@ namespace BT class SequenceNode : public ControlNode { public: - SequenceNode( - const std::string& name, bool make_async = false, - const NodeConfiguration& conf = NodeConfiguration()); + SequenceNode(const std::string& name, bool make_async = false, + const NodeConfiguration& conf = NodeConfiguration()); virtual ~SequenceNode() override = default; diff --git a/src/controls/sequence_node.cpp b/src/controls/sequence_node.cpp index 76371146b..a19c6b9ec 100644 --- a/src/controls/sequence_node.cpp +++ b/src/controls/sequence_node.cpp @@ -15,7 +15,8 @@ namespace BT { -SequenceNode::SequenceNode(const std::string& name, bool make_async, const NodeConfiguration& conf) +SequenceNode::SequenceNode(const std::string& name, bool make_async, + const NodeConfiguration& conf) : ControlNode::ControlNode(name, conf), current_child_idx_(0), asynch_(make_async) { if(asynch_) From 6731c518f804e58147eb9698a03ae7a01f90ef93 Mon Sep 17 00:00:00 2001 From: Yiyi Wang <91304853+ahuo1@users.noreply.github.com> Date: Wed, 10 Sep 2025 18:20:00 +0800 Subject: [PATCH 004/147] fix: check path attribute before using (#1005) Co-authored-by: ahuo --- src/xml_parsing.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/xml_parsing.cpp b/src/xml_parsing.cpp index 8b9ce95a1..e90e1b118 100644 --- a/src/xml_parsing.cpp +++ b/src/xml_parsing.cpp @@ -261,10 +261,16 @@ void XMLParser::PImpl::loadDocImpl(XMLDocument* doc, bool add_includes) break; } + const char* path_attr = incl_node->Attribute("path"); + if(!path_attr) + { + throw RuntimeError("Invalid tag: missing 'path' attribute"); + } + #if __bt_cplusplus >= 202002L - auto file_path(std::filesystem::path(incl_node->Attribute("path"))); + auto file_path{ std::filesystem::path(path_attr) }; #else - auto file_path(std::filesystem::u8path(incl_node->Attribute("path"))); + auto file_path{ std::filesystem::u8path(path_attr) }; #endif const char* ros_pkg_relative_path = incl_node->Attribute("ros_pkg"); From 4b23dcaf0ce951a31299ebdd61df69f9ce99a76d Mon Sep 17 00:00:00 2001 From: Yiyi Wang <91304853+ahuo1@users.noreply.github.com> Date: Thu, 11 Sep 2025 18:59:23 +0800 Subject: [PATCH 005/147] fix: validate __type field before accessing in fromJson (#1009) Co-authored-by: ahuo --- src/json_export.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/json_export.cpp b/src/json_export.cpp index 2ad648f3e..b716a94ea 100644 --- a/src/json_export.cpp +++ b/src/json_export.cpp @@ -112,9 +112,20 @@ JsonExporter::ExpectedEntry JsonExporter::fromJson(const nlohmann::json& source) } } - if(!source.contains("__type") && !source.is_array()) + if(source.is_array()) { - return nonstd::make_unexpected("Missing field '__type'"); + if(source.empty()) + return nonstd::make_unexpected("Missing field '__type'"); + const auto& first = source[0]; + if(!first.is_object() || !first.contains("__type")) + return nonstd::make_unexpected("Missing field '__type'"); + if(!first["__type"].is_string()) + return nonstd::make_unexpected("Invalid '__type' (must be string)"); + } + else + { + if(!source.is_object() || !source.contains("__type") || !source["__type"].is_string()) + return nonstd::make_unexpected("Missing field '__type'"); } auto& from_converters = From cb6c7514efa628adb8180b58b4c9ccdebbe096e3 Mon Sep 17 00:00:00 2001 From: Yiyi Wang <91304853+ahuo1@users.noreply.github.com> Date: Thu, 11 Sep 2025 22:24:02 +0800 Subject: [PATCH 006/147] fix: use dynamically growing error buffer in ParseScript (#1007) * fix: use dynamically growing error buffer in ParseScript * style: format code * fix: use dynamically growing error buffer in ValidateScript --------- Co-authored-by: ahuo --- src/script_parser.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/script_parser.cpp b/src/script_parser.cpp index 5bbc62006..95c629fa2 100644 --- a/src/script_parser.cpp +++ b/src/script_parser.cpp @@ -13,11 +13,12 @@ using ErrorReport = lexy_ext::_report_error; Expected ParseScript(const std::string& script) { - char error_msgs_buffer[2048]; + std::string error_msgs_buffer; // dynamically growing error buffer auto input = lexy::string_input(script); - auto result = - lexy::parse(input, ErrorReport().to(error_msgs_buffer)); + + auto reporter = ErrorReport().to(std::back_inserter(error_msgs_buffer)); + auto result = lexy::parse(input, reporter); if(result.has_value() && result.error_count() == 0) { try @@ -69,11 +70,12 @@ BT::Expected ParseScriptAndExecute(Ast::Environment& env, const std::string Result ValidateScript(const std::string& script) { - char error_msgs_buffer[2048]; + std::string error_msgs_buffer; // dynamically growing error buffer auto input = lexy::string_input(script); - auto result = - lexy::parse(input, ErrorReport().to(error_msgs_buffer)); + + auto reporter = ErrorReport().to(std::back_inserter(error_msgs_buffer)); + auto result = lexy::parse(input, reporter); if(result.has_value() && result.error_count() == 0) { try From 875037033bafcbe335d8e130dcb1d9374448d98e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Su=C3=A1rez?= Date: Thu, 11 Sep 2025 16:24:31 +0200 Subject: [PATCH 007/147] Append SQLite3_INCLUDE_DIRS to BTCPP_EXTRA_INCLUDE_DIRS, otherwise sqlite3.h won't be found (#1002) Co-authored-by: alejandro.suarez@omron.com --- cmake/conan_build.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/conan_build.cmake b/cmake/conan_build.cmake index 3bf3a7225..83876cc61 100644 --- a/cmake/conan_build.cmake +++ b/cmake/conan_build.cmake @@ -10,6 +10,7 @@ endif() if(BTCPP_SQLITE_LOGGING) find_package(SQLite3 REQUIRED) list(APPEND BTCPP_EXTRA_LIBRARIES ${SQLite3_LIBRARIES}) + list(APPEND BTCPP_EXTRA_INCLUDE_DIRS ${SQLite3_INCLUDE_DIRS}) message(STATUS "SQLite3_LIBRARIES: ${SQLite3_LIBRARIES}") endif() From 2ed6d496b07fd80b302655b64b1caa90f63aebdd Mon Sep 17 00:00:00 2001 From: Ezra Brooks Date: Thu, 11 Sep 2025 08:27:17 -0600 Subject: [PATCH 008/147] Clean up VerifyXML logic (#1000) * Refactor VerifyXML to clarify logic - Reduces duplication in VerifyXML by handling the ID check for built-in node types up front so they can then be definitively looked up in the registered nodes. - Enhances error messaging in VerifyXML by using *either* the node name *or* the ID, depending on which is appropriate, instead of leaving users guessing "which Decorator is wrong" - Fixes custom Action and Condition nodes using shorthand syntax not being properly verified - Fixes `` not being verified with the same logic as `` - Fixes `` not triggering a behavior lookup when `` would. * fix tests that were failing due to bad assumptions --- src/xml_parsing.cpp | 133 ++++++++++++++++------------------------ tests/gtest_factory.cpp | 5 +- 2 files changed, 55 insertions(+), 83 deletions(-) diff --git a/src/xml_parsing.cpp b/src/xml_parsing.cpp index e90e1b118..3f895e04b 100644 --- a/src/xml_parsing.cpp +++ b/src/xml_parsing.cpp @@ -434,119 +434,76 @@ void VerifyXML(const std::string& xml_text, const std::string ID = node->Attribute("ID") ? node->Attribute("ID") : ""; const int line_number = node->GetLineNum(); - if(name == "Decorator") + // Precondition: built-in XML element types must define attribute [ID] + const bool is_builtin = + (name == "Decorator" || name == "Action" || name == "Condition" || + name == "Control" || name == "SubTree"); + if(is_builtin && ID.empty()) { - if(children_count != 1) - { - ThrowError(line_number, "The tag must have exactly 1 " - "child"); - } - if(ID.empty()) - { - ThrowError(line_number, "The tag must have the " - "attribute [ID]"); - } - } - else if(name == "Action") - { - if(children_count != 0) - { - ThrowError(line_number, "The tag must not have any " - "child"); - } - if(ID.empty()) - { - ThrowError(line_number, "The tag must have the " - "attribute [ID]"); - } + ThrowError(line_number, + std::string("The tag <") + name + "> must have the attribute [ID]"); } - else if(name == "Condition") + + if(name == "BehaviorTree") { - if(children_count != 0) - { - ThrowError(line_number, "The tag must not have any " - "child"); - } - if(ID.empty()) + if(ID.empty() && behavior_tree_count > 1) { - ThrowError(line_number, "The tag must have the " - "attribute [ID]"); + ThrowError(line_number, "The tag must have the attribute [ID]"); } - } - else if(name == "Control") - { - if(children_count == 0) + if(registered_nodes.count(ID) != 0) { - ThrowError(line_number, "The tag must have at least 1 " - "child"); + ThrowError(line_number, "The attribute [ID] of tag must not use " + "the name of a registered Node"); } - if(ID.empty()) + if(children_count != 1) { - ThrowError(line_number, "The tag must have the " - "attribute [ID]"); + ThrowError(line_number, "The tag with ID '" + ID + + "' must have exactly 1 child"); } } else if(name == "SubTree") { if(children_count != 0) { - ThrowError(line_number, " should not have any child"); - } - if(ID.empty()) - { - ThrowError(line_number, "The tag must have the " - "attribute [ID]"); + ThrowError(line_number, + " with ID '" + ID + "' should not have any child"); } if(registered_nodes.count(ID) != 0) { - ThrowError(line_number, "The attribute [ID] of tag must " - "not use the name of a registered Node"); - } - } - else if(name == "BehaviorTree") - { - if(ID.empty() && behavior_tree_count > 1) - { - ThrowError(line_number, "The tag must have the " - "attribute [ID]"); - } - if(children_count != 1) - { - ThrowError(line_number, "The tag must have exactly 1 " - "child"); - } - if(registered_nodes.count(ID) != 0) - { - ThrowError(line_number, "The attribute [ID] of tag " - "must not use the name of a registered Node"); + ThrowError(line_number, "The attribute [ID] of tag must not use the " + "name of a registered Node"); } } else { - // search in the factory and the list of subtrees - const auto search = registered_nodes.find(name); + // use ID for builtin node types, otherwise use the element name + const auto lookup_name = is_builtin ? ID : name; + const auto search = registered_nodes.find(lookup_name); bool found = (search != registered_nodes.end()); if(!found) { - ThrowError(line_number, std::string("Node not recognized: ") + name); + ThrowError(line_number, std::string("Node not recognized: ") + lookup_name); } - if(search->second == NodeType::DECORATOR) + const auto node_type = search->second; + const std::string& registered_name = search->first; + + if(node_type == NodeType::DECORATOR) { if(children_count != 1) { - ThrowError(line_number, - std::string("The node <") + name + "> must have exactly 1 child"); + ThrowError(line_number, std::string("The node '") + registered_name + + "' must have exactly 1 child"); } } - else if(search->second == NodeType::CONTROL) + else if(node_type == NodeType::CONTROL) { if(children_count == 0) { - ThrowError(line_number, - std::string("The node <") + name + "> must have 1 or more children"); + ThrowError(line_number, std::string("The node '") + registered_name + + "' must have 1 or more children"); } - if(name == "ReactiveSequence") + if(registered_name == "ReactiveSequence") { size_t async_count = 0; for(auto child = node->FirstChildElement(); child != nullptr; @@ -568,13 +525,29 @@ void VerifyXML(const std::string& xml_text, ++async_count; if(async_count > 1) { - ThrowError(line_number, std::string("A ReactiveSequence cannot have more " - "than one async child.")); + ThrowError(line_number, std::string("A ReactiveSequence cannot have " + "more than one async child.")); } } } } } + else if(node_type == NodeType::ACTION) + { + if(children_count != 0) + { + ThrowError(line_number, std::string("The node '") + registered_name + + "' must not have any child"); + } + } + else if(node_type == NodeType::CONDITION) + { + if(children_count != 0) + { + ThrowError(line_number, std::string("The node '") + registered_name + + "' must not have any child"); + } + } } //recursion for(auto child = node->FirstChildElement(); child != nullptr; diff --git a/tests/gtest_factory.cpp b/tests/gtest_factory.cpp index 321d24ef5..b07835a1f 100644 --- a/tests/gtest_factory.cpp +++ b/tests/gtest_factory.cpp @@ -82,7 +82,7 @@ static const char* xml_text_subtree_part1 = R"( - + )"; @@ -93,11 +93,10 @@ static const char* xml_text_subtree_part2 = R"( - + - )"; From 58ed0ece368395c43e5e88b4059345c8de444fb5 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Tue, 30 Sep 2025 23:46:52 +0200 Subject: [PATCH 009/147] remove wildcards from 3rd party --- 3rdparty/wildcards/LICENSE_1_0.txt | 23 - 3rdparty/wildcards/README.md | 215 -- 3rdparty/wildcards/wildcards.hpp | 1830 ------------------ include/behaviortree_cpp/utils/wildcards.hpp | 61 + src/bt_factory.cpp | 6 +- tests/gtest_match.cpp | 16 +- 6 files changed, 72 insertions(+), 2079 deletions(-) delete mode 100644 3rdparty/wildcards/LICENSE_1_0.txt delete mode 100644 3rdparty/wildcards/README.md delete mode 100644 3rdparty/wildcards/wildcards.hpp create mode 100644 include/behaviortree_cpp/utils/wildcards.hpp diff --git a/3rdparty/wildcards/LICENSE_1_0.txt b/3rdparty/wildcards/LICENSE_1_0.txt deleted file mode 100644 index 36b7cd93c..000000000 --- a/3rdparty/wildcards/LICENSE_1_0.txt +++ /dev/null @@ -1,23 +0,0 @@ -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/3rdparty/wildcards/README.md b/3rdparty/wildcards/README.md deleted file mode 100644 index 72b4a6207..000000000 --- a/3rdparty/wildcards/README.md +++ /dev/null @@ -1,215 +0,0 @@ -[![language.badge]][language.url] [![standard.badge]][standard.url] [![license.badge]][license.url] [![travis.badge]][travis.url] [![appveyor.badge]][appveyor.url] [![release.badge]][release.url] [![godbolt.badge]][godbolt.url] [![wandbox.badge]][wandbox.url] - -# Wildcards - -*Wildcards* is a simple C++ header-only template library which implements -a general purpose algorithm for matching using wildcards. It supports both -runtime and compile time execution. - -## Basic usage - -The following examples of the basic usage are functionaly equivalent. - -```C++ -#include - -int main() -{ - { - using wildcards::match; - - static_assert(match("Hello, World!", "H*World?"), ""); - } - - { - using wildcards::make_matcher; - - static_assert(make_matcher("H*World?").matches("Hello, World!"), ""); - } - - { - using namespace wildcards::literals; - - static_assert("H*World?"_wc.matches("Hello, World!"), ""); - } - - return 0; -} -``` - -## Advanced usage - -The following examples of the advanced usage are functionaly equivalent. - -```C++ -#include - -int main() -{ - { - using wildcards::match; - - static_assert(match("Hello, World!", "H%World_", {'%', '_', '\\'}), ""); - } - - { - using wildcards::make_matcher; - - static_assert(make_matcher("H%World_", {'%', '_', '\\'}).matches("Hello, World!"), ""); - } - - return 0; -} -``` - -See more useful and complex [examples](example) and try them online! See also -[the tests](test/src/wildcards) to learn more. - -## Demonstration on Compiler Explorer - -Check compilers output of the following example on [Compiler Explorer][godbolt.url]. - -```C++ -#include - -using namespace wildcards::literals; - -constexpr auto pattern = "*.[hc](pp|)"_wc; - -// returns true -bool test1() -{ - constexpr auto res = pattern.matches("source.c"); - - static_assert(res, "must be true"); - - return res; -} - -// returns false -bool test2() -{ - constexpr auto res = pattern.matches("source.cc"); - - static_assert(!res, "must be false"); - - return res; -} -``` - -## Integration - -1. Single-header approach - * Copy [`wildcards.hpp`](single_include/wildcards.hpp) from - [`single_include`](single_include) directory to your project's header - search path. - * Add `#include ` to your source file. - * Use `wildcards::match()` or `wildcards::make_matcher()`. You can also use - operator `""_wc` from `wildcards::literals` namespace. - -2. Multi-header approach - * Add [`include`](include) directory to your project's header search path. - * Add `#include ` to your source file. - * Use `wildcards::match()` or `wildcards::make_matcher()`. You can also use - operator `""_wc` from `wildcards::literals` namespace. - -## Portability - -The library requires at least a C++11 compiler to build. It has no external -dependencies. - -The following compilers are continuously tested at [Travis CI][travis.url] -and [Appveyor CI][appveyor.url]. - -| Compiler | Version | Operating System | Notes | -|---------------------|---------|---------------------|-------------------------| -| Xcode | 9.0 | OS X 10.12 | C++11/14/17 | -| Clang (with libcxx) | 3.9 | Ubuntu 14.04 LTS | C++14/17 | -| Clang (with libcxx) | 4.0 | Ubuntu 14.04 LTS | C++11/14/17 | -| Clang (with libcxx) | 5.0 | Ubuntu 14.04 LTS | C++11/14/17 | -| Clang (with libcxx) | 6.0 | Ubuntu 14.04 LTS | C++11/14/17 | -| GCC | 5.5 | Ubuntu 14.04 LTS | C++11/14/17 | -| GCC | 6.4 | Ubuntu 14.04 LTS | C++11/14/17 | -| GCC | 7.3 | Ubuntu 14.04 LTS | C++11/14/17 | -| GCC | 8.1 | Ubuntu 14.04 LTS | C++11/14/17 | -| DJGPP | 7.2 | Ubuntu 14.04 LTS | C++11/14/17, build only | -| Visual Studio | 14 2015 | Windows Server 2016 | C++11/14/17, limited | -| Visual Studio | 15 2017 | Windows Server 2016 | C++11/14/17 | -| MinGW | 6.3 | Windows Server 2016 | C++11/14/17 | -| MinGW | 7.2 | Windows Server 2016 | C++11/14/17 | -| MinGW | 7.3 | Windows Server 2016 | C++11/14/17 | - -## License - -This project is licensed under the [Boost 1.0][license.url]. - -## Details - -### Syntax - -| Pattern | Meaning | -| --------- | ---------------------------------------------- | -| `*` | Matches everything. | -| `?` | Matches any single character. | -| `\` | Escape character. | -| `[abc]` | Matches any character in *Set*. | -| `[!abc]` | Matches any character not in *Set*. | -| `(ab\|c)` | Matches one of the sequences in *Alternative*. | - -* *Set* cannot be empty. Any special character loses its special meaning in it. -* *Alternative* can contain more than two or just one sequence. -* The use of *Sets* and *Alternatives* can be switched off. -* Special characters are predefined for `char`, `char16_t`, `char32_t` - and `wchar_t`, but can be redefined. - -### Technical Notes - -* *Wildcards* depends on two components which originate from external sources - and were made part of the repository: - * [`cpp_feature.hpp`](include/cpp_feature.hpp) taken from - [here](https://github.com/ned14/quickcpplib/blob/master/include/cpp_feature.h), - * [`catch.hpp`](test/include/catch.hpp) taken from - [here](https://github.com/catchorg/Catch2/releases/download/v2.4.2/catch.hpp). - -* *Wildcards* uses a recursive approach. Hence you can simply run out of stack - (during runtime execution) or you can exceed the maximum depth of constexpr - evaluation (during compile time execution). If so, try making the input - sequence shorter or the pattern less complex. You can also try to build using - the C++14 standard since the C++14 implementation of the library is more - effective and consumes less resources. - -* Place more specific sequences in *Alternatives* first. This becomes important - when *Alternatives* are nested. E.g. `match("source.cpp", "(*.[hc](pp|))")` - will work as expected but `match("source.cpp", "(*.[hc](|pp))")` will not. - Fixing that would make *Wildcards* unreasonably complex. - -* The `cx` library is a byproduct created during the development of *Wildcards* - which uses some pieces from its functionality internally. More of the `cx` is - used in tests and examples. You can use this library in exactly the same way - as you use *Wildcards* (single-header / multi-header approach) but if you are - interested only in *Wildcards*, you don't need to care about the `cx` at all. - This library might become a separate project in the future. - -[language.url]: https://isocpp.org/ -[language.badge]: https://img.shields.io/badge/language-C++-blue.svg - -[standard.url]: https://en.wikipedia.org/wiki/C%2B%2B#Standardization -[standard.badge]: https://img.shields.io/badge/C%2B%2B-11%2F14%2F17-blue.svg - -[license.url]: http://www.boost.org/LICENSE_1_0.txt -[license.badge]: https://img.shields.io/badge/license-Boost%201.0-blue.svg - -[travis.url]: https://travis-ci.org/zemasoft/wildcards -[travis.badge]: https://travis-ci.org/zemasoft/wildcards.svg?branch=master - -[appveyor.url]: https://ci.appveyor.com/project/zemasoft/wildcards -[appveyor.badge]: https://ci.appveyor.com/api/projects/status/github/zemasoft/wildcards?svg=true&branch=master - -[release.url]: https://github.com/zemasoft/wildcards/releases -[release.badge]: https://img.shields.io/github/release/zemasoft/wildcards.svg - -[godbolt.url]: https://godbolt.org/z/nPr4h7 -[godbolt.badge]: https://img.shields.io/badge/try%20it-on%20godbolt-blue.svg - -[wandbox.url]: https://github.com/zemasoft/wildcards/tree/master/example -[wandbox.badge]: https://img.shields.io/badge/try%20it-on%20wandbox-blue.svg diff --git a/3rdparty/wildcards/wildcards.hpp b/3rdparty/wildcards/wildcards.hpp deleted file mode 100644 index df4f236c9..000000000 --- a/3rdparty/wildcards/wildcards.hpp +++ /dev/null @@ -1,1830 +0,0 @@ -// THIS FILE HAS BEEN GENERATED AUTOMATICALLY. DO NOT EDIT DIRECTLY. -// Generated: 2019-03-08 09:59:35.958950200 -// Copyright Tomas Zeman 2018. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -#ifndef WILDCARDS_HPP -#define WILDCARDS_HPP -#define WILDCARDS_VERSION_MAJOR 1 -#define WILDCARDS_VERSION_MINOR 5 -#define WILDCARDS_VERSION_PATCH 0 -#ifndef WILDCARDS_CARDS_HPP -#define WILDCARDS_CARDS_HPP -#include -namespace wildcards -{ -template -struct cards -{ -constexpr cards(T a, T s, T e) -: anything{std::move(a)}, -single{std::move(s)}, -escape{std::move(e)}, -set_enabled{false}, -alt_enabled{false} -{ -} -constexpr cards(T a, T s, T e, T so, T sc, T sn, T ao, T ac, T ar) -: anything{std::move(a)}, -single{std::move(s)}, -escape{std::move(e)}, -set_enabled{true}, -set_open{std::move(so)}, -set_close{std::move(sc)}, -set_not{std::move(sn)}, -alt_enabled{true}, -alt_open{std::move(ao)}, -alt_close{std::move(ac)}, -alt_or{std::move(ar)} -{ -} -T anything; -T single; -T escape; -bool set_enabled; -T set_open; -T set_close; -T set_not; -bool alt_enabled; -T alt_open; -T alt_close; -T alt_or; -}; -enum class cards_type -{ -standard, -extended -}; -template <> -struct cards -{ -constexpr cards(cards_type type = cards_type::extended) -: set_enabled{type == cards_type::extended}, alt_enabled{type == cards_type::extended} -{ -} -constexpr cards(char a, char s, char e) -: anything{std::move(a)}, -single{std::move(s)}, -escape{std::move(e)}, -set_enabled{false}, -alt_enabled{false} -{ -} -constexpr cards(char a, char s, char e, char so, char sc, char sn, char ao, char ac, char ar) -: anything{std::move(a)}, -single{std::move(s)}, -escape{std::move(e)}, -set_enabled{true}, -set_open{std::move(so)}, -set_close{std::move(sc)}, -set_not{std::move(sn)}, -alt_enabled{true}, -alt_open{std::move(ao)}, -alt_close{std::move(ac)}, -alt_or{std::move(ar)} -{ -} -char anything{'*'}; -char single{'?'}; -char escape{'\\'}; -bool set_enabled{true}; -char set_open{'['}; -char set_close{']'}; -char set_not{'!'}; -bool alt_enabled{true}; -char alt_open{'('}; -char alt_close{')'}; -char alt_or{'|'}; -}; -template <> -struct cards -{ -constexpr cards(cards_type type = cards_type::extended) -: set_enabled{type == cards_type::extended}, alt_enabled{type == cards_type::extended} -{ -} -constexpr cards(char16_t a, char16_t s, char16_t e) -: anything{std::move(a)}, -single{std::move(s)}, -escape{std::move(e)}, -set_enabled{false}, -alt_enabled{false} -{ -} -constexpr cards(char16_t a, char16_t s, char16_t e, char16_t so, char16_t sc, char16_t sn, -char16_t ao, char16_t ac, char16_t ar) -: anything{std::move(a)}, -single{std::move(s)}, -escape{std::move(e)}, -set_enabled{true}, -set_open{std::move(so)}, -set_close{std::move(sc)}, -set_not{std::move(sn)}, -alt_enabled{true}, -alt_open{std::move(ao)}, -alt_close{std::move(ac)}, -alt_or{std::move(ar)} -{ -} -char16_t anything{u'*'}; -char16_t single{u'?'}; -char16_t escape{u'\\'}; -bool set_enabled{true}; -char16_t set_open{u'['}; -char16_t set_close{u']'}; -char16_t set_not{u'!'}; -bool alt_enabled{true}; -char16_t alt_open{u'('}; -char16_t alt_close{u')'}; -char16_t alt_or{u'|'}; -}; -template <> -struct cards -{ -constexpr cards(cards_type type = cards_type::extended) -: set_enabled{type == cards_type::extended}, alt_enabled{type == cards_type::extended} -{ -} -constexpr cards(char32_t a, char32_t s, char32_t e) -: anything{std::move(a)}, -single{std::move(s)}, -escape{std::move(e)}, -set_enabled{false}, -alt_enabled{false} -{ -} -constexpr cards(char32_t a, char32_t s, char32_t e, char32_t so, char32_t sc, char32_t sn, -char32_t ao, char32_t ac, char32_t ar) -: anything{std::move(a)}, -single{std::move(s)}, -escape{std::move(e)}, -set_enabled{true}, -set_open{std::move(so)}, -set_close{std::move(sc)}, -set_not{std::move(sn)}, -alt_enabled{true}, -alt_open{std::move(ao)}, -alt_close{std::move(ac)}, -alt_or{std::move(ar)} -{ -} -char32_t anything{U'*'}; -char32_t single{U'?'}; -char32_t escape{U'\\'}; -bool set_enabled{true}; -char32_t set_open{U'['}; -char32_t set_close{U']'}; -char32_t set_not{U'!'}; -bool alt_enabled{true}; -char32_t alt_open{U'('}; -char32_t alt_close{U')'}; -char32_t alt_or{U'|'}; -}; -template <> -struct cards -{ -constexpr cards(cards_type type = cards_type::extended) -: set_enabled{type == cards_type::extended}, alt_enabled{type == cards_type::extended} -{ -} -constexpr cards(wchar_t a, wchar_t s, wchar_t e) -: anything{std::move(a)}, -single{std::move(s)}, -escape{std::move(e)}, -set_enabled{false}, -alt_enabled{false} -{ -} -constexpr cards(wchar_t a, wchar_t s, wchar_t e, wchar_t so, wchar_t sc, wchar_t sn, wchar_t ao, -wchar_t ac, wchar_t ar) -: anything{std::move(a)}, -single{std::move(s)}, -escape{std::move(e)}, -set_enabled{true}, -set_open{std::move(so)}, -set_close{std::move(sc)}, -set_not{std::move(sn)}, -alt_enabled{true}, -alt_open{std::move(ao)}, -alt_close{std::move(ac)}, -alt_or{std::move(ar)} -{ -} -wchar_t anything{L'*'}; -wchar_t single{L'?'}; -wchar_t escape{L'\\'}; -bool set_enabled{true}; -wchar_t set_open{L'['}; -wchar_t set_close{L']'}; -wchar_t set_not{L'!'}; -bool alt_enabled{true}; -wchar_t alt_open{L'('}; -wchar_t alt_close{L')'}; -wchar_t alt_or{L'|'}; -}; -template -constexpr cards make_cards(T&& a, T&& s, T&& e) -{ -return {std::forward(a), std::forward(s), std::forward(e)}; -} -template -constexpr cards make_cards(T&& a, T&& s, T&& e, T&& so, T&& sc, T&& sn, T&& ao, T&& ac, T&& ar) -{ -return {std::forward(a), std::forward(s), std::forward(e), -std::forward(so), std::forward(sc), std::forward(sn), -std::forward(ao), std::forward(ac), std::forward(ar)}; -} -} -#endif -#ifndef WILDCARDS_MATCH_HPP -#define WILDCARDS_MATCH_HPP -#include -#include -#include -#ifndef CONFIG_HPP -#define CONFIG_HPP -#ifndef QUICKCPPLIB_HAS_FEATURE_H -#define QUICKCPPLIB_HAS_FEATURE_H -#if __cplusplus >= 201103L -#if !defined(__cpp_alias_templates) -#define __cpp_alias_templates 190000 -#endif -#if !defined(__cpp_attributes) -#define __cpp_attributes 190000 -#endif -#if !defined(__cpp_constexpr) -#if __cplusplus >= 201402L -#define __cpp_constexpr 201304 -#else -#define __cpp_constexpr 190000 -#endif -#endif -#if !defined(__cpp_decltype) -#define __cpp_decltype 190000 -#endif -#if !defined(__cpp_delegating_constructors) -#define __cpp_delegating_constructors 190000 -#endif -#if !defined(__cpp_explicit_conversion) -#define __cpp_explicit_conversion 190000 -#endif -#if !defined(__cpp_inheriting_constructors) -#define __cpp_inheriting_constructors 190000 -#endif -#if !defined(__cpp_initializer_lists) -#define __cpp_initializer_lists 190000 -#endif -#if !defined(__cpp_lambdas) -#define __cpp_lambdas 190000 -#endif -#if !defined(__cpp_nsdmi) -#define __cpp_nsdmi 190000 -#endif -#if !defined(__cpp_range_based_for) -#define __cpp_range_based_for 190000 -#endif -#if !defined(__cpp_raw_strings) -#define __cpp_raw_strings 190000 -#endif -#if !defined(__cpp_ref_qualifiers) -#define __cpp_ref_qualifiers 190000 -#endif -#if !defined(__cpp_rvalue_references) -#define __cpp_rvalue_references 190000 -#endif -#if !defined(__cpp_static_assert) -#define __cpp_static_assert 190000 -#endif -#if !defined(__cpp_unicode_characters) -#define __cpp_unicode_characters 190000 -#endif -#if !defined(__cpp_unicode_literals) -#define __cpp_unicode_literals 190000 -#endif -#if !defined(__cpp_user_defined_literals) -#define __cpp_user_defined_literals 190000 -#endif -#if !defined(__cpp_variadic_templates) -#define __cpp_variadic_templates 190000 -#endif -#endif -#if __cplusplus >= 201402L -#if !defined(__cpp_aggregate_nsdmi) -#define __cpp_aggregate_nsdmi 190000 -#endif -#if !defined(__cpp_binary_literals) -#define __cpp_binary_literals 190000 -#endif -#if !defined(__cpp_decltype_auto) -#define __cpp_decltype_auto 190000 -#endif -#if !defined(__cpp_generic_lambdas) -#define __cpp_generic_lambdas 190000 -#endif -#if !defined(__cpp_init_captures) -#define __cpp_init_captures 190000 -#endif -#if !defined(__cpp_return_type_deduction) -#define __cpp_return_type_deduction 190000 -#endif -#if !defined(__cpp_sized_deallocation) -#define __cpp_sized_deallocation 190000 -#endif -#if !defined(__cpp_variable_templates) -#define __cpp_variable_templates 190000 -#endif -#endif -#if defined(_MSC_VER) && !defined(__clang__) -#if !defined(__cpp_exceptions) && defined(_CPPUNWIND) -#define __cpp_exceptions 190000 -#endif -#if !defined(__cpp_rtti) && defined(_CPPRTTI) -#define __cpp_rtti 190000 -#endif -#if !defined(__cpp_alias_templates) && _MSC_VER >= 1800 -#define __cpp_alias_templates 190000 -#endif -#if !defined(__cpp_attributes) -#define __cpp_attributes 190000 -#endif -#if !defined(__cpp_constexpr) && _MSC_FULL_VER >= 190023506 -#define __cpp_constexpr 190000 -#endif -#if !defined(__cpp_decltype) && _MSC_VER >= 1600 -#define __cpp_decltype 190000 -#endif -#if !defined(__cpp_delegating_constructors) && _MSC_VER >= 1800 -#define __cpp_delegating_constructors 190000 -#endif -#if !defined(__cpp_explicit_conversion) && _MSC_VER >= 1800 -#define __cpp_explicit_conversion 190000 -#endif -#if !defined(__cpp_inheriting_constructors) && _MSC_VER >= 1900 -#define __cpp_inheriting_constructors 190000 -#endif -#if !defined(__cpp_initializer_lists) && _MSC_VER >= 1900 -#define __cpp_initializer_lists 190000 -#endif -#if !defined(__cpp_lambdas) && _MSC_VER >= 1600 -#define __cpp_lambdas 190000 -#endif -#if !defined(__cpp_nsdmi) && _MSC_VER >= 1900 -#define __cpp_nsdmi 190000 -#endif -#if !defined(__cpp_range_based_for) && _MSC_VER >= 1700 -#define __cpp_range_based_for 190000 -#endif -#if !defined(__cpp_raw_strings) && _MSC_VER >= 1800 -#define __cpp_raw_strings 190000 -#endif -#if !defined(__cpp_ref_qualifiers) && _MSC_VER >= 1900 -#define __cpp_ref_qualifiers 190000 -#endif -#if !defined(__cpp_rvalue_references) && _MSC_VER >= 1600 -#define __cpp_rvalue_references 190000 -#endif -#if !defined(__cpp_static_assert) && _MSC_VER >= 1600 -#define __cpp_static_assert 190000 -#endif -#if !defined(__cpp_user_defined_literals) && _MSC_VER >= 1900 -#define __cpp_user_defined_literals 190000 -#endif -#if !defined(__cpp_variadic_templates) && _MSC_VER >= 1800 -#define __cpp_variadic_templates 190000 -#endif -#if !defined(__cpp_binary_literals) && _MSC_VER >= 1900 -#define __cpp_binary_literals 190000 -#endif -#if !defined(__cpp_decltype_auto) && _MSC_VER >= 1900 -#define __cpp_decltype_auto 190000 -#endif -#if !defined(__cpp_generic_lambdas) && _MSC_VER >= 1900 -#define __cpp_generic_lambdas 190000 -#endif -#if !defined(__cpp_init_captures) && _MSC_VER >= 1900 -#define __cpp_init_captures 190000 -#endif -#if !defined(__cpp_return_type_deduction) && _MSC_VER >= 1900 -#define __cpp_return_type_deduction 190000 -#endif -#if !defined(__cpp_sized_deallocation) && _MSC_VER >= 1900 -#define __cpp_sized_deallocation 190000 -#endif -#if !defined(__cpp_variable_templates) && _MSC_FULL_VER >= 190023506 -#define __cpp_variable_templates 190000 -#endif -#endif -#if(defined(__GNUC__) && !defined(__clang__)) -#define QUICKCPPLIB_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) -#if !defined(__cpp_exceptions) && defined(__EXCEPTIONS) -#define __cpp_exceptions 190000 -#endif -#if !defined(__cpp_rtti) && defined(__GXX_RTTI) -#define __cpp_rtti 190000 -#endif -#if defined(__GXX_EXPERIMENTAL_CXX0X__) -#if !defined(__cpp_alias_templates) && (QUICKCPPLIB_GCC >= 40700) -#define __cpp_alias_templates 190000 -#endif -#if !defined(__cpp_attributes) && (QUICKCPPLIB_GCC >= 40800) -#define __cpp_attributes 190000 -#endif -#if !defined(__cpp_constexpr) && (QUICKCPPLIB_GCC >= 40600) -#define __cpp_constexpr 190000 -#endif -#if !defined(__cpp_decltype) && (QUICKCPPLIB_GCC >= 40300) -#define __cpp_decltype 190000 -#endif -#if !defined(__cpp_delegating_constructors) && (QUICKCPPLIB_GCC >= 40700) -#define __cpp_delegating_constructors 190000 -#endif -#if !defined(__cpp_explicit_conversion) && (QUICKCPPLIB_GCC >= 40500) -#define __cpp_explicit_conversion 190000 -#endif -#if !defined(__cpp_inheriting_constructors) && (QUICKCPPLIB_GCC >= 40800) -#define __cpp_inheriting_constructors 190000 -#endif -#if !defined(__cpp_initializer_lists) && (QUICKCPPLIB_GCC >= 40800) -#define __cpp_initializer_lists 190000 -#endif -#if !defined(__cpp_lambdas) && (QUICKCPPLIB_GCC >= 40500) -#define __cpp_lambdas 190000 -#endif -#if !defined(__cpp_nsdmi) && (QUICKCPPLIB_GCC >= 40700) -#define __cpp_nsdmi 190000 -#endif -#if !defined(__cpp_range_based_for) && (QUICKCPPLIB_GCC >= 40600) -#define __cpp_range_based_for 190000 -#endif -#if !defined(__cpp_raw_strings) && (QUICKCPPLIB_GCC >= 40500) -#define __cpp_raw_strings 190000 -#endif -#if !defined(__cpp_ref_qualifiers) && (QUICKCPPLIB_GCC >= 40801) -#define __cpp_ref_qualifiers 190000 -#endif -#if !defined(__cpp_rvalue_references) && defined(__cpp_rvalue_reference) -#define __cpp_rvalue_references __cpp_rvalue_reference -#endif -#if !defined(__cpp_static_assert) && (QUICKCPPLIB_GCC >= 40300) -#define __cpp_static_assert 190000 -#endif -#if !defined(__cpp_unicode_characters) && (QUICKCPPLIB_GCC >= 40500) -#define __cpp_unicode_characters 190000 -#endif -#if !defined(__cpp_unicode_literals) && (QUICKCPPLIB_GCC >= 40500) -#define __cpp_unicode_literals 190000 -#endif -#if !defined(__cpp_user_defined_literals) && (QUICKCPPLIB_GCC >= 40700) -#define __cpp_user_defined_literals 190000 -#endif -#if !defined(__cpp_variadic_templates) && (QUICKCPPLIB_GCC >= 40400) -#define __cpp_variadic_templates 190000 -#endif -#endif -#endif -#if defined(__clang__) -#define QUICKCPPLIB_CLANG (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) -#if !defined(__cpp_exceptions) && (defined(__EXCEPTIONS) || defined(_CPPUNWIND)) -#define __cpp_exceptions 190000 -#endif -#if !defined(__cpp_rtti) && (defined(__GXX_RTTI) || defined(_CPPRTTI)) -#define __cpp_rtti 190000 -#endif -#if defined(__GXX_EXPERIMENTAL_CXX0X__) -#if !defined(__cpp_alias_templates) && (QUICKCPPLIB_CLANG >= 30000) -#define __cpp_alias_templates 190000 -#endif -#if !defined(__cpp_attributes) && (QUICKCPPLIB_CLANG >= 30300) -#define __cpp_attributes 190000 -#endif -#if !defined(__cpp_constexpr) && (QUICKCPPLIB_CLANG >= 30100) -#define __cpp_constexpr 190000 -#endif -#if !defined(__cpp_decltype) && (QUICKCPPLIB_CLANG >= 20900) -#define __cpp_decltype 190000 -#endif -#if !defined(__cpp_delegating_constructors) && (QUICKCPPLIB_CLANG >= 30000) -#define __cpp_delegating_constructors 190000 -#endif -#if !defined(__cpp_explicit_conversion) && (QUICKCPPLIB_CLANG >= 30000) -#define __cpp_explicit_conversion 190000 -#endif -#if !defined(__cpp_inheriting_constructors) && (QUICKCPPLIB_CLANG >= 30300) -#define __cpp_inheriting_constructors 190000 -#endif -#if !defined(__cpp_initializer_lists) && (QUICKCPPLIB_CLANG >= 30100) -#define __cpp_initializer_lists 190000 -#endif -#if !defined(__cpp_lambdas) && (QUICKCPPLIB_CLANG >= 30100) -#define __cpp_lambdas 190000 -#endif -#if !defined(__cpp_nsdmi) && (QUICKCPPLIB_CLANG >= 30000) -#define __cpp_nsdmi 190000 -#endif -#if !defined(__cpp_range_based_for) && (QUICKCPPLIB_CLANG >= 30000) -#define __cpp_range_based_for 190000 -#endif -#if !defined(__cpp_raw_strings) && defined(__cpp_raw_string_literals) -#define __cpp_raw_strings __cpp_raw_string_literals -#endif -#if !defined(__cpp_raw_strings) && (QUICKCPPLIB_CLANG >= 30000) -#define __cpp_raw_strings 190000 -#endif -#if !defined(__cpp_ref_qualifiers) && (QUICKCPPLIB_CLANG >= 20900) -#define __cpp_ref_qualifiers 190000 -#endif -#if !defined(__cpp_rvalue_references) && defined(__cpp_rvalue_reference) -#define __cpp_rvalue_references __cpp_rvalue_reference -#endif -#if !defined(__cpp_rvalue_references) && (QUICKCPPLIB_CLANG >= 20900) -#define __cpp_rvalue_references 190000 -#endif -#if !defined(__cpp_static_assert) && (QUICKCPPLIB_CLANG >= 20900) -#define __cpp_static_assert 190000 -#endif -#if !defined(__cpp_unicode_characters) && (QUICKCPPLIB_CLANG >= 30000) -#define __cpp_unicode_characters 190000 -#endif -#if !defined(__cpp_unicode_literals) && (QUICKCPPLIB_CLANG >= 30000) -#define __cpp_unicode_literals 190000 -#endif -#if !defined(__cpp_user_defined_literals) && defined(__cpp_user_literals) -#define __cpp_user_defined_literals __cpp_user_literals -#endif -#if !defined(__cpp_user_defined_literals) && (QUICKCPPLIB_CLANG >= 30100) -#define __cpp_user_defined_literals 190000 -#endif -#if !defined(__cpp_variadic_templates) && (QUICKCPPLIB_CLANG >= 20900) -#define __cpp_variadic_templates 190000 -#endif -#endif -#endif -#endif -#define cfg_HAS_CONSTEXPR14 (__cpp_constexpr >= 201304) -#if cfg_HAS_CONSTEXPR14 -#define cfg_constexpr14 constexpr -#else -#define cfg_constexpr14 -#endif -#if cfg_HAS_CONSTEXPR14 && defined(__clang__) -#define cfg_HAS_FULL_FEATURED_CONSTEXPR14 1 -#else -#define cfg_HAS_FULL_FEATURED_CONSTEXPR14 0 -#endif -#endif -#ifndef CX_FUNCTIONAL_HPP -#define CX_FUNCTIONAL_HPP -#include -namespace cx -{ -template -struct less -{ -constexpr auto operator()(const T& lhs, const T& rhs) const -> decltype(lhs < rhs) -{ -return lhs < rhs; -} -}; -template <> -struct less -{ -template -constexpr auto operator()(T&& lhs, U&& rhs) const --> decltype(std::forward(lhs) < std::forward(rhs)) -{ -return std::forward(lhs) < std::forward(rhs); -} -}; -template -struct equal_to -{ -constexpr auto operator()(const T& lhs, const T& rhs) const -> decltype(lhs == rhs) -{ -return lhs == rhs; -} -}; -template <> -struct equal_to -{ -template -constexpr auto operator()(T&& lhs, U&& rhs) const --> decltype(std::forward(lhs) == std::forward(rhs)) -{ -return std::forward(lhs) == std::forward(rhs); -} -}; -} -#endif -#ifndef CX_ITERATOR_HPP -#define CX_ITERATOR_HPP -#include -#include -namespace cx -{ -template -constexpr It next(It it) -{ -return it + 1; -} -template -constexpr It prev(It it) -{ -return it - 1; -} -template -constexpr auto size(const C& c) -> decltype(c.size()) -{ -return c.size(); -} -template -constexpr std::size_t size(const T (&)[N]) -{ -return N; -} -template -constexpr auto empty(const C& c) -> decltype(c.empty()) -{ -return c.empty(); -} -template -constexpr bool empty(const T (&)[N]) -{ -return false; -} -template -constexpr bool empty(std::initializer_list il) -{ -return il.size() == 0; -} -template -constexpr auto begin(const C& c) -> decltype(c.begin()) -{ -return c.begin(); -} -template -constexpr auto begin(C& c) -> decltype(c.begin()) -{ -return c.begin(); -} -template -constexpr T* begin(T (&array)[N]) -{ -return &array[0]; -} -template -constexpr const E* begin(std::initializer_list il) -{ -return il.begin(); -} -template -constexpr auto cbegin(const C& c) -> decltype(cx::begin(c)) -{ -return cx::begin(c); -} -template -constexpr auto end(const C& c) -> decltype(c.end()) -{ -return c.end(); -} -template -constexpr auto end(C& c) -> decltype(c.end()) -{ -return c.end(); -} -template -constexpr T* end(T (&array)[N]) -{ -return &array[N]; -} -template -constexpr const E* end(std::initializer_list il) -{ -return il.end(); -} -template -constexpr auto cend(const C& c) -> decltype(cx::end(c)) -{ -return cx::end(c); -} -} -#endif -#ifndef WILDCARDS_UTILITY_HPP -#define WILDCARDS_UTILITY_HPP -#include -#include -namespace wildcards -{ -template -struct const_iterator -{ -using type = typename std::remove_cv< -typename std::remove_reference()))>::type>::type; -}; -template -using const_iterator_t = typename const_iterator::type; -template -struct iterator -{ -using type = typename std::remove_cv< -typename std::remove_reference()))>::type>::type; -}; -template -using iterator_t = typename iterator::type; -template -struct iterated_item -{ -using type = typename std::remove_cv< -typename std::remove_reference())>::type>::type; -}; -template -using iterated_item_t = typename iterated_item::type; -template -struct container_item -{ -using type = typename std::remove_cv< -typename std::remove_reference()))>::type>::type; -}; -template -using container_item_t = typename container_item::type; -} -#endif -namespace wildcards -{ -template -struct full_match_result -{ -bool res; -SequenceIterator s, send, s1; -PatternIterator p, pend, p1; -constexpr operator bool() const -{ -return res; -} -}; -namespace detail -{ -template -struct match_result -{ -bool res; -SequenceIterator s; -PatternIterator p; -constexpr operator bool() const -{ -return res; -} -}; -template -constexpr match_result make_match_result(bool res, -SequenceIterator s, -PatternIterator p) -{ -return {std::move(res), std::move(s), std::move(p)}; -} -template -constexpr full_match_result make_full_match_result( -SequenceIterator s, SequenceIterator send, PatternIterator p, PatternIterator pend, -match_result mr) -{ -return {std::move(mr.res), std::move(s), std::move(send), std::move(mr.s), -std::move(p), std::move(pend), std::move(mr.p)}; -} -#if !cfg_HAS_FULL_FEATURED_CONSTEXPR14 -constexpr bool throw_invalid_argument(const char* what_arg) -{ -return what_arg == nullptr ? false : throw std::invalid_argument(what_arg); -} -template -constexpr T throw_invalid_argument(T t, const char* what_arg) -{ -return what_arg == nullptr ? t : throw std::invalid_argument(what_arg); -} -constexpr bool throw_logic_error(const char* what_arg) -{ -return what_arg == nullptr ? false : throw std::logic_error(what_arg); -} -template -constexpr T throw_logic_error(T t, const char* what_arg) -{ -return what_arg == nullptr ? t : throw std::logic_error(what_arg); -} -#endif -enum class is_set_state -{ -open, -not_or_first, -first, -next -}; -template -constexpr bool is_set( -PatternIterator p, PatternIterator pend, -const cards>& c = cards>(), -is_set_state state = is_set_state::open) -{ -#if cfg_HAS_CONSTEXPR14 -if (!c.set_enabled) -{ -return false; -} -while (p != pend) -{ -switch (state) -{ -case is_set_state::open: -if (*p != c.set_open) -{ -return false; -} -state = is_set_state::not_or_first; -break; -case is_set_state::not_or_first: -if (*p == c.set_not) -{ -state = is_set_state::first; -} -else -{ -state = is_set_state::next; -} -break; -case is_set_state::first: -state = is_set_state::next; -break; -case is_set_state::next: -if (*p == c.set_close) -{ -return true; -} -break; -default: -#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 -throw std::logic_error( -"The program execution should never end up here throwing this exception"); -#else -return throw_logic_error( -"The program execution should never end up here throwing this exception"); -#endif -} -p = cx::next(p); -} -return false; -#else -return c.set_enabled && p != pend && -(state == is_set_state::open -? *p == c.set_open && is_set(cx::next(p), pend, c, is_set_state::not_or_first) -: -state == is_set_state::not_or_first -? *p == c.set_not ? is_set(cx::next(p), pend, c, is_set_state::first) -: is_set(cx::next(p), pend, c, is_set_state::next) -: state == is_set_state::first -? is_set(cx::next(p), pend, c, is_set_state::next) -: state == is_set_state::next -? *p == c.set_close || -is_set(cx::next(p), pend, c, is_set_state::next) -: throw std::logic_error("The program execution should never end up " -"here throwing this exception")); -#endif -} -enum class set_end_state -{ -open, -not_or_first, -first, -next -}; -template -constexpr PatternIterator set_end( -PatternIterator p, PatternIterator pend, -const cards>& c = cards>(), -set_end_state state = set_end_state::open) -{ -#if cfg_HAS_CONSTEXPR14 -if (!c.set_enabled) -{ -#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 -throw std::invalid_argument("The use of sets is disabled"); -#else -return throw_invalid_argument(p, "The use of sets is disabled"); -#endif -} -while (p != pend) -{ -switch (state) -{ -case set_end_state::open: -if (*p != c.set_open) -{ -#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 -throw std::invalid_argument("The given pattern is not a valid set"); -#else -return throw_invalid_argument(p, "The given pattern is not a valid set"); -#endif -} -state = set_end_state::not_or_first; -break; -case set_end_state::not_or_first: -if (*p == c.set_not) -{ -state = set_end_state::first; -} -else -{ -state = set_end_state::next; -} -break; -case set_end_state::first: -state = set_end_state::next; -break; -case set_end_state::next: -if (*p == c.set_close) -{ -return cx::next(p); -} -break; -default: -#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 -throw std::logic_error( -"The program execution should never end up here throwing this exception"); -#else -return throw_logic_error( -p, "The program execution should never end up here throwing this exception"); -#endif -} -p = cx::next(p); -} -#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 -throw std::invalid_argument("The given pattern is not a valid set"); -#else -return throw_invalid_argument(p, "The given pattern is not a valid set"); -#endif -#else -return !c.set_enabled -? throw std::invalid_argument("The use of sets is disabled") -: p == pend -? throw std::invalid_argument("The given pattern is not a valid set") -: -state == set_end_state::open -? *p == c.set_open -? set_end(cx::next(p), pend, c, set_end_state::not_or_first) -: throw std::invalid_argument("The given pattern is not a valid set") -: -state == set_end_state::not_or_first -? *p == c.set_not ? set_end(cx::next(p), pend, c, set_end_state::first) -: set_end(cx::next(p), pend, c, set_end_state::next) -: state == set_end_state::first -? set_end(cx::next(p), pend, c, set_end_state::next) -: state == set_end_state::next -? *p == c.set_close -? cx::next(p) -: set_end(cx::next(p), pend, c, set_end_state::next) -: throw std::logic_error( -"The program execution should never end up " -"here throwing this exception"); -#endif -} -enum class match_set_state -{ -open, -not_or_first_in, -first_out, -next_in, -next_out -}; -template > -constexpr match_result match_set( -SequenceIterator s, SequenceIterator send, PatternIterator p, PatternIterator pend, -const cards>& c = cards>(), -const EqualTo& equal_to = EqualTo(), match_set_state state = match_set_state::open) -{ -#if cfg_HAS_CONSTEXPR14 -if (!c.set_enabled) -{ -#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 -throw std::invalid_argument("The use of sets is disabled"); -#else -return throw_invalid_argument(make_match_result(false, s, p), "The use of sets is disabled"); -#endif -} -while (p != pend) -{ -switch (state) -{ -case match_set_state::open: -if (*p != c.set_open) -{ -#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 -throw std::invalid_argument("The given pattern is not a valid set"); -#else -return throw_invalid_argument(make_match_result(false, s, p), -"The given pattern is not a valid set"); -#endif -} -state = match_set_state::not_or_first_in; -break; -case match_set_state::not_or_first_in: -if (*p == c.set_not) -{ -state = match_set_state::first_out; -} -else -{ -if (s == send) -{ -return make_match_result(false, s, p); -} -if (equal_to(*s, *p)) -{ -return make_match_result(true, s, p); -} -state = match_set_state::next_in; -} -break; -case match_set_state::first_out: -if (s == send || equal_to(*s, *p)) -{ -return make_match_result(false, s, p); -} -state = match_set_state::next_out; -break; -case match_set_state::next_in: -if (*p == c.set_close || s == send) -{ -return make_match_result(false, s, p); -} -if (equal_to(*s, *p)) -{ -return make_match_result(true, s, p); -} -break; -case match_set_state::next_out: -if (*p == c.set_close) -{ -return make_match_result(true, s, p); -} -if (s == send || equal_to(*s, *p)) -{ -return make_match_result(false, s, p); -} -break; -default: -#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 -throw std::logic_error( -"The program execution should never end up here throwing this exception"); -#else -return throw_logic_error( -make_match_result(false, s, p), -"The program execution should never end up here throwing this exception"); -#endif -} -p = cx::next(p); -} -#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 -throw std::invalid_argument("The given pattern is not a valid set"); -#else -return throw_invalid_argument(make_match_result(false, s, p), -"The given pattern is not a valid set"); -#endif -#else -return !c.set_enabled -? throw std::invalid_argument("The use of sets is disabled") -: p == pend -? throw std::invalid_argument("The given pattern is not a valid set") -: state == match_set_state::open -? *p == c.set_open -? match_set(s, send, cx::next(p), pend, c, equal_to, -match_set_state::not_or_first_in) -: -throw std::invalid_argument("The given pattern is not a valid set") -: -state == match_set_state::not_or_first_in -? *p == c.set_not -? match_set(s, send, cx::next(p), pend, c, equal_to, -match_set_state::first_out) -: -s == send ? make_match_result(false, s, p) -: equal_to(*s, *p) -? make_match_result(true, s, p) -: match_set(s, send, cx::next(p), pend, c, -equal_to, match_set_state::next_in) -: -state == match_set_state::first_out -? s == send || equal_to(*s, *p) -? make_match_result(false, s, p) -: match_set(s, send, cx::next(p), pend, c, equal_to, -match_set_state::next_out) -: -state == match_set_state::next_in -? *p == c.set_close || s == send -? make_match_result(false, s, p) -: equal_to(*s, *p) ? make_match_result(true, s, p) -: match_set(s, send, cx::next(p), -pend, c, equal_to, state) -: -state == match_set_state::next_out -? *p == c.set_close -? make_match_result(true, s, p) -: s == send || equal_to(*s, *p) -? make_match_result(false, s, p) -: match_set(s, send, cx::next(p), pend, c, -equal_to, state) -: throw std::logic_error( -"The program execution should never end up " -"here " -"throwing this exception"); -#endif -} -enum class is_alt_state -{ -open, -next, -escape -}; -template -constexpr bool is_alt( -PatternIterator p, PatternIterator pend, -const cards>& c = cards>(), -is_alt_state state = is_alt_state::open, int depth = 0) -{ -#if cfg_HAS_CONSTEXPR14 -if (!c.alt_enabled) -{ -return false; -} -while (p != pend) -{ -switch (state) -{ -case is_alt_state::open: -if (*p != c.alt_open) -{ -return false; -} -state = is_alt_state::next; -++depth; -break; -case is_alt_state::next: -if (*p == c.escape) -{ -state = is_alt_state::escape; -} -else if (c.set_enabled && *p == c.set_open && -is_set(cx::next(p), pend, c, is_set_state::not_or_first)) -{ -p = cx::prev(set_end(cx::next(p), pend, c, set_end_state::not_or_first)); -} -else if (*p == c.alt_open) -{ -++depth; -} -else if (*p == c.alt_close) -{ ---depth; -if (depth == 0) -{ -return true; -} -} -break; -case is_alt_state::escape: -state = is_alt_state::next; -break; -default: - -throw std::logic_error( -"The program execution should never end up here throwing this exception"); - -} -p = cx::next(p); -} -return false; -#else -return c.alt_enabled && p != pend && -(state == is_alt_state::open -? *p == c.alt_open && is_alt(cx::next(p), pend, c, is_alt_state::next, depth + 1) -: state == is_alt_state::next -? *p == c.escape -? is_alt(cx::next(p), pend, c, is_alt_state::escape, depth) -: c.set_enabled && *p == c.set_open && -is_set(cx::next(p), pend, c, is_set_state::not_or_first) -? is_alt(set_end(cx::next(p), pend, c, set_end_state::not_or_first), -pend, c, state, depth) -: *p == c.alt_open -? is_alt(cx::next(p), pend, c, state, depth + 1) -: *p == c.alt_close -? depth == 1 || -is_alt(cx::next(p), pend, c, state, depth - 1) -: is_alt(cx::next(p), pend, c, state, depth) -: -state == is_alt_state::escape -? is_alt(cx::next(p), pend, c, is_alt_state::next, depth) -: throw std::logic_error( -"The program execution should never end up here throwing this " -"exception")); -#endif -} -enum class alt_end_state -{ -open, -next, -escape -}; -template -constexpr PatternIterator alt_end( -PatternIterator p, PatternIterator pend, -const cards>& c = cards>(), -alt_end_state state = alt_end_state::open, int depth = 0) -{ -#if cfg_HAS_CONSTEXPR14 -if (!c.alt_enabled) -{ -#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 -throw std::invalid_argument("The use of alternatives is disabled"); -#else -return throw_invalid_argument(p, "The use of alternatives is disabled"); -#endif -} -while (p != pend) -{ -switch (state) -{ -case alt_end_state::open: -if (*p != c.alt_open) -{ -#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 -throw std::invalid_argument("The given pattern is not a valid alternative"); -#else -return throw_invalid_argument(p, "The given pattern is not a valid alternative"); -#endif -} -state = alt_end_state::next; -++depth; -break; -case alt_end_state::next: -if (*p == c.escape) -{ -state = alt_end_state::escape; -} -else if (c.set_enabled && *p == c.set_open && -is_set(cx::next(p), pend, c, is_set_state::not_or_first)) -{ -p = cx::prev(set_end(cx::next(p), pend, c, set_end_state::not_or_first)); -} -else if (*p == c.alt_open) -{ -++depth; -} -else if (*p == c.alt_close) -{ ---depth; -if (depth == 0) -{ -return cx::next(p); -} -} -break; -case alt_end_state::escape: -state = alt_end_state::next; -break; -default: -#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 -throw std::logic_error( -"The program execution should never end up here throwing this exception"); -#else -return throw_logic_error( -p, "The program execution should never end up here throwing this exception"); -#endif -} -p = cx::next(p); -} -#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 -throw std::invalid_argument("The given pattern is not a valid alternative"); -#else -return throw_invalid_argument(p, "The given pattern is not a valid alternative"); -#endif -#else -return !c.alt_enabled -? throw std::invalid_argument("The use of alternatives is disabled") -: p == pend -? throw std::invalid_argument("The given pattern is not a valid alternative") -: state == alt_end_state::open -? *p == c.alt_open -? alt_end(cx::next(p), pend, c, alt_end_state::next, depth + 1) -: throw std::invalid_argument( -"The given pattern is not a valid alternative") -: state == alt_end_state::next -? *p == c.escape -? alt_end(cx::next(p), pend, c, alt_end_state::escape, depth) -: c.set_enabled && *p == c.set_open && -is_set(cx::next(p), pend, c, -is_set_state::not_or_first) -? alt_end(set_end(cx::next(p), pend, c, -set_end_state::not_or_first), -pend, c, state, depth) -: *p == c.alt_open -? alt_end(cx::next(p), pend, c, state, depth + 1) -: *p == c.alt_close -? depth == 1 ? cx::next(p) -: alt_end(cx::next(p), pend, c, -state, depth - 1) -: alt_end(cx::next(p), pend, c, state, depth) -: -state == alt_end_state::escape -? alt_end(cx::next(p), pend, c, alt_end_state::next, depth) -: throw std::logic_error( -"The program execution should never end up here throwing " -"this " -"exception"); -#endif -} -enum class alt_sub_end_state -{ -next, -escape -}; -template -constexpr PatternIterator alt_sub_end( -PatternIterator p, PatternIterator pend, -const cards>& c = cards>(), -alt_sub_end_state state = alt_sub_end_state::next, int depth = 1) -{ -#if cfg_HAS_CONSTEXPR14 -if (!c.alt_enabled) -{ -#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 -throw std::invalid_argument("The use of alternatives is disabled"); -#else -return throw_invalid_argument(p, "The use of alternatives is disabled"); -#endif -} -while (p != pend) -{ -switch (state) -{ -case alt_sub_end_state::next: -if (*p == c.escape) -{ -state = alt_sub_end_state::escape; -} -else if (c.set_enabled && *p == c.set_open && -is_set(cx::next(p), pend, c, is_set_state::not_or_first)) -{ -p = cx::prev(set_end(cx::next(p), pend, c, set_end_state::not_or_first)); -} -else if (*p == c.alt_open) -{ -++depth; -} -else if (*p == c.alt_close) -{ ---depth; -if (depth == 0) -{ -return p; -} -} -else if (*p == c.alt_or) -{ -if (depth == 1) -{ -return p; -} -} -break; -case alt_sub_end_state::escape: -state = alt_sub_end_state::next; -break; -default: -#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 -throw std::logic_error( -"The program execution should never end up here throwing this exception"); -#else -return throw_logic_error( -p, "The program execution should never end up here throwing this exception"); -#endif -} -p = cx::next(p); -} -#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 -throw std::invalid_argument("The given pattern is not a valid alternative"); -#else -return throw_invalid_argument(p, "The given pattern is not a valid alternative"); -#endif -#else -return !c.alt_enabled -? throw std::invalid_argument("The use of alternatives is disabled") -: p == pend -? throw std::invalid_argument("The given pattern is not a valid alternative") -: state == alt_sub_end_state::next -? *p == c.escape -? alt_sub_end(cx::next(p), pend, c, alt_sub_end_state::escape, depth) -: c.set_enabled && *p == c.set_open && -is_set(cx::next(p), pend, c, is_set_state::not_or_first) -? alt_sub_end(set_end(cx::next(p), pend, c, -set_end_state::not_or_first), -pend, c, state, depth) -: *p == c.alt_open -? alt_sub_end(cx::next(p), pend, c, state, depth + 1) -: *p == c.alt_close -? depth == 1 ? p : alt_sub_end(cx::next(p), pend, -c, state, depth - 1) -: *p == c.alt_or -? depth == 1 ? p -: alt_sub_end(cx::next(p), pend, -c, state, depth) -: alt_sub_end(cx::next(p), pend, c, state, -depth) -: -state == alt_sub_end_state::escape -? alt_sub_end(cx::next(p), pend, c, alt_sub_end_state::next, depth) -: throw std::logic_error( -"The program execution should never end up here throwing " -"this " -"exception"); -#endif -} -template > -constexpr match_result match( -SequenceIterator s, SequenceIterator send, PatternIterator p, PatternIterator pend, -const cards>& c = cards>(), -const EqualTo& equal_to = EqualTo(), bool partial = false, bool escape = false); -template > -constexpr match_result match_alt( -SequenceIterator s, SequenceIterator send, PatternIterator p1, PatternIterator p1end, -PatternIterator p2, PatternIterator p2end, -const cards>& c = cards>(), -const EqualTo& equal_to = EqualTo(), bool partial = false) -{ -#if cfg_HAS_CONSTEXPR14 -auto result1 = match(s, send, p1, p1end, c, equal_to, true); -if (result1) -{ -auto result2 = match(result1.s, send, p2, p2end, c, equal_to, partial); -if (result2) -{ -return result2; -} -} -p1 = cx::next(p1end); -if (p1 == p2) -{ -return make_match_result(false, s, p1end); -} -return match_alt(s, send, p1, alt_sub_end(p1, p2, c), p2, p2end, c, equal_to, partial); -#else -return match(s, send, p1, p1end, c, equal_to, true) && -match(match(s, send, p1, p1end, c, equal_to, true).s, send, p2, p2end, c, equal_to, -partial) -? match(match(s, send, p1, p1end, c, equal_to, true).s, send, p2, p2end, c, equal_to, -partial) -: cx::next(p1end) == p2 -? make_match_result(false, s, p1end) -: match_alt(s, send, cx::next(p1end), alt_sub_end(cx::next(p1end), p2, c), p2, -p2end, c, equal_to, partial); -#endif -} -template -constexpr match_result match( -SequenceIterator s, SequenceIterator send, PatternIterator p, PatternIterator pend, -const cards>& c, const EqualTo& equal_to, bool partial, -bool escape) -{ -#if cfg_HAS_CONSTEXPR14 -if (p == pend) -{ -return make_match_result(partial || s == send, s, p); -} -if (escape) -{ -if (s == send || !equal_to(*s, *p)) -{ -return make_match_result(false, s, p); -} -return match(cx::next(s), send, cx::next(p), pend, c, equal_to, partial); -} -if (*p == c.anything) -{ -auto result = match(s, send, cx::next(p), pend, c, equal_to, partial); -if (result) -{ -return result; -} -if (s == send) -{ -return make_match_result(false, s, p); -} -return match(cx::next(s), send, p, pend, c, equal_to, partial); -} -if (*p == c.single) -{ -if (s == send) -{ -return make_match_result(false, s, p); -} -return match(cx::next(s), send, cx::next(p), pend, c, equal_to, partial); -} -if (*p == c.escape) -{ -return match(s, send, cx::next(p), pend, c, equal_to, partial, true); -} -if (c.set_enabled && *p == c.set_open && is_set(cx::next(p), pend, c, is_set_state::not_or_first)) -{ -auto result = -match_set(s, send, cx::next(p), pend, c, equal_to, match_set_state::not_or_first_in); -if (!result) -{ -return result; -} -return match(cx::next(s), send, set_end(cx::next(p), pend, c, set_end_state::not_or_first), -pend, c, equal_to, partial); -} -if (c.alt_enabled && *p == c.alt_open && is_alt(cx::next(p), pend, c, is_alt_state::next, 1)) -{ -auto p_alt_end = alt_end(cx::next(p), pend, c, alt_end_state::next, 1); -return match_alt(s, send, cx::next(p), alt_sub_end(cx::next(p), p_alt_end, c), p_alt_end, pend, -c, equal_to, partial); -} -if (s == send || !equal_to(*s, *p)) -{ -return make_match_result(false, s, p); -} -return match(cx::next(s), send, cx::next(p), pend, c, equal_to, partial); -#else -return p == pend -? make_match_result(partial || s == send, s, p) -: escape -? s == send || !equal_to(*s, *p) -? make_match_result(false, s, p) -: match(cx::next(s), send, cx::next(p), pend, c, equal_to, partial) -: *p == c.anything -? match(s, send, cx::next(p), pend, c, equal_to, partial) -? match(s, send, cx::next(p), pend, c, equal_to, partial) -: s == send ? make_match_result(false, s, p) -: match(cx::next(s), send, p, pend, c, equal_to, partial) -: *p == c.single -? s == send ? make_match_result(false, s, p) -: match(cx::next(s), send, cx::next(p), pend, c, -equal_to, partial) -: *p == c.escape -? match(s, send, cx::next(p), pend, c, equal_to, partial, true) -: c.set_enabled && *p == c.set_open && -is_set(cx::next(p), pend, c, -is_set_state::not_or_first) -? !match_set(s, send, cx::next(p), pend, c, equal_to, -match_set_state::not_or_first_in) -? match_set(s, send, cx::next(p), pend, c, -equal_to, -match_set_state::not_or_first_in) -: match(cx::next(s), send, -set_end(cx::next(p), pend, c, -set_end_state::not_or_first), -pend, c, equal_to, partial) -: c.alt_enabled && *p == c.alt_open && -is_alt(cx::next(p), pend, c, -is_alt_state::next, 1) -? match_alt( -s, send, cx::next(p), -alt_sub_end(cx::next(p), -alt_end(cx::next(p), pend, c, -alt_end_state::next, 1), -c), -alt_end(cx::next(p), pend, c, -alt_end_state::next, 1), -pend, c, equal_to, partial) -: s == send || !equal_to(*s, *p) -? make_match_result(false, s, p) -: match(cx::next(s), send, cx::next(p), pend, -c, equal_to, partial); -#endif -} -} -template > -constexpr full_match_result, const_iterator_t> match( -Sequence&& sequence, Pattern&& pattern, -const cards>& c = cards>(), -const EqualTo& equal_to = EqualTo()) -{ -return detail::make_full_match_result( -cx::cbegin(sequence), cx::cend(sequence), cx::cbegin(pattern), cx::cend(pattern), -detail::match(cx::cbegin(sequence), cx::cend(std::forward(sequence)), -cx::cbegin(pattern), cx::cend(std::forward(pattern)), c, equal_to)); -} -template , -typename = typename std::enable_if::value>::type> -constexpr full_match_result, const_iterator_t> match( -Sequence&& sequence, Pattern&& pattern, const EqualTo& equal_to) -{ -return match(std::forward(sequence), std::forward(pattern), -cards>(), equal_to); -} -} -#endif -#ifndef WILDCARDS_MATCHER_HPP -#define WILDCARDS_MATCHER_HPP -#include -#include -#include -#ifndef CX_STRING_VIEW_HPP -#define CX_STRING_VIEW_HPP -#include -#include -#ifndef CX_ALGORITHM_HPP -#define CX_ALGORITHM_HPP -namespace cx -{ -template -constexpr bool equal(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2) -{ -#if cfg_HAS_CONSTEXPR14 -while (first1 != last1 && first2 != last2 && *first1 == *first2) -{ -++first1, ++first2; -} -return first1 == last1 && first2 == last2; -#else -return first1 != last1 && first2 != last2 && *first1 == *first2 -? equal(first1 + 1, last1, first2 + 1, last2) -: first1 == last1 && first2 == last2; -#endif -} -} -#endif -namespace cx -{ -template -class basic_string_view -{ -public: -using value_type = T; -constexpr basic_string_view() = default; -template -constexpr basic_string_view(const T (&str)[N]) : data_{&str[0]}, size_{N - 1} -{ -} -constexpr basic_string_view(const T* str, std::size_t s) : data_{str}, size_{s} -{ -} -constexpr const T* data() const -{ -return data_; -} -constexpr std::size_t size() const -{ -return size_; -} -constexpr bool empty() const -{ -return size() == 0; -} -constexpr const T* begin() const -{ -return data_; -} -constexpr const T* cbegin() const -{ -return begin(); -} -constexpr const T* end() const -{ -return data_ + size_; -} -constexpr const T* cend() const -{ -return end(); -} -private: -const T* data_{nullptr}; -std::size_t size_{0}; -}; -template -constexpr bool operator==(const basic_string_view& lhs, const basic_string_view& rhs) -{ -return equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); -} -template -constexpr bool operator!=(const basic_string_view& lhs, const basic_string_view& rhs) -{ -return !(lhs == rhs); -} -template -std::basic_ostream& operator<<(std::basic_ostream& o, const basic_string_view& s) -{ -o << s.data(); -return o; -} -template -constexpr basic_string_view make_string_view(const T (&str)[N]) -{ -return {str, N - 1}; -} -template -constexpr basic_string_view make_string_view(const T* str, std::size_t s) -{ -return {str, s}; -} -using string_view = basic_string_view; -using u16string_view = basic_string_view; -using u32string_view = basic_string_view; -using wstring_view = basic_string_view; -namespace literals -{ -constexpr string_view operator"" _sv(const char* str, std::size_t s) -{ -return {str, s}; -} -constexpr u16string_view operator"" _sv(const char16_t* str, std::size_t s) -{ -return {str, s}; -} -constexpr u32string_view operator"" _sv(const char32_t* str, std::size_t s) -{ -return {str, s}; -} -constexpr wstring_view operator"" _sv(const wchar_t* str, std::size_t s) -{ -return {str, s}; -} -} -} -#endif -namespace wildcards -{ -template > -class matcher -{ -public: -constexpr explicit matcher(Pattern&& pattern, const cards>& c = -cards>(), -const EqualTo& equal_to = EqualTo()) -: p_{cx::cbegin(pattern)}, -pend_{cx::cend(std::forward(pattern))}, -c_{c}, -equal_to_{equal_to} -{ -} -constexpr matcher(Pattern&& pattern, const EqualTo& equal_to) -: p_{cx::cbegin(pattern)}, -pend_{cx::cend(std::forward(pattern))}, -c_{cards>()}, -equal_to_{equal_to} -{ -} -template -constexpr full_match_result, const_iterator_t> matches( -Sequence&& sequence) const -{ -return detail::make_full_match_result( -cx::cbegin(sequence), cx::cend(sequence), p_, pend_, -detail::match(cx::cbegin(sequence), cx::cend(std::forward(sequence)), p_, pend_, -c_, equal_to_)); -} -private: -const_iterator_t p_; -const_iterator_t pend_; -cards> c_; -EqualTo equal_to_; -}; -template > -constexpr matcher make_matcher( -Pattern&& pattern, -const cards>& c = cards>(), -const EqualTo& equal_to = EqualTo()) -{ -return matcher{std::forward(pattern), c, equal_to}; -} -template , -typename = typename std::enable_if::value>::type> -constexpr matcher make_matcher(Pattern&& pattern, const EqualTo& equal_to) -{ -return make_matcher(std::forward(pattern), cards>(), equal_to); -} -namespace literals -{ -constexpr auto operator"" _wc(const char* str, std::size_t s) --> decltype(make_matcher(cx::make_string_view(str, s + 1))) -{ -return make_matcher(cx::make_string_view(str, s + 1)); -} -constexpr auto operator"" _wc(const char16_t* str, std::size_t s) --> decltype(make_matcher(cx::make_string_view(str, s + 1))) -{ -return make_matcher(cx::make_string_view(str, s + 1)); -} -constexpr auto operator"" _wc(const char32_t* str, std::size_t s) --> decltype(make_matcher(cx::make_string_view(str, s + 1))) -{ -return make_matcher(cx::make_string_view(str, s + 1)); -} -constexpr auto operator"" _wc(const wchar_t* str, std::size_t s) --> decltype(make_matcher(cx::make_string_view(str, s + 1))) -{ -return make_matcher(cx::make_string_view(str, s + 1)); -} -} -} -#endif -#endif diff --git a/include/behaviortree_cpp/utils/wildcards.hpp b/include/behaviortree_cpp/utils/wildcards.hpp new file mode 100644 index 000000000..f4b6b40a6 --- /dev/null +++ b/include/behaviortree_cpp/utils/wildcards.hpp @@ -0,0 +1,61 @@ +#pragma once + +/** + * @file wildcards.hpp + * @brief Simple wildcard matching function supporting '*' and '?'. + * + * This file provides a function to match strings against patterns containing + * wildcard characters: + * - '*' matches any sequence of characters (including the empty sequence) + * - '?' matches any single character + * + * The implementation uses recursion with memoization to efficiently handle + * overlapping subproblems. + */ + +inline bool wildcards_match(std::string_view str, std::string_view pattern) +{ + const size_t n = str.size(); + const size_t m = pattern.size(); + + // Pre-allocate memo table: -1 = not computed, 0 = false, 1 = true + std::vector memo((n + 1) * (m + 1), -1); + + auto get_memo = [&](size_t i, size_t j) -> int8_t& { return memo[i * (m + 1) + j]; }; + + auto match = [&](auto& match_ref, size_t i, size_t j) -> bool { + if(j == m) + { + return i == n; + } + + int8_t& cached = get_memo(i, j); + if(cached != -1) + { + return cached == 1; + } + + bool result; + if(pattern[j] == '*') + { + result = match_ref(match_ref, i, j + 1); + if(!result && i < n) + { + result = match_ref(match_ref, i + 1, j); + } + } + else if(i < n && (pattern[j] == '?' || pattern[j] == str[i])) + { + result = match_ref(match_ref, i + 1, j + 1); + } + else + { + result = false; + } + + cached = result ? 1 : 0; + return result; + }; + + return match(match, 0, 0); +} \ No newline at end of file diff --git a/src/bt_factory.cpp b/src/bt_factory.cpp index 63f6652ef..01e01301a 100644 --- a/src/bt_factory.cpp +++ b/src/bt_factory.cpp @@ -13,15 +13,15 @@ #include #include "behaviortree_cpp/bt_factory.h" #include "behaviortree_cpp/utils/shared_library.h" +#include "behaviortree_cpp/utils/wildcards.hpp" #include "behaviortree_cpp/xml_parsing.h" -#include "wildcards/wildcards.hpp" namespace BT { bool WildcardMatch(std::string const& str, StringView filter) { - return wildcards::match(str, filter); + return wildcards_match(str, { filter.data(), filter.size() }); } struct BehaviorTreeFactory::PImpl @@ -242,7 +242,7 @@ std::unique_ptr BehaviorTreeFactory::instantiateTreeNode( bool substituted = false; for(const auto& [filter, rule] : _p->substitution_rules) { - if(filter == name || filter == ID || wildcards::match(config.path, filter)) + if(filter == name || filter == ID || wildcards_match(config.path, filter)) { // first case: the rule is simply a string with the name of the // node to create instead diff --git a/tests/gtest_match.cpp b/tests/gtest_match.cpp index 9078e08e3..aebea060d 100644 --- a/tests/gtest_match.cpp +++ b/tests/gtest_match.cpp @@ -1,14 +1,14 @@ #include -#include "wildcards/wildcards.hpp" +#include "behaviortree_cpp/utils/wildcards.hpp" TEST(Match, Match) { - ASSERT_TRUE(wildcards::match("prefix/suffix", "*/suffix")); - ASSERT_TRUE(wildcards::match("prefix/suffix", "prefix/*")); - ASSERT_TRUE(wildcards::match("prefix/suffix", "pre*fix")); + ASSERT_TRUE(wildcards_match("prefix/suffix", "*/suffix")); + ASSERT_TRUE(wildcards_match("prefix/suffix", "prefix/*")); + ASSERT_TRUE(wildcards_match("prefix/suffix", "pre*fix")); - ASSERT_FALSE(wildcards::match("prefix/suffix", "*/suff")); - ASSERT_FALSE(wildcards::match("prefix/suffix", "pre/*")); - ASSERT_FALSE(wildcards::match("prefix/suffix", "pre*fi")); - ASSERT_FALSE(wildcards::match("prefix/suffix", "re*fix")); + ASSERT_FALSE(wildcards_match("prefix/suffix", "*/suff")); + ASSERT_FALSE(wildcards_match("prefix/suffix", "pre/*")); + ASSERT_FALSE(wildcards_match("prefix/suffix", "pre*fi")); + ASSERT_FALSE(wildcards_match("prefix/suffix", "re*fix")); } From 3ea38fc6199d822510c0705679f3eeb0af4cb94c Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Tue, 30 Sep 2025 23:51:30 +0200 Subject: [PATCH 010/147] update cppzmq to 4.11.0 --- 3rdparty/cppzmq/README.md | 38 ++-- 3rdparty/cppzmq/zmq.hpp | 381 ++++++++++++++++++++++------------ 3rdparty/cppzmq/zmq_addon.hpp | 107 +++++++++- 3 files changed, 371 insertions(+), 155 deletions(-) diff --git a/3rdparty/cppzmq/README.md b/3rdparty/cppzmq/README.md index e2bea0b63..5d804d0c8 100644 --- a/3rdparty/cppzmq/README.md +++ b/3rdparty/cppzmq/README.md @@ -163,25 +163,27 @@ Build instructions Build steps: 1. Build [libzmq](https://github.com/zeromq/libzmq) via cmake. This does an out of source build and installs the build files - - download and unzip the lib, cd to directory - - mkdir build - - cd build - - cmake .. - - sudo make -j4 install + - `git clone https://github.com/zeromq/libzmq.git` + - `cd libzmq` + - `mkdir build` + - `cd build` + - `cmake ..` + - `sudo make -j4 install` 2. Build cppzmq via cmake. This does an out of source build and installs the build files - - download and unzip the lib, cd to directory - - mkdir build - - cd build - - cmake .. - - sudo make -j4 install - -3. Build cppzmq via [vcpkg](https://github.com/Microsoft/vcpkg/). This does an out of source build and installs the build files - - git clone https://github.com/Microsoft/vcpkg.git - - cd vcpkg - - ./bootstrap-vcpkg.sh # bootstrap-vcpkg.bat for Powershell - - ./vcpkg integrate install - - ./vcpkg install cppzmq + - `git clone https://github.com/zeromq/cppzmq.git` + - `cd cppzmq` + - `mkdir build` + - `cd build` + - `cmake ..` or `cmake -DCPPZMQ_BUILD_TESTS=OFF ..` to skip building tests + - `sudo make -j4 install` + +3. Alternatively, build cppzmq via [vcpkg](https://github.com/Microsoft/vcpkg/). This does an out of source build and installs the build files + - `git clone https://github.com/Microsoft/vcpkg.git` + - `cd vcpkg` + - `./bootstrap-vcpkg.sh` (bootstrap-vcpkg.bat for Powershell) + - `./vcpkg integrate install` + - `./vcpkg install cppzmq` Using this: @@ -193,4 +195,6 @@ cpp zmq (which will also include libzmq for you). #find cppzmq wrapper, installed by make of cppzmq find_package(cppzmq) target_link_libraries(*Your Project Name* cppzmq) +# Or use static library to link +target_link_libraries(*Your Project Name* cppzmq-static) ``` diff --git a/3rdparty/cppzmq/zmq.hpp b/3rdparty/cppzmq/zmq.hpp index 3fa484c6c..ad0509e89 100644 --- a/3rdparty/cppzmq/zmq.hpp +++ b/3rdparty/cppzmq/zmq.hpp @@ -108,6 +108,7 @@ #include #include +#include #include #include #include @@ -147,7 +148,7 @@ /* Version macros for compile-time API version detection */ #define CPPZMQ_VERSION_MAJOR 4 -#define CPPZMQ_VERSION_MINOR 10 +#define CPPZMQ_VERSION_MINOR 11 #define CPPZMQ_VERSION_PATCH 0 #define CPPZMQ_VERSION \ @@ -690,39 +691,40 @@ class message_t * Use to_string() or to_string_view() for * interpreting the message as a string. */ - std::string str() const + std::string str(size_t max_size = 1000) const { // Partly mutuated from the same method in zmq::multipart_t std::stringstream os; const unsigned char *msg_data = this->data(); - unsigned char byte; - size_t size = this->size(); + size_t size_to_print = (std::min)(this->size(), max_size); int is_ascii[2] = {0, 0}; + // Set is_ascii for the first character + if (size_to_print > 0) + is_ascii[0] = (*msg_data >= 32 && *msg_data < 127); os << "zmq::message_t [size " << std::dec << std::setw(3) - << std::setfill('0') << size << "] ("; - // Totally arbitrary - if (size >= 1000) { - os << "... too big to print)"; - } else { - while (size--) { - byte = *msg_data++; - - is_ascii[1] = (byte >= 32 && byte < 127); - if (is_ascii[1] != is_ascii[0]) - os << " "; // Separate text/non text - - if (is_ascii[1]) { - os << byte; - } else { - os << std::hex << std::uppercase << std::setw(2) - << std::setfill('0') << static_cast(byte); - } - is_ascii[0] = is_ascii[1]; + << std::setfill('0') << this->size() << "] ("; + while (size_to_print--) { + const unsigned char byte = *msg_data++; + + is_ascii[1] = (byte >= 32 && byte < 127); + if (is_ascii[1] != is_ascii[0]) + os << " "; // Separate text/non text + + if (is_ascii[1]) { + os << byte; + } else { + os << std::hex << std::uppercase << std::setw(2) << std::setfill('0') + << static_cast(byte); } - os << ")"; + is_ascii[0] = is_ascii[1]; } + // Elide the rest if the message is too large + if (max_size < this->size()) + os << "... too big to print)"; + else + os << ")"; return os.str(); } @@ -1363,19 +1365,19 @@ constexpr const_buffer str_buffer(const Char (&data)[N]) noexcept namespace literals { -constexpr const_buffer operator"" _zbuf(const char *str, size_t len) noexcept +constexpr const_buffer operator""_zbuf(const char *str, size_t len) noexcept { return const_buffer(str, len * sizeof(char)); } -constexpr const_buffer operator"" _zbuf(const wchar_t *str, size_t len) noexcept +constexpr const_buffer operator""_zbuf(const wchar_t *str, size_t len) noexcept { return const_buffer(str, len * sizeof(wchar_t)); } -constexpr const_buffer operator"" _zbuf(const char16_t *str, size_t len) noexcept +constexpr const_buffer operator""_zbuf(const char16_t *str, size_t len) noexcept { return const_buffer(str, len * sizeof(char16_t)); } -constexpr const_buffer operator"" _zbuf(const char32_t *str, size_t len) noexcept +constexpr const_buffer operator""_zbuf(const char32_t *str, size_t len) noexcept { return const_buffer(str, len * sizeof(char32_t)); } @@ -1461,6 +1463,9 @@ ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_BACKLOG, backlog, int); #ifdef ZMQ_BINDTODEVICE ZMQ_DEFINE_ARRAY_OPT_BINARY(ZMQ_BINDTODEVICE, bindtodevice); #endif +#ifdef ZMQ_BUSY_POLL +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_BUSY_POLL, busy_poll, int); +#endif #ifdef ZMQ_CONFLATE ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_CONFLATE, conflate, int); #endif @@ -1624,6 +1629,9 @@ ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ROUTER_MANDATORY, router_mandatory, int); #ifdef ZMQ_ROUTER_NOTIFY ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_ROUTER_NOTIFY, router_notify, int); #endif +#ifdef ZMQ_ROUTER_RAW +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_ROUTER_RAW, router_raw, int); +#endif #ifdef ZMQ_ROUTING_ID ZMQ_DEFINE_ARRAY_OPT_BINARY(ZMQ_ROUTING_ID, routing_id); #endif @@ -2362,8 +2370,6 @@ class monitor_t { assert(_monitor_socket); - zmq::message_t eventMsg; - zmq::pollitem_t items[] = { {_monitor_socket.handle(), 0, ZMQ_POLLIN, 0}, }; @@ -2374,106 +2380,7 @@ class monitor_t zmq::poll(&items[0], 1, timeout); #endif - if (items[0].revents & ZMQ_POLLIN) { - int rc = zmq_msg_recv(eventMsg.handle(), _monitor_socket.handle(), 0); - if (rc == -1 && zmq_errno() == ETERM) - return false; - assert(rc != -1); - - } else { - return false; - } - -#if ZMQ_VERSION_MAJOR >= 4 - const char *data = static_cast(eventMsg.data()); - zmq_event_t msgEvent; - memcpy(&msgEvent.event, data, sizeof(uint16_t)); - data += sizeof(uint16_t); - memcpy(&msgEvent.value, data, sizeof(int32_t)); - zmq_event_t *event = &msgEvent; -#else - zmq_event_t *event = static_cast(eventMsg.data()); -#endif - -#ifdef ZMQ_NEW_MONITOR_EVENT_LAYOUT - zmq::message_t addrMsg; - int rc = zmq_msg_recv(addrMsg.handle(), _monitor_socket.handle(), 0); - if (rc == -1 && zmq_errno() == ETERM) { - return false; - } - - assert(rc != -1); - std::string address = addrMsg.to_string(); -#else - // Bit of a hack, but all events in the zmq_event_t union have the same layout so this will work for all event types. - std::string address = event->data.connected.addr; -#endif - -#ifdef ZMQ_EVENT_MONITOR_STOPPED - if (event->event == ZMQ_EVENT_MONITOR_STOPPED) { - return false; - } - -#endif - - switch (event->event) { - case ZMQ_EVENT_CONNECTED: - on_event_connected(*event, address.c_str()); - break; - case ZMQ_EVENT_CONNECT_DELAYED: - on_event_connect_delayed(*event, address.c_str()); - break; - case ZMQ_EVENT_CONNECT_RETRIED: - on_event_connect_retried(*event, address.c_str()); - break; - case ZMQ_EVENT_LISTENING: - on_event_listening(*event, address.c_str()); - break; - case ZMQ_EVENT_BIND_FAILED: - on_event_bind_failed(*event, address.c_str()); - break; - case ZMQ_EVENT_ACCEPTED: - on_event_accepted(*event, address.c_str()); - break; - case ZMQ_EVENT_ACCEPT_FAILED: - on_event_accept_failed(*event, address.c_str()); - break; - case ZMQ_EVENT_CLOSED: - on_event_closed(*event, address.c_str()); - break; - case ZMQ_EVENT_CLOSE_FAILED: - on_event_close_failed(*event, address.c_str()); - break; - case ZMQ_EVENT_DISCONNECTED: - on_event_disconnected(*event, address.c_str()); - break; -#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 3, 0) || (defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3)) - case ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL: - on_event_handshake_failed_no_detail(*event, address.c_str()); - break; - case ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL: - on_event_handshake_failed_protocol(*event, address.c_str()); - break; - case ZMQ_EVENT_HANDSHAKE_FAILED_AUTH: - on_event_handshake_failed_auth(*event, address.c_str()); - break; - case ZMQ_EVENT_HANDSHAKE_SUCCEEDED: - on_event_handshake_succeeded(*event, address.c_str()); - break; -#elif defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 1) - case ZMQ_EVENT_HANDSHAKE_FAILED: - on_event_handshake_failed(*event, address.c_str()); - break; - case ZMQ_EVENT_HANDSHAKE_SUCCEED: - on_event_handshake_succeed(*event, address.c_str()); - break; -#endif - default: - on_event_unknown(*event, address.c_str()); - break; - } - - return true; + return process_event(items[0].revents); } #ifdef ZMQ_EVENT_MONITOR_STOPPED @@ -2484,6 +2391,8 @@ class monitor_t _socket = socket_ref(); } + + virtual void on_monitor_stopped() {} #endif virtual void on_monitor_started() {} virtual void on_event_connected(const zmq_event_t &event_, const char *addr_) @@ -2583,6 +2492,116 @@ class monitor_t (void) addr_; } + protected: + bool process_event(short events) + { + zmq::message_t eventMsg; + + if (events & ZMQ_POLLIN) { + int rc = zmq_msg_recv(eventMsg.handle(), _monitor_socket.handle(), 0); + if (rc == -1 && zmq_errno() == ETERM) + return false; + assert(rc != -1); + + } else { + return false; + } + +#if ZMQ_VERSION_MAJOR >= 4 + const char *data = static_cast(eventMsg.data()); + zmq_event_t msgEvent; + memcpy(&msgEvent.event, data, sizeof(uint16_t)); + data += sizeof(uint16_t); + memcpy(&msgEvent.value, data, sizeof(int32_t)); + zmq_event_t *event = &msgEvent; +#else + zmq_event_t *event = static_cast(eventMsg.data()); +#endif + +#ifdef ZMQ_NEW_MONITOR_EVENT_LAYOUT + zmq::message_t addrMsg; + int rc = zmq_msg_recv(addrMsg.handle(), _monitor_socket.handle(), 0); + if (rc == -1 && zmq_errno() == ETERM) { + return false; + } + + assert(rc != -1); + std::string address = addrMsg.to_string(); +#else + // Bit of a hack, but all events in the zmq_event_t union have the same layout so this will work for all event types. + std::string address = event->data.connected.addr; +#endif + +#ifdef ZMQ_EVENT_MONITOR_STOPPED + if (event->event == ZMQ_EVENT_MONITOR_STOPPED) { + on_monitor_stopped(); + return false; + } + +#endif + + switch (event->event) { + case ZMQ_EVENT_CONNECTED: + on_event_connected(*event, address.c_str()); + break; + case ZMQ_EVENT_CONNECT_DELAYED: + on_event_connect_delayed(*event, address.c_str()); + break; + case ZMQ_EVENT_CONNECT_RETRIED: + on_event_connect_retried(*event, address.c_str()); + break; + case ZMQ_EVENT_LISTENING: + on_event_listening(*event, address.c_str()); + break; + case ZMQ_EVENT_BIND_FAILED: + on_event_bind_failed(*event, address.c_str()); + break; + case ZMQ_EVENT_ACCEPTED: + on_event_accepted(*event, address.c_str()); + break; + case ZMQ_EVENT_ACCEPT_FAILED: + on_event_accept_failed(*event, address.c_str()); + break; + case ZMQ_EVENT_CLOSED: + on_event_closed(*event, address.c_str()); + break; + case ZMQ_EVENT_CLOSE_FAILED: + on_event_close_failed(*event, address.c_str()); + break; + case ZMQ_EVENT_DISCONNECTED: + on_event_disconnected(*event, address.c_str()); + break; +#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 3, 0) || (defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3)) + case ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL: + on_event_handshake_failed_no_detail(*event, address.c_str()); + break; + case ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL: + on_event_handshake_failed_protocol(*event, address.c_str()); + break; + case ZMQ_EVENT_HANDSHAKE_FAILED_AUTH: + on_event_handshake_failed_auth(*event, address.c_str()); + break; + case ZMQ_EVENT_HANDSHAKE_SUCCEEDED: + on_event_handshake_succeeded(*event, address.c_str()); + break; +#elif defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 1) + case ZMQ_EVENT_HANDSHAKE_FAILED: + on_event_handshake_failed(*event, address.c_str()); + break; + case ZMQ_EVENT_HANDSHAKE_SUCCEED: + on_event_handshake_succeed(*event, address.c_str()); + break; +#endif + default: + on_event_unknown(*event, address.c_str()); + break; + } + + return true; + } + + socket_ref monitor_socket() {return _monitor_socket;} + private: monitor_t(const monitor_t &) ZMQ_DELETED_FUNCTION; void operator=(const monitor_t &) ZMQ_DELETED_FUNCTION; @@ -2681,6 +2700,13 @@ template class poller_t } } + void remove(fd_t fd) + { + if (0 != zmq_poller_remove_fd(poller_ptr.get(), fd)) { + throw error_t(); + } + } + void modify(zmq::socket_ref socket, event_flags events) { if (0 @@ -2690,9 +2716,21 @@ template class poller_t } } - size_t wait_all(std::vector &poller_events, + void modify(fd_t fd, event_flags events) + { + if (0 + != zmq_poller_modify_fd(poller_ptr.get(), fd, + static_cast(events))) { + throw error_t(); + } + } + + template + size_t wait_all(Sequence &poller_events, const std::chrono::milliseconds timeout) { + static_assert(std::is_same::value, + "Sequence::value_type must be of poller_t::event_type"); int rc = zmq_poller_wait_all( poller_ptr.get(), reinterpret_cast(poller_events.data()), @@ -2716,7 +2754,7 @@ template class poller_t { int rc = zmq_poller_size(const_cast(poller_ptr.get())); ZMQ_ASSERT(rc >= 0); - return static_cast(std::max(rc, 0)); + return static_cast((std::max)(rc, 0)); } #endif @@ -2757,6 +2795,85 @@ inline std::ostream &operator<<(std::ostream &os, const message_t &msg) return os << msg.str(); } +#if defined(ZMQ_CPP11) && defined(ZMQ_HAVE_TIMERS) + +class timers +{ + public: + using id_t = int; + using fn_t = zmq_timer_fn; + +#if CPPZMQ_HAS_OPTIONAL + using timeout_result_t = std::optional; +#else + using timeout_result_t = detail::trivial_optional; +#endif + + timers() : _timers(zmq_timers_new()) + { + if (_timers == nullptr) + throw error_t(); + } + + timers(const timers &other) = delete; + timers &operator=(const timers &other) = delete; + + ~timers() + { + int rc = zmq_timers_destroy(&_timers); + ZMQ_ASSERT(rc == 0); + } + + id_t add(std::chrono::milliseconds interval, zmq_timer_fn handler, void *arg) + { + id_t timer_id = zmq_timers_add(_timers, interval.count(), handler, arg); + if (timer_id == -1) + throw zmq::error_t(); + return timer_id; + } + + void cancel(id_t timer_id) + { + int rc = zmq_timers_cancel(_timers, timer_id); + if (rc == -1) + throw zmq::error_t(); + } + + void set_interval(id_t timer_id, std::chrono::milliseconds interval) + { + int rc = zmq_timers_set_interval(_timers, timer_id, interval.count()); + if (rc == -1) + throw zmq::error_t(); + } + + void reset(id_t timer_id) + { + int rc = zmq_timers_reset(_timers, timer_id); + if (rc == -1) + throw zmq::error_t(); + } + + timeout_result_t timeout() const + { + int timeout = zmq_timers_timeout(_timers); + if (timeout == -1) + return timeout_result_t{}; + return std::chrono::milliseconds{timeout}; + } + + void execute() + { + int rc = zmq_timers_execute(_timers); + if (rc == -1) + throw zmq::error_t(); + } + + private: + void *_timers; +}; + +#endif // defined(ZMQ_CPP11) && defined(ZMQ_HAVE_TIMERS) + } // namespace zmq #endif // __ZMQ_HPP_INCLUDED__ diff --git a/3rdparty/cppzmq/zmq_addon.hpp b/3rdparty/cppzmq/zmq_addon.hpp index 958eec56d..c6b4462cb 100644 --- a/3rdparty/cppzmq/zmq_addon.hpp +++ b/3rdparty/cppzmq/zmq_addon.hpp @@ -34,7 +34,65 @@ #include #include #include -#endif + +namespace zmq +{ + // socket ref or native file descriptor for poller + class poller_ref_t + { + public: + enum RefType + { + RT_SOCKET, + RT_FD + }; + + poller_ref_t() : poller_ref_t(socket_ref{}) + {} + + poller_ref_t(const zmq::socket_ref& socket) : data{RT_SOCKET, socket, {}} + {} + + poller_ref_t(zmq::fd_t fd) : data{RT_FD, {}, fd} + {} + + size_t hash() const ZMQ_NOTHROW + { + std::size_t h = 0; + hash_combine(h, std::get<0>(data)); + hash_combine(h, std::get<1>(data)); + hash_combine(h, std::get<2>(data)); + return h; + } + + bool operator == (const poller_ref_t& o) const ZMQ_NOTHROW + { + return data == o.data; + } + + private: + template + static void hash_combine(std::size_t& seed, const T& v) ZMQ_NOTHROW + { + std::hash hasher; + seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); + } + + std::tuple data; + + }; // class poller_ref_t + +} // namespace zmq + +// std::hash<> specialization for std::unordered_map +template <> struct std::hash +{ + size_t operator()(const zmq::poller_ref_t& ref) const ZMQ_NOTHROW + { + return ref.hash(); + } +}; +#endif // ZMQ_CPP11 namespace zmq { @@ -242,7 +300,7 @@ message_t encode(const Range &parts) if (part_size < (std::numeric_limits::max)()) { // small part - *buf++ = (unsigned char) part_size; + *buf++ = static_cast(part_size); } else { // big part *buf++ = (std::numeric_limits::max)(); @@ -683,10 +741,12 @@ class active_poller_t void add(zmq::socket_ref socket, event_flags events, handler_type handler) { + const poller_ref_t ref{socket}; + if (!handler) - throw std::invalid_argument("null handler in active_poller_t::add"); + throw std::invalid_argument("null handler in active_poller_t::add (socket)"); auto ret = handlers.emplace( - socket, std::make_shared(std::move(handler))); + ref, std::make_shared(std::move(handler))); if (!ret.second) throw error_t(EINVAL); // already added try { @@ -695,7 +755,28 @@ class active_poller_t } catch (...) { // rollback - handlers.erase(socket); + handlers.erase(ref); + throw; + } + } + + void add(fd_t fd, event_flags events, handler_type handler) + { + const poller_ref_t ref{fd}; + + if (!handler) + throw std::invalid_argument("null handler in active_poller_t::add (fd)"); + auto ret = handlers.emplace( + ref, std::make_shared(std::move(handler))); + if (!ret.second) + throw error_t(EINVAL); // already added + try { + base_poller.add(fd, events, ret.first->second.get()); + need_rebuild = true; + } + catch (...) { + // rollback + handlers.erase(ref); throw; } } @@ -707,11 +788,23 @@ class active_poller_t need_rebuild = true; } + void remove(fd_t fd) + { + base_poller.remove(fd); + handlers.erase(fd); + need_rebuild = true; + } + void modify(zmq::socket_ref socket, event_flags events) { base_poller.modify(socket, events); } + void modify(fd_t fd, event_flags events) + { + base_poller.modify(fd, events); + } + size_t wait(std::chrono::milliseconds timeout) { if (need_rebuild) { @@ -741,7 +834,9 @@ class active_poller_t bool need_rebuild{false}; poller_t base_poller{}; - std::unordered_map> handlers{}; + + std::unordered_map> handlers{}; + std::vector poller_events{}; std::vector> poller_handlers{}; }; // class active_poller_t From e2e3dbc0499282d47f7c276d445d2f58c9b3eb1c Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Tue, 30 Sep 2025 23:51:55 +0200 Subject: [PATCH 011/147] formatting --- include/behaviortree_cpp/utils/wildcards.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/behaviortree_cpp/utils/wildcards.hpp b/include/behaviortree_cpp/utils/wildcards.hpp index f4b6b40a6..68e2029f4 100644 --- a/include/behaviortree_cpp/utils/wildcards.hpp +++ b/include/behaviortree_cpp/utils/wildcards.hpp @@ -58,4 +58,4 @@ inline bool wildcards_match(std::string_view str, std::string_view pattern) }; return match(match, 0, 0); -} \ No newline at end of file +} From 624b11b9c80f2af7ef7651cb02fb1341209debb8 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Wed, 1 Oct 2025 00:10:48 +0200 Subject: [PATCH 012/147] remove cpp-sqlite --- 3rdparty/cpp-sqlite/README.md | 37 -- 3rdparty/cpp-sqlite/sqlite.hpp | 603 ------------------ .../loggers/bt_sqlite_logger.h | 8 +- src/loggers/bt_sqlite_logger.cpp | 149 +++-- 4 files changed, 107 insertions(+), 690 deletions(-) delete mode 100644 3rdparty/cpp-sqlite/README.md delete mode 100644 3rdparty/cpp-sqlite/sqlite.hpp diff --git a/3rdparty/cpp-sqlite/README.md b/3rdparty/cpp-sqlite/README.md deleted file mode 100644 index 913354947..000000000 --- a/3rdparty/cpp-sqlite/README.md +++ /dev/null @@ -1,37 +0,0 @@ -## Single file header only sqlite wrapper for C++ - -## Example -```cpp -#include "sqlite.hpp" -#include - -int main() -{ - sqlite::Connection connection("example.db"); - - sqlite::Statement(connection, "CREATE TABLE IF NOT EXISTS exampleTable (" - "textData TEXT, " - "intData INTEGER, " - "floatData REAL)"); - - sqlite::Statement(connection, - "INSERT INTO exampleTable VALUES (?, ?, ?)", - "Hello world", - 1234, - 5.6789); - - sqlite::Result res = sqlite::Query(connection, "SELECT * FROM exampleTable"); - - while(res.Next()) - { - std::string textData = res.Get(0); - int intData = res.Get(1); - float floatData = res.Get(2); - - std::cout << textData << " " << intData << " " << floatData << std::endl; - } - - return 0; -} - -``` diff --git a/3rdparty/cpp-sqlite/sqlite.hpp b/3rdparty/cpp-sqlite/sqlite.hpp deleted file mode 100644 index c512d891c..000000000 --- a/3rdparty/cpp-sqlite/sqlite.hpp +++ /dev/null @@ -1,603 +0,0 @@ -/** - Copyright (C) 2023 Toni Lipponen - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source distribution. - */ - -#pragma once -#include -#include -#include -#include -#include -#include - -#if __cplusplus > 201402L - #define CPP_SQLITE_NODISCARD [[nodiscard]] -#else - #define CPP_SQLITE_NODISCARD -#endif - -#if defined(CPP_SQLITE_NOTHROW) - #define CPP_SQLITE_THROW(...) return false -#else - #define CPP_SQLITE_THROW(...) throw sqlite::Error(__VA_ARGS__) -#endif - -namespace sqlite -{ - class Error : public std::runtime_error - { - public: - explicit Error(const char* message, int errorCode = SQLITE_ERROR) - : std::runtime_error(message), m_errorCode(errorCode) - { - - } - - explicit Error(const std::string& message, int errorCode = SQLITE_ERROR) - : std::runtime_error(message), m_errorCode(errorCode) - { - - } - - CPP_SQLITE_NODISCARD - int GetCode() const - { - return m_errorCode; - } - - private: - int m_errorCode; - }; - - namespace Priv - { - inline bool CheckError(sqlite3* db, int code) - { - if(code != SQLITE_OK && code != SQLITE_DONE) - { - const int extendedCode = sqlite3_extended_errcode(db); - std::string errstr = sqlite3_errstr(extendedCode); - std::string errmsg = sqlite3_errmsg(db); - - CPP_SQLITE_THROW(errstr + ": " + errmsg, extendedCode); - } - - return true; - } - - inline bool CheckError(int code) - { - if(code != SQLITE_OK && code != SQLITE_DONE) - { - std::string errstr = std::string("SQL error: ") + sqlite3_errstr(code); - CPP_SQLITE_THROW(errstr, code); - } - - return true; - } - } - - class Connection - { - public: - Connection() : m_connection(nullptr) {} - - explicit Connection(const std::string& filename) - { - this->Open(filename); - } - - Connection(const Connection&) = delete; - - Connection(Connection&& other) noexcept - { - this->m_connection = other.m_connection; - other.m_connection = nullptr; - } - - virtual ~Connection() noexcept - { - try - { - this->Close(); - } - catch(...) - { - - } - } - - Connection& operator=(const Connection&) = delete; - - Connection& operator=(Connection&& other) noexcept - { - if(&other != this) - { - this->m_connection = other.m_connection; - other.m_connection = nullptr; - } - - return *this; - } - - bool Open(const std::string& filename) - { - return sqlite::Priv::CheckError(sqlite3_open(filename.data(), &m_connection)); - } - - bool Close() - { - const auto result = Priv::CheckError(sqlite3_close(m_connection)); - m_connection = nullptr; - - return result; - } - - CPP_SQLITE_NODISCARD - int GetExtendedResult() const - { - return sqlite3_extended_errcode(m_connection); - } - - CPP_SQLITE_NODISCARD - sqlite3* GetPtr() - { - return m_connection; - } - - private: - sqlite3* m_connection = nullptr; - }; - - class Blob - { - public: - Blob(const void* data, int32_t bytes) - { - m_data.resize(bytes); - std::memcpy(&m_data.at(0), data, bytes); - } - - explicit Blob(std::vector data) - : m_data(std::move(data)) - { - - } - - CPP_SQLITE_NODISCARD - uint32_t GetSize() const - { - return m_data.size(); - } - - CPP_SQLITE_NODISCARD - unsigned char* GetData() - { - return m_data.data(); - } - - CPP_SQLITE_NODISCARD - const unsigned char* GetData() const - { - return m_data.data(); - } - - private: - std::vector m_data; - }; - - /** Non-owning blob*/ - class NOBlob - { - public: - NOBlob(const void* ptr, uint32_t bytes) - : m_ptr(ptr), m_bytes(bytes) - { - - } - - CPP_SQLITE_NODISCARD - uint32_t GetSize() const - { - return m_bytes; - } - - const void* GetData() - { - return m_ptr; - } - - CPP_SQLITE_NODISCARD - const void* GetData() const - { - return m_ptr; - } - - private: - const void* m_ptr; - uint32_t m_bytes; - }; - - namespace Priv - { - inline void Append(sqlite3_stmt* statement, int index, const int32_t& data) - { - sqlite::Priv::CheckError(sqlite3_bind_int(statement, index, data)); - } - - inline void Append(sqlite3_stmt* statement, int index, const int64_t& data) - { - sqlite::Priv::CheckError(sqlite3_bind_int64(statement, index, data)); - } - - inline void Append(sqlite3_stmt* statement, int index, const float& data) - { - sqlite::Priv::CheckError(sqlite3_bind_double(statement, index, static_cast(data))); - } - - inline void Append(sqlite3_stmt* statement, int index, const double& data) - { - sqlite::Priv::CheckError(sqlite3_bind_double(statement, index, data)); - } - - inline void Append(sqlite3_stmt* statement, int index, const std::string& data) - { - sqlite::Priv::CheckError(sqlite3_bind_text(statement, index, data.data(), static_cast(data.size()), nullptr)); - } - - inline void Append(sqlite3_stmt* statement, int index, const char* data) - { - sqlite::Priv::CheckError(sqlite3_bind_text(statement, index, data, static_cast(std::strlen(data)), nullptr)); - } - - inline void Append(sqlite3_stmt* statement, int index, const sqlite::Blob& blob) - { - sqlite::Priv::CheckError(sqlite3_bind_blob(statement, index, blob.GetData(), static_cast(blob.GetSize()), nullptr)); - } - - inline void Append(sqlite3_stmt* statement, int index, const sqlite::NOBlob& blob) - { - sqlite::Priv::CheckError(sqlite3_bind_blob(statement, index, blob.GetData(), static_cast(blob.GetSize()), nullptr)); - } - - template - inline void AppendToQuery(sqlite3_stmt* statement, int index, const Arg& arg) - { - sqlite::Priv::Append(statement, index, arg); - } - - template - inline void AppendToQuery(sqlite3_stmt* statement, int index, const First& first, const Args&... args) - { - sqlite::Priv::Append(statement, index, first); - sqlite::Priv::AppendToQuery(statement, ++index, args...); - } - - struct Statement - { - Statement() : handle(nullptr) {} - - Statement(sqlite::Connection& connection, const std::string& command) - { - auto* db = connection.GetPtr(); - - const int code = sqlite3_prepare_v2( - db, - command.data(), - static_cast(command.size()), - &handle, - nullptr); - - Priv::CheckError(db, code); - } - - Statement(Statement&& other) noexcept - { - std::swap(handle, other.handle); - } - - ~Statement() - { - sqlite::Priv::CheckError(sqlite3_finalize(handle)); - } - - Statement& operator=(Statement&& other) noexcept - { - handle = other.handle; - other.handle = nullptr; - - return *this; - } - - CPP_SQLITE_NODISCARD - bool Advance() const - { - const int code = sqlite3_step(handle); - - if(code == SQLITE_ROW) - { - return true; - } - - sqlite::Priv::CheckError(code); - Reset(); - - return false; - } - - bool Reset() const - { - return sqlite::Priv::CheckError(sqlite3_reset(handle)); - } - - CPP_SQLITE_NODISCARD - int ColumnCount() const - { - Reset(); - - if(!Advance()) - { - return 0; - } - - const int count = sqlite3_column_count(handle); - Reset(); - - return count; - } - - CPP_SQLITE_NODISCARD - std::string GetColumnName(int columnIndex) const - { - Reset(); - - if(!Advance()) - { -#ifndef CPP_SQLITE_NOTHROW - throw sqlite::Error("SQL error: invalid column index"); -#endif - } - - std::string name = sqlite3_column_name(handle, columnIndex); - - if(name.empty()) - { -#ifndef CPP_SQLITE_NOTHROW - throw sqlite::Error("SQL error: failed to get column name at index " + std::to_string(columnIndex)); -#endif - } - - Reset(); - - return name; - } - - template - CPP_SQLITE_NODISCARD - T Get(int) const - { - static_assert(sizeof(T) == -1, "SQL error: invalid column data type"); - } - - sqlite3_stmt* handle = nullptr; - }; - - template<> - inline float Statement::Get(int col) const - { - return static_cast(sqlite3_column_double(handle, col)); - } - - template<> - inline double Statement::Get(int col) const - { - return sqlite3_column_double(handle, col); - } - - template<> - inline int32_t Statement::Get(int col) const - { - return sqlite3_column_int(handle, col); - } - - template<> - inline int64_t Statement::Get(int col) const - { - return sqlite3_column_int64(handle, col); - } - - template<> - inline std::string Statement::Get(int col) const - { - const unsigned char* bytes = sqlite3_column_text(handle, col); - const int size = sqlite3_column_bytes(handle, col); - - if(size == 0) - { - return ""; - } - - return {reinterpret_cast(bytes), static_cast(size)}; - } - - template<> - inline sqlite::Blob Statement::Get(int col) const - { - const void* bytes = sqlite3_column_blob(handle, col); - const int size = sqlite3_column_bytes(handle, col); - - return {bytes, size}; - } - } - - class Type - { - private: - Type(const sqlite::Priv::Statement& statement, int col) - : m_statement(statement), m_columnIndex(col) - { - - } - public: - template - operator T() const - { - return m_statement.Get(m_columnIndex); - } - - friend class Result; - - private: - const sqlite::Priv::Statement& m_statement; - const int m_columnIndex; - }; - - class Result - { - explicit Result(sqlite::Priv::Statement&& statement) - : m_statement(std::move(statement)) - { - - } - - public: - Result() = default; - - Result(Result&& other) noexcept - { - m_statement = std::move(other.m_statement); - } - - Result& operator=(Result&& other) noexcept - { - m_statement = std::move(other.m_statement); - - return *this; - } - - CPP_SQLITE_NODISCARD - bool HasData() const - { - return ColumnCount() > 0; - } - - CPP_SQLITE_NODISCARD - int ColumnCount() const - { - return m_statement.ColumnCount(); - } - - bool Reset() const - { - return m_statement.Reset(); - } - - CPP_SQLITE_NODISCARD - bool Next() const - { - return m_statement.Advance(); - } - - CPP_SQLITE_NODISCARD - Type Get(int columnIndex) const - { - return {m_statement, columnIndex}; - } - - CPP_SQLITE_NODISCARD - std::string GetColumnName(int columnIndex) const - { - return m_statement.GetColumnName(columnIndex); - } - - friend void Statement(sqlite::Connection&, const std::string&); - - template - friend Result Query(sqlite::Connection& connection, const std::string& command, const First& first, const Args... args); - friend Result Query(sqlite::Connection& connection, const std::string& command); - - private: - sqlite::Priv::Statement m_statement; - }; - - template - inline void Statement(sqlite::Connection& connection, const std::string& command, const First& first, const Args... args) - { - sqlite::Priv::Statement statement(connection, command); - sqlite::Priv::AppendToQuery(statement.handle, 1, first, args...); - - (void)statement.Advance(); - } - - inline void Statement(sqlite::Connection& connection, const std::string& command) - { - sqlite::Priv::Statement statement(connection, command); - - (void)statement.Advance(); - } - - template - CPP_SQLITE_NODISCARD - inline Result Query(sqlite::Connection& connection, const std::string& command, const First& first, const Args... args) - { - sqlite::Priv::Statement statement(connection, command); - sqlite::Priv::AppendToQuery(statement.handle, 1, first, args...); - - return Result(std::move(statement)); - } - - CPP_SQLITE_NODISCARD - inline Result Query(sqlite::Connection& connection, const std::string& command) - { - sqlite::Priv::Statement statement(connection, command); - - return Result(std::move(statement)); - } - - inline bool Backup(sqlite::Connection& from, sqlite::Connection& to) - { - sqlite3_backup* backup = sqlite3_backup_init(to.GetPtr(), "main", from.GetPtr(), "main"); - - if(!backup) - { - CPP_SQLITE_THROW("SQL error: failed to initialize backup"); - } - - if(!Priv::CheckError(sqlite3_backup_step(backup, -1))) - { - return false; - } - - if(!Priv::CheckError(sqlite3_backup_finish(backup))) - { - return false; - } - - return true; - } - - inline bool Backup(sqlite::Connection& from, const std::string& filename) - { - sqlite::Connection to(filename); - - return sqlite::Backup(from, to); - } -} diff --git a/include/behaviortree_cpp/loggers/bt_sqlite_logger.h b/include/behaviortree_cpp/loggers/bt_sqlite_logger.h index 1b33f41fa..82f625ded 100644 --- a/include/behaviortree_cpp/loggers/bt_sqlite_logger.h +++ b/include/behaviortree_cpp/loggers/bt_sqlite_logger.h @@ -3,10 +3,8 @@ #include #include "behaviortree_cpp/loggers/abstract_logger.h" -namespace sqlite -{ -class Connection; -} +// forward declaration +struct sqlite3; namespace BT { @@ -74,7 +72,7 @@ class SqliteLogger : public StatusChangeLogger virtual void flush() override; private: - std::unique_ptr db_; + sqlite3* db_ = nullptr; int64_t monotonic_timestamp_ = 0; std::unordered_map starting_time_; diff --git a/src/loggers/bt_sqlite_logger.cpp b/src/loggers/bt_sqlite_logger.cpp index 7dd736b31..86038912f 100644 --- a/src/loggers/bt_sqlite_logger.cpp +++ b/src/loggers/bt_sqlite_logger.cpp @@ -1,10 +1,56 @@ #include "behaviortree_cpp/loggers/bt_sqlite_logger.h" #include "behaviortree_cpp/xml_parsing.h" -#include "cpp-sqlite/sqlite.hpp" +#include +#include +#include namespace BT { +namespace +{ +// Helper function to execute a SQL statement and check for errors +void execSQL(sqlite3* db, const std::string& sql) +{ + char* err_msg = nullptr; + int rc = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &err_msg); + if(rc != SQLITE_OK) + { + std::string error = "SQL error: "; + if(err_msg) + { + error += err_msg; + sqlite3_free(err_msg); + } + throw RuntimeError(error); + } +} + +// Helper function to prepare a statement +sqlite3_stmt* prepareStatement(sqlite3* db, const std::string& sql) +{ + sqlite3_stmt* stmt = nullptr; + int rc = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr); + if(rc != SQLITE_OK) + { + throw RuntimeError(std::string("Failed to prepare statement: ") + sqlite3_errmsg(db)); + } + return stmt; +} + +// Helper function to execute a prepared statement +void execStatement(sqlite3_stmt* stmt) +{ + int rc = sqlite3_step(stmt); + if(rc != SQLITE_DONE && rc != SQLITE_ROW) + { + throw RuntimeError(std::string("Failed to execute statement: ") + std::to_string(rc)); + } + sqlite3_finalize(stmt); +} + +} // namespace + SqliteLogger::SqliteLogger(const Tree& tree, std::filesystem::path const& filepath, bool append) : StatusChangeLogger(tree.rootNode()) @@ -17,53 +63,64 @@ SqliteLogger::SqliteLogger(const Tree& tree, std::filesystem::path const& filepa enableTransitionToIdle(true); - db_ = std::make_unique(filepath.string()); - - sqlite::Statement(*db_, "CREATE TABLE IF NOT EXISTS Transitions (" - "timestamp INTEGER PRIMARY KEY NOT NULL, " - "session_id INTEGER NOT NULL, " - "node_uid INTEGER NOT NULL, " - "duration INTEGER, " - "state INTEGER NOT NULL," - "extra_data VARCHAR );"); - - sqlite::Statement(*db_, "CREATE TABLE IF NOT EXISTS Nodes (" - "session_id INTEGER NOT NULL, " - "fullpath VARCHAR, " - "node_uid INTEGER NOT NULL );"); + // Open database + int rc = sqlite3_open(filepath.string().c_str(), &db_); + if(rc != SQLITE_OK) + { + throw RuntimeError(std::string("Cannot open database: ") + sqlite3_errmsg(db_)); + } - sqlite::Statement(*db_, "CREATE TABLE IF NOT EXISTS Definitions (" - "session_id INTEGER PRIMARY KEY AUTOINCREMENT, " - "date TEXT NOT NULL," - "xml_tree TEXT NOT NULL);"); + // Create tables + execSQL(db_, "CREATE TABLE IF NOT EXISTS Transitions (" + "timestamp INTEGER PRIMARY KEY NOT NULL, " + "session_id INTEGER NOT NULL, " + "node_uid INTEGER NOT NULL, " + "duration INTEGER, " + "state INTEGER NOT NULL," + "extra_data VARCHAR );"); + + execSQL(db_, "CREATE TABLE IF NOT EXISTS Nodes (" + "session_id INTEGER NOT NULL, " + "fullpath VARCHAR, " + "node_uid INTEGER NOT NULL );"); + + execSQL(db_, "CREATE TABLE IF NOT EXISTS Definitions (" + "session_id INTEGER PRIMARY KEY AUTOINCREMENT, " + "date TEXT NOT NULL," + "xml_tree TEXT NOT NULL);"); if(!append) { - sqlite::Statement(*db_, "DELETE from Transitions;"); - sqlite::Statement(*db_, "DELETE from Definitions;"); - sqlite::Statement(*db_, "DELETE from Nodes;"); + execSQL(db_, "DELETE from Transitions;"); + execSQL(db_, "DELETE from Definitions;"); + execSQL(db_, "DELETE from Nodes;"); } + // Insert tree definition auto tree_xml = WriteTreeToXML(tree, true, true); - sqlite::Statement(*db_, - "INSERT into Definitions (date, xml_tree) " - "VALUES (datetime('now','localtime'),?);", - tree_xml); - - auto res = sqlite::Query(*db_, "SELECT MAX(session_id) " - "FROM Definitions LIMIT 1;"); - - while(res.Next()) + sqlite3_stmt* stmt = prepareStatement(db_, "INSERT into Definitions (date, xml_tree) " + "VALUES (datetime('now','localtime'),?);"); + sqlite3_bind_text(stmt, 1, tree_xml.c_str(), -1, SQLITE_TRANSIENT); + execStatement(stmt); + + // Get session_id + stmt = prepareStatement(db_, "SELECT MAX(session_id) FROM Definitions LIMIT 1;"); + if(sqlite3_step(stmt) == SQLITE_ROW) { - session_id_ = res.Get(0); + session_id_ = sqlite3_column_int(stmt, 0); } + sqlite3_finalize(stmt); + // Insert nodes for(const auto& subtree : tree.subtrees) { for(const auto& node : subtree->nodes) { - sqlite::Statement(*db_, "INSERT INTO Nodes VALUES (?, ?, ?)", session_id_, - node->fullPath(), node->UID()); + stmt = prepareStatement(db_, "INSERT INTO Nodes VALUES (?, ?, ?)"); + sqlite3_bind_int(stmt, 1, session_id_); + sqlite3_bind_text(stmt, 2, node->fullPath().c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_int(stmt, 3, node->UID()); + execStatement(stmt); } } @@ -76,7 +133,8 @@ SqliteLogger::~SqliteLogger() queue_cv_.notify_one(); writer_thread_.join(); flush(); - sqlite::Statement(*db_, "PRAGMA optimize;"); + execSQL(db_, "PRAGMA optimize;"); + sqlite3_close(db_); } void SqliteLogger::setAdditionalCallback(ExtraCallback func) @@ -124,16 +182,11 @@ void SqliteLogger::callback(Duration timestamp, const TreeNode& node, transitions_queue_.push_back(trans); } queue_cv_.notify_one(); - - if(extra_func_) - { - extra_func_(timestamp, node, prev_status, status); - } } void SqliteLogger::execSqlStatement(std::string statement) { - sqlite::Statement(*db_, statement); + execSQL(db_, statement); } void SqliteLogger::writerLoop() @@ -154,16 +207,22 @@ void SqliteLogger::writerLoop() auto const trans = transitions.front(); transitions.pop_front(); - sqlite::Statement(*db_, "INSERT INTO Transitions VALUES (?, ?, ?, ?, ?, ?)", - trans.timestamp, session_id_, trans.node_uid, trans.duration, - static_cast(trans.status), trans.extra_data); + sqlite3_stmt* stmt = prepareStatement(db_, "INSERT INTO Transitions VALUES (?, ?, " + "?, ?, ?, ?)"); + sqlite3_bind_int64(stmt, 1, trans.timestamp); + sqlite3_bind_int(stmt, 2, session_id_); + sqlite3_bind_int(stmt, 3, trans.node_uid); + sqlite3_bind_int64(stmt, 4, trans.duration); + sqlite3_bind_int(stmt, 5, static_cast(trans.status)); + sqlite3_bind_text(stmt, 6, trans.extra_data.c_str(), -1, SQLITE_TRANSIENT); + execStatement(stmt); } } } void BT::SqliteLogger::flush() { - sqlite3_db_cacheflush(db_->GetPtr()); + sqlite3_db_cacheflush(db_); } } // namespace BT From b6761f5257d2b63c61775481ec5d2bc269854d6c Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Wed, 1 Oct 2025 00:14:26 +0200 Subject: [PATCH 013/147] changelog updated --- CHANGELOG.rst | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 600abbaa3..e0fb55b7a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,46 @@ Changelog for package behaviortree_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- +* remove cpp-sqlite +* update cppzmq to 4.11.0 +* remove wildcards from 3rd party +* Clean up VerifyXML logic (`#1000 `_) + * Refactor VerifyXML to clarify logic + - Reduces duplication in VerifyXML by handling the ID check for built-in + node types up front so they can then be definitively looked up in the + registered nodes. + - Enhances error messaging in VerifyXML by using *either* the node name + *or* the ID, depending on which is appropriate, instead of leaving + users guessing "which Decorator is wrong" + - Fixes custom Action and Condition nodes using shorthand syntax not + being properly verified + - Fixes `` not being verified with the + same logic as `` + - Fixes `` not triggering a behavior lookup when + `` would. + * fix tests that were failing due to bad assumptions +* Append SQLite3_INCLUDE_DIRS to BTCPP_EXTRA_INCLUDE_DIRS, otherwise sqlite3.h won't be found (`#1002 `_) + Co-authored-by: alejandro.suarez@omron.com +* fix: use dynamically growing error buffer in ParseScript (`#1007 `_) + * fix: use dynamically growing error buffer in ParseScript + * style: format code + * fix: use dynamically growing error buffer in ValidateScript + --------- + Co-authored-by: ahuo +* fix: validate __type field before accessing in fromJson (`#1009 `_) + Co-authored-by: ahuo +* fix: check path attribute before using (`#1005 `_) + Co-authored-by: ahuo +* Set current_child_idx of SequenceNode protected (`#991 `_) +* Add convertFromString> (`#992 `_) +* Update README.md fix `#985 `_ + Duuuude +* fix: exclude 3rd party libraries from sonar issue tracking (`#984 `_) +* change CI file +* Contributors: Alejandro Suárez, Davide Faconti, Ezra Brooks, Marcus Ebner von Eschenbach, Shaur(ya) Kumar, Vince Reda, Yiyi Wang + 4.7.2 (2025-05-29) ------------------ * Fix issue `#978 `_ : skipped was not working properly From 9b3e791f8c09845866bf50faf2c56d7bcd99ea42 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Wed, 1 Oct 2025 00:14:34 +0200 Subject: [PATCH 014/147] 4.7.3 --- CHANGELOG.rst | 4 ++-- package.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e0fb55b7a..8a1b78774 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package behaviortree_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +4.7.3 (2025-10-01) +------------------ * remove cpp-sqlite * update cppzmq to 4.11.0 * remove wildcards from 3rd party diff --git a/package.xml b/package.xml index 1d245b625..5e2b8f237 100644 --- a/package.xml +++ b/package.xml @@ -1,7 +1,7 @@ behaviortree_cpp - 4.7.2 + 4.7.3 This package provides the Behavior Trees core library. From 8ce8b8ee8bf0448aa720f65ed9cb771c382ab16c Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Tue, 30 Sep 2025 18:30:13 -0400 Subject: [PATCH 015/147] compile for c++ 17 (#1013) --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index e69c9e96c..194ad7bf5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ project(behaviortree_cpp VERSION 4.7.2 LANGUAGES C CXX) # create compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_CXX_STANDARD 17) #---- project configuration ---- option(BTCPP_SHARED_LIBS "Build shared libraries" ON) From d8c16098a49b7fd2718d1916c4d5df74173a859a Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Wed, 1 Oct 2025 00:29:31 +0200 Subject: [PATCH 016/147] fix potential compilation errors --- CMakeLists.txt | 2 +- src/xml_parsing.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 194ad7bf5..81ac107b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16.3) # version on Ubuntu Focal -project(behaviortree_cpp VERSION 4.7.2 LANGUAGES C CXX) +project(behaviortree_cpp VERSION 4.7.3 LANGUAGES C CXX) # create compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/src/xml_parsing.cpp b/src/xml_parsing.cpp index 3f895e04b..8ab66b1fc 100644 --- a/src/xml_parsing.cpp +++ b/src/xml_parsing.cpp @@ -53,7 +53,7 @@ std::string xsdAttributeType(const BT::PortInfo& port_info) return "blackboardType"; } const auto& type_info = port_info.type(); - if((type_info == typeid(int)) or (type_info == typeid(unsigned int))) + if((type_info == typeid(int)) || (type_info == typeid(unsigned int))) { return "integerOrBlackboardType"; } @@ -1444,7 +1444,7 @@ std::string writeTreeXSD(const BehaviorTreeFactory& factory) { XMLElement* type = doc.NewElement("xs:complexType"); type->SetAttribute("name", (model->registration_ID + "Type").c_str()); - if((model->type == NodeType::ACTION) or (model->type == NodeType::CONDITION) or + if((model->type == NodeType::ACTION) || (model->type == NodeType::CONDITION) || (model->type == NodeType::SUBTREE)) { /* No children, nothing to add. */ @@ -1478,11 +1478,11 @@ std::string writeTreeXSD(const BehaviorTreeFactory& factory) XMLElement* attr = doc.NewElement("xs:attribute"); attr->SetAttribute("name", port_name.c_str()); const auto xsd_attribute_type = xsdAttributeType(port_info); - if(not xsd_attribute_type.empty()) + if(!xsd_attribute_type.empty()) { attr->SetAttribute("type", xsd_attribute_type.c_str()); } - if(not port_info.defaultValue().empty()) + if(!port_info.defaultValue().empty()) { attr->SetAttribute("default", port_info.defaultValueString().c_str()); } From c0bc00b2a20fd14dfdf17226b52bc472ab7a9d0b Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Mon, 6 Oct 2025 11:14:41 +0200 Subject: [PATCH 017/147] update tinyxml to version 11.0 --- 3rdparty/tinyxml2/tinyxml2.cpp | 168 +++++++++++++++------------------ 3rdparty/tinyxml2/tinyxml2.h | 75 ++++++++------- 2 files changed, 115 insertions(+), 128 deletions(-) mode change 100755 => 100644 3rdparty/tinyxml2/tinyxml2.cpp mode change 100755 => 100644 3rdparty/tinyxml2/tinyxml2.h diff --git a/3rdparty/tinyxml2/tinyxml2.cpp b/3rdparty/tinyxml2/tinyxml2.cpp old mode 100755 new mode 100644 index c5c487010..66ef0c962 --- a/3rdparty/tinyxml2/tinyxml2.cpp +++ b/3rdparty/tinyxml2/tinyxml2.cpp @@ -24,7 +24,7 @@ distribution. #include "tinyxml2.h" #include // yes, this one new style header, is in the Android SDK. -#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) +#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) || defined(__CC_ARM) # include # include #else @@ -106,14 +106,9 @@ distribution. #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__CYGWIN__) #define TIXML_FSEEK fseeko #define TIXML_FTELL ftello -#elif defined(__ANDROID__) - #if __ANDROID_API__ > 24 - #define TIXML_FSEEK fseeko64 - #define TIXML_FTELL ftello64 - #else - #define TIXML_FSEEK fseeko - #define TIXML_FTELL ftello - #endif +#elif defined(__ANDROID__) && __ANDROID_API__ > 24 + #define TIXML_FSEEK fseeko64 + #define TIXML_FTELL ftello64 #else #define TIXML_FSEEK fseek #define TIXML_FTELL ftell @@ -239,13 +234,13 @@ char* StrPair::ParseName( char* p ) if ( !p || !(*p) ) { return 0; } - if ( !XMLUtil::IsNameStartChar( (unsigned char) *p ) ) { + if ( !XMLUtil::IsNameStartChar( static_cast(*p) ) ) { return 0; } char* const start = p; ++p; - while ( *p && XMLUtil::IsNameChar( (unsigned char) *p ) ) { + while ( *p && XMLUtil::IsNameChar( static_cast(*p) ) ) { ++p; } @@ -472,102 +467,94 @@ void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length } -const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) +const char* XMLUtil::GetCharacterRef(const char* p, char* value, int* length) { - // Presume an entity, and pull it out. + // Assume an entity, and pull it out. *length = 0; - if ( *(p+1) == '#' && *(p+2) ) { - unsigned long ucs = 0; - TIXMLASSERT( sizeof( ucs ) >= 4 ); + static const uint32_t MAX_CODE_POINT = 0x10FFFF; + + if (*(p + 1) == '#' && *(p + 2)) { + uint32_t ucs = 0; ptrdiff_t delta = 0; - unsigned mult = 1; + uint32_t mult = 1; static const char SEMICOLON = ';'; - if ( *(p+2) == 'x' ) { + bool hex = false; + uint32_t radix = 10; + const char* q = 0; + char terminator = '#'; + + if (*(p + 2) == 'x') { // Hexadecimal. - const char* q = p+3; - if ( !(*q) ) { - return 0; - } + hex = true; + radix = 16; + terminator = 'x'; - q = strchr( q, SEMICOLON ); + q = p + 3; + } + else { + // Decimal. + q = p + 2; + } + if (!(*q)) { + return 0; + } - if ( !q ) { - return 0; - } - TIXMLASSERT( *q == SEMICOLON ); + q = strchr(q, SEMICOLON); + if (!q) { + return 0; + } + TIXMLASSERT(*q == SEMICOLON); - delta = q-p; - --q; + delta = q - p; + --q; - while ( *q != 'x' ) { - unsigned int digit = 0; + while (*q != terminator) { + uint32_t digit = 0; - if ( *q >= '0' && *q <= '9' ) { - digit = *q - '0'; - } - else if ( *q >= 'a' && *q <= 'f' ) { - digit = *q - 'a' + 10; - } - else if ( *q >= 'A' && *q <= 'F' ) { - digit = *q - 'A' + 10; - } - else { - return 0; - } - TIXMLASSERT( digit < 16 ); - TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); - const unsigned int digitScaled = mult * digit; - TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); - ucs += digitScaled; - TIXMLASSERT( mult <= UINT_MAX / 16 ); - mult *= 16; - --q; + if (*q >= '0' && *q <= '9') { + digit = *q - '0'; } - } - else { - // Decimal. - const char* q = p+2; - if ( !(*q) ) { - return 0; + else if (hex && (*q >= 'a' && *q <= 'f')) { + digit = *q - 'a' + 10; } - - q = strchr( q, SEMICOLON ); - - if ( !q ) { + else if (hex && (*q >= 'A' && *q <= 'F')) { + digit = *q - 'A' + 10; + } + else { return 0; } - TIXMLASSERT( *q == SEMICOLON ); - - delta = q-p; - --q; - - while ( *q != '#' ) { - if ( *q >= '0' && *q <= '9' ) { - const unsigned int digit = *q - '0'; - TIXMLASSERT( digit < 10 ); - TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); - const unsigned int digitScaled = mult * digit; - TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); - ucs += digitScaled; - } - else { - return 0; - } - TIXMLASSERT( mult <= UINT_MAX / 10 ); - mult *= 10; - --q; + TIXMLASSERT(digit < radix); + + const unsigned int digitScaled = mult * digit; + ucs += digitScaled; + mult *= radix; + + // Security check: could a value exist that is out of range? + // Easily; limit to the MAX_CODE_POINT, which also allows for a + // bunch of leading zeroes. + if (mult > MAX_CODE_POINT) { + mult = MAX_CODE_POINT; } + --q; + } + // Out of range: + if (ucs > MAX_CODE_POINT) { + return 0; } // convert the UCS to UTF-8 - ConvertUTF32ToUTF8( ucs, value, length ); + ConvertUTF32ToUTF8(ucs, value, length); + if (length == 0) { + // If length is 0, there was an error. (Security? Bad input?) + // Fail safely. + return 0; + } return p + delta + 1; } - return p+1; + return p + 1; } - void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) { TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); @@ -610,7 +597,7 @@ void XMLUtil::ToStr( int64_t v, char* buffer, int bufferSize ) void XMLUtil::ToStr( uint64_t v, char* buffer, int bufferSize ) { // horrible syntax trick to make the compiler happy about %llu - TIXML_SNPRINTF(buffer, bufferSize, "%llu", (long long)v); + TIXML_SNPRINTF(buffer, bufferSize, "%llu", static_cast(v)); } bool XMLUtil::ToInt(const char* str, int* value) @@ -705,7 +692,7 @@ bool XMLUtil::ToInt64(const char* str, int64_t* value) bool XMLUtil::ToUnsigned64(const char* str, uint64_t* value) { unsigned long long v = 0; // horrible syntax trick to make the compiler happy about %llu if(TIXML_SSCANF(str, IsPrefixHex(str) ? "%llx" : "%llu", &v) == 1) { - *value = (uint64_t)v; + *value = static_cast(v); return true; } return false; @@ -1982,7 +1969,7 @@ char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr ) } // attribute. - if (XMLUtil::IsNameStartChar( (unsigned char) *p ) ) { + if (XMLUtil::IsNameStartChar( static_cast(*p) ) ) { XMLAttribute* attrib = CreateAttribute(); TIXMLASSERT( attrib ); attrib->_parseLineNum = _document->_parseCurLineNum; @@ -2226,7 +2213,7 @@ void XMLDocument::MarkInUse(const XMLNode* const node) TIXMLASSERT(node); TIXMLASSERT(node->_parent == 0); - for (int i = 0; i < _unlinked.Size(); ++i) { + for (size_t i = 0; i < _unlinked.Size(); ++i) { if (node == _unlinked[i]) { _unlinked.SwapRemove(i); break; @@ -2509,7 +2496,7 @@ void XMLDocument::ClearError() { void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... ) { - TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT ); + TIXMLASSERT(error >= 0 && error < XML_ERROR_COUNT); _errorID = error; _errorLineNum = lineNum; _errorStr.Reset(); @@ -2518,7 +2505,8 @@ void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... char* buffer = new char[BUFFER_SIZE]; TIXMLASSERT(sizeof(error) <= sizeof(int)); - TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum); + TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", + ErrorIDToName(error), static_cast(error), static_cast(error), lineNum); if (format) { size_t len = strlen(buffer); diff --git a/3rdparty/tinyxml2/tinyxml2.h b/3rdparty/tinyxml2/tinyxml2.h old mode 100755 new mode 100644 index 7586f7b8d..8179f57b4 --- a/3rdparty/tinyxml2/tinyxml2.h +++ b/3rdparty/tinyxml2/tinyxml2.h @@ -96,11 +96,11 @@ distribution. /* Versioning, past 1.0.14: http://semver.org/ */ -static const int TIXML2_MAJOR_VERSION = 10; +static const int TIXML2_MAJOR_VERSION = 11; static const int TIXML2_MINOR_VERSION = 0; static const int TIXML2_PATCH_VERSION = 0; -#define TINYXML2_MAJOR_VERSION 10 +#define TINYXML2_MAJOR_VERSION 11 #define TINYXML2_MINOR_VERSION 0 #define TINYXML2_PATCH_VERSION 0 @@ -199,7 +199,7 @@ class TINYXML2_LIB StrPair Has a small initial memory pool, so that low or no usage will not cause a call to new/delete */ -template +template class DynArray { public: @@ -227,9 +227,8 @@ class DynArray ++_size; } - T* PushArr( int count ) { - TIXMLASSERT( count >= 0 ); - TIXMLASSERT( _size <= INT_MAX - count ); + T* PushArr( size_t count ) { + TIXMLASSERT( _size <= SIZE_MAX - count ); EnsureCapacity( _size+count ); T* ret = &_mem[_size]; _size += count; @@ -242,7 +241,7 @@ class DynArray return _mem[_size]; } - void PopArr( int count ) { + void PopArr( size_t count ) { TIXMLASSERT( _size >= count ); _size -= count; } @@ -251,13 +250,13 @@ class DynArray return _size == 0; } - T& operator[](int i) { - TIXMLASSERT( i>= 0 && i < _size ); + T& operator[](size_t i) { + TIXMLASSERT( i < _size ); return _mem[i]; } - const T& operator[](int i) const { - TIXMLASSERT( i>= 0 && i < _size ); + const T& operator[](size_t i) const { + TIXMLASSERT( i < _size ); return _mem[i]; } @@ -266,18 +265,18 @@ class DynArray return _mem[ _size - 1]; } - int Size() const { + size_t Size() const { TIXMLASSERT( _size >= 0 ); return _size; } - int Capacity() const { + size_t Capacity() const { TIXMLASSERT( _allocated >= INITIAL_SIZE ); return _allocated; } - void SwapRemove(int i) { - TIXMLASSERT(i >= 0 && i < _size); + void SwapRemove(size_t i) { + TIXMLASSERT(i < _size); TIXMLASSERT(_size > 0); _mem[i] = _mem[_size - 1]; --_size; @@ -297,14 +296,14 @@ class DynArray DynArray( const DynArray& ); // not supported void operator=( const DynArray& ); // not supported - void EnsureCapacity( int cap ) { + void EnsureCapacity( size_t cap ) { TIXMLASSERT( cap > 0 ); if ( cap > _allocated ) { - TIXMLASSERT( cap <= INT_MAX / 2 ); - const int newAllocated = cap * 2; - T* newMem = new T[static_cast(newAllocated)]; + TIXMLASSERT( cap <= SIZE_MAX / 2 / sizeof(T)); + const size_t newAllocated = cap * 2; + T* newMem = new T[newAllocated]; TIXMLASSERT( newAllocated >= _size ); - memcpy( newMem, _mem, sizeof(T)*static_cast(_size) ); // warning: not using constructors, only works for PODs + memcpy( newMem, _mem, sizeof(T) * _size ); // warning: not using constructors, only works for PODs if ( _mem != _pool ) { delete [] _mem; } @@ -314,9 +313,9 @@ class DynArray } T* _mem; - T _pool[static_cast(INITIAL_SIZE)]; - int _allocated; // objects allocated - int _size; // number objects in use + T _pool[INITIAL_SIZE]; + size_t _allocated; // objects allocated + size_t _size; // number objects in use }; @@ -330,7 +329,7 @@ class MemPool MemPool() {} virtual ~MemPool() {} - virtual int ItemSize() const = 0; + virtual size_t ItemSize() const = 0; virtual void* Alloc() = 0; virtual void Free( void* ) = 0; virtual void SetTracked() = 0; @@ -340,7 +339,7 @@ class MemPool /* Template child class to create pools of the correct type. */ -template< int ITEM_SIZE > +template< size_t ITEM_SIZE > class MemPoolT : public MemPool { public: @@ -362,10 +361,10 @@ class MemPoolT : public MemPool _nUntracked = 0; } - virtual int ItemSize() const override{ + virtual size_t ItemSize() const override { return ITEM_SIZE; } - int CurrentAllocs() const { + size_t CurrentAllocs() const { return _currentAllocs; } @@ -376,7 +375,7 @@ class MemPoolT : public MemPool _blockPtrs.Push( block ); Item* blockItems = block->items; - for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) { + for( size_t i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) { blockItems[i].next = &(blockItems[i + 1]); } blockItems[ITEMS_PER_BLOCK - 1].next = 0; @@ -417,7 +416,7 @@ class MemPoolT : public MemPool --_nUntracked; } - int Untracked() const { + size_t Untracked() const { return _nUntracked; } @@ -448,10 +447,10 @@ class MemPoolT : public MemPool DynArray< Block*, 10 > _blockPtrs; Item* _root; - int _currentAllocs; - int _nAllocs; - int _maxAllocs; - int _nUntracked; + size_t _currentAllocs; + size_t _nAllocs; + size_t _maxAllocs; + size_t _nUntracked; }; @@ -892,7 +891,7 @@ class TINYXML2_LIB XMLNode If the 'target' is null, then the nodes will be allocated in the current document. If 'target' - is specified, the memory will be allocated is the + is specified, the memory will be allocated in the specified XMLDocument. NOTE: This is probably not the correct tool to @@ -1981,11 +1980,11 @@ class TINYXML2_LIB XMLDocument : public XMLNode void PushDepth(); void PopDepth(); - template + template NodeType* CreateUnlinkedNode( MemPoolT& pool ); }; -template +template inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT& pool ) { TIXMLASSERT( sizeof( NodeType ) == PoolElementSize ); @@ -2315,7 +2314,7 @@ class TINYXML2_LIB XMLPrinter : public XMLVisitor of the XML file in memory. (Note the size returned includes the terminating null.) */ - int CStrSize() const { + size_t CStrSize() const { return _buffer.Size(); } /** @@ -2375,7 +2374,7 @@ class TINYXML2_LIB XMLPrinter : public XMLVisitor }; -} // tinyxml2 +} // namespace tinyxml2 #if defined(_MSC_VER) # pragma warning(pop) From c381486027294aa95f5d5a503810b031ebdd6f84 Mon Sep 17 00:00:00 2001 From: Eric Riff <57375845+ericriff@users.noreply.github.com> Date: Mon, 6 Oct 2025 10:59:55 -0300 Subject: [PATCH 018/147] Improve handling of dependencies (#1012) * Support using minitrace from conan * Support using tinyxml2 from conan * Add support for using minicoro from conan * Add support for using flatbuffers from conan * Create separate targets for each 3rdparty lib not yet supported by conan so we can avoid exposing the whole 3rdparty folder on target_include_directories Since this can create some confusion around which headers are actually being included -- the ones from that folder or the ones from conan? Also fixes the include dirs by using ${CMAKE_CURRENT_SOURCE_DIR} instead of "." * Fix builds For whatever reason including zmq.hpp before zmq_addon.hpp (which does include zmq.hpp internally) breaks builds * Do not include the whole 3rdparty folder, only link in what we need * Use the regular lexy target * Do not expose the whole 3rdparty folder as a include_directory * Add options to opt-out of vendored libraries * This was shared across both code paths, conan_build.cmake and ament_build.cmake So it is better to keep this on a single place * Keep all the find_package calls on the toplevel CMakeLists * SQLite3 is actually a dependency of cpp-sqlite * Fix include dirs of the vendored minicoro and flatbuffers * Define libzmq cmake target on FindZeroMQ to match the conan package * Improve message. This code path doesn't really mean we're using conan, it just means we're not using ament. * Use the python version of conanfile.py so we can set the CMake options needed to opt out of vendored dependencies * Address pre-commit complains * Use conanfile.py across the board * Do not look for ZeroMQ directly as it is a dependency of cppzmq Also only look for cppzmq if BTCPP_GROOT_INTERFACE * Keep a single copy of zmq.hpp This header is part of the cppzmq library so it lives on 3rdparty/cppzmq. But for whatever reason there was another version of this header here. Furthermore it was a differnt version of the library. * Leave a FIXME for posterity This target was silently being skiped, not it is explicit * Leave comment for posterity * Remove empty line * Remove unneeded line * Remove uneeded line * Remove empty line * Revert unintentional changes * Use cmake_layout to support multiconfig * Update toolchain path on cicd * Emtpy commit to re-trigger CI * Use cppzmq from conan * Use cmake presets on conan builds * It looks like in windows the preset is called conan-default * It looks like the preset is only called default for config? * Fix tests path in windows * Use lexy from conan * Force cppstd to 17, conan profile detect uses 14 * Try to fix windows builds * Remove wildcards cmake options, it has been removed on master * Update changes after cpp-sqlite removal --- .github/workflows/cmake_ubuntu.yml | 23 +- .github/workflows/cmake_windows.yml | 20 +- 3rdparty/cppzmq/CMakeLists.txt | 19 + 3rdparty/flatbuffers/CMakeLists.txt | 8 + 3rdparty/flatbuffers/{ => flatbuffers}/base.h | 0 3rdparty/minicoro/CMakeLists.txt | 8 + 3rdparty/minitrace/CMakeLists.txt | 20 + 3rdparty/tinyxml2/CMakeLists.txt | 15 + CMakeLists.txt | 80 +- README.md | 12 +- cmake/FindZeroMQ.cmake | 8 + cmake/ament_build.cmake | 12 - cmake/conan_build.cmake | 9 +- conanfile.py | 39 + conanfile.txt | 8 - sample_nodes/CMakeLists.txt | 3 - src/action_node.cpp | 2 +- src/loggers/bt_minitrace_logger.cpp | 2 +- src/loggers/groot2_publisher.cpp | 3 +- src/loggers/zmq.hpp | 2815 ----------------- src/xml_parsing.cpp | 2 +- tests/CMakeLists.txt | 2 +- tools/CMakeLists.txt | 23 +- tools/bt_recorder.cpp | 2 +- 24 files changed, 228 insertions(+), 2907 deletions(-) create mode 100644 3rdparty/cppzmq/CMakeLists.txt create mode 100644 3rdparty/flatbuffers/CMakeLists.txt rename 3rdparty/flatbuffers/{ => flatbuffers}/base.h (100%) create mode 100644 3rdparty/minicoro/CMakeLists.txt create mode 100644 3rdparty/minitrace/CMakeLists.txt create mode 100644 3rdparty/tinyxml2/CMakeLists.txt create mode 100644 conanfile.py delete mode 100644 conanfile.txt delete mode 100644 src/loggers/zmq.hpp diff --git a/.github/workflows/cmake_ubuntu.yml b/.github/workflows/cmake_ubuntu.yml index 41ed9a196..1d4ba93f1 100644 --- a/.github/workflows/cmake_ubuntu.yml +++ b/.github/workflows/cmake_ubuntu.yml @@ -32,28 +32,25 @@ jobs: - name: Create default profile run: conan profile detect - - name: Create Build Environment - # Some projects don't allow in-source building, so create a separate build directory - # We'll use this as our working directory for all subsequent commands - run: cmake -E make_directory ${{github.workspace}}/build - - name: Install conan dependencies - working-directory: ${{github.workspace}}/build - run: conan install ${{github.workspace}}/conanfile.txt -s build_type=${{env.BUILD_TYPE}} --build=missing + run: conan install conanfile.py -s build_type=${{env.BUILD_TYPE}} --build=missing + + - name: Normalize build type + shell: bash + # The build type is Capitalized, e.g. Release, but the preset is all lowercase, e.g. release. + # There is no built in way to do string manipulations on GHA as far as I know.` + run: echo "BUILD_TYPE_LOWERCASE=$(echo "${BUILD_TYPE}" | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV - name: Configure CMake shell: bash - working-directory: ${{github.workspace}}/build - run: cmake ${{github.workspace}} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake + run: cmake --preset conan-${{ env.BUILD_TYPE_LOWERCASE }} - name: Build shell: bash - working-directory: ${{github.workspace}}/build - run: cmake --build . --config ${{env.BUILD_TYPE}} + run: cmake --build --preset conan-${{ env.BUILD_TYPE_LOWERCASE }} - name: run test (Linux) - working-directory: ${{github.workspace}}/build/tests - run: ctest + run: ctest --test-dir build/${{env.BUILD_TYPE}} - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3 diff --git a/.github/workflows/cmake_windows.yml b/.github/workflows/cmake_windows.yml index 34f4f97ce..5082acdc7 100644 --- a/.github/workflows/cmake_windows.yml +++ b/.github/workflows/cmake_windows.yml @@ -32,24 +32,22 @@ jobs: - name: Create default profile run: conan profile detect - - name: Create Build Environment - # Some projects don't allow in-source building, so create a separate build directory - # We'll use this as our working directory for all subsequent commands - run: cmake -E make_directory ${{github.workspace}}/build - - name: Install conan dependencies - working-directory: ${{github.workspace}}/build - run: conan install ${{github.workspace}}/conanfile.txt -s build_type=${{env.BUILD_TYPE}} --build=missing + run: conan install conanfile.py -s build_type=${{env.BUILD_TYPE}} --build=missing --settings:host compiler.cppstd=17 + + - name: Normalize build type + shell: bash + # The build type is Capitalized, e.g. Release, but the preset is all lowercase, e.g. release. + # There is no built in way to do string manipulations on GHA as far as I know.` + run: echo "BUILD_TYPE_LOWERCASE=$(echo "${BUILD_TYPE}" | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV - name: Configure CMake shell: bash - working-directory: ${{github.workspace}}/build - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake + run: cmake --preset conan-default - name: Build - working-directory: ${{github.workspace}}/build shell: bash - run: cmake --build . --config ${{env.BUILD_TYPE}} + run: cmake --build --preset conan-${{ env.BUILD_TYPE_LOWERCASE }} - name: run test (Windows) working-directory: ${{github.workspace}}/build diff --git a/3rdparty/cppzmq/CMakeLists.txt b/3rdparty/cppzmq/CMakeLists.txt new file mode 100644 index 000000000..9a0bb86b0 --- /dev/null +++ b/3rdparty/cppzmq/CMakeLists.txt @@ -0,0 +1,19 @@ +find_package(ZeroMQ REQUIRED) + +add_library(cppzmq INTERFACE) + +# This library doesn't use modern targets unfortunately. +#add_library(cppzmq::cppzmq ALIAS cppzmq) + +target_include_directories(cppzmq + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +if(TARGET libzmq-static) + target_link_libraries(cppzmq INTERFACE libzmq-static) +elseif(TARGET libzmq) + target_link_libraries(cppzmq INTERFACE libzmq) +else() + message(FATAL_ERROR "Unknown zeromq target name") +endif() diff --git a/3rdparty/flatbuffers/CMakeLists.txt b/3rdparty/flatbuffers/CMakeLists.txt new file mode 100644 index 000000000..bc91f8e27 --- /dev/null +++ b/3rdparty/flatbuffers/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(flatbuffers INTERFACE) + +add_library(flatbuffers::flatbuffers ALIAS flatbuffers) + +target_include_directories(flatbuffers + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/3rdparty/flatbuffers/base.h b/3rdparty/flatbuffers/flatbuffers/base.h similarity index 100% rename from 3rdparty/flatbuffers/base.h rename to 3rdparty/flatbuffers/flatbuffers/base.h diff --git a/3rdparty/minicoro/CMakeLists.txt b/3rdparty/minicoro/CMakeLists.txt new file mode 100644 index 000000000..9508d8445 --- /dev/null +++ b/3rdparty/minicoro/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(minicoro INTERFACE) + +add_library(minicoro::minicoro ALIAS minicoro) + +target_include_directories(minicoro + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/3rdparty/minitrace/CMakeLists.txt b/3rdparty/minitrace/CMakeLists.txt new file mode 100644 index 000000000..a228df116 --- /dev/null +++ b/3rdparty/minitrace/CMakeLists.txt @@ -0,0 +1,20 @@ +add_library(minitrace STATIC + minitrace.cpp +) + +add_library(minitrace::minitrace ALIAS minitrace) + +target_include_directories(minitrace + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_compile_definitions(minitrace + PRIVATE + MTR_ENABLED=True +) + +set_property(TARGET minitrace + PROPERTY + POSITION_INDEPENDENT_CODE ON +) diff --git a/3rdparty/tinyxml2/CMakeLists.txt b/3rdparty/tinyxml2/CMakeLists.txt new file mode 100644 index 000000000..9d5dc9fb9 --- /dev/null +++ b/3rdparty/tinyxml2/CMakeLists.txt @@ -0,0 +1,15 @@ +add_library(tinyxml2 STATIC + tinyxml2.cpp +) + +add_library(tinyxml2::tinyxml2 ALIAS tinyxml2) + +target_include_directories(tinyxml2 + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) + +set_property(TARGET tinyxml2 + PROPERTY + POSITION_INDEPENDENT_CODE ON +) diff --git a/CMakeLists.txt b/CMakeLists.txt index 81ac107b3..a0cb7b202 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,17 @@ option(USE_AFLPLUSPLUS "Use AFL++ instead of libFuzzer" OFF) option(ENABLE_DEBUG "Enable debug build with full symbols" OFF) option(FORCE_STATIC_LINKING "Force static linking of all dependencies" OFF) +option(USE_VENDORED_CPPZMQ "Use the bundled version of cppzmq" ON) +option(USE_VENDORED_FLATBUFFERS "Use the bundled version of flatbuffers" ON) +option(USE_VENDORED_LEXY "Use the bundled version of lexy" ON) +option(USE_VENDORED_MINICORO "Use the bundled version of minicoro" ON) +option(USE_VENDORED_MINITRACE "Use the bundled version of minitrace" ON) +option(USE_VENDORED_TINYXML2 "Use the bundled version of tinyxml2" ON) + +set(BTCPP_LIB_DESTINATION lib) +set(BTCPP_INCLUDE_DESTINATION include) +set(BTCPP_BIN_DESTINATION bin) + set(BASE_FLAGS "") if(ENABLE_DEBUG) @@ -62,12 +73,6 @@ if(USE_V3_COMPATIBLE_NAMES) add_definitions(-DUSE_BTCPP3_OLD_NAMES) endif() -#---- Find other packages ---- -find_package(Threads REQUIRED) - - -set(BEHAVIOR_TREE_LIBRARY ${PROJECT_NAME}) - # Update the policy setting to avoid an error when loading the ament_cmake package # at the current cmake version level if(POLICY CMP0057) @@ -85,19 +90,57 @@ if ( ament_cmake_FOUND ) include(cmake/ament_build.cmake) else() message(STATUS "------------------------------------------") - message(STATUS "BehaviorTree is being built with conan.") + message(STATUS "BehaviorTree is being built without AMENT.") message(STATUS "------------------------------------------") include(cmake/conan_build.cmake) endif() ############################################################# -# LIBRARY +# Handle dependencies + +find_package(Threads REQUIRED) + +if(BTCPP_GROOT_INTERFACE) + if(USE_VENDORED_CPPZMQ) + add_subdirectory(3rdparty/cppzmq) + else() + find_package(cppzmq REQUIRED) + endif() +endif() + +if(BTCPP_SQLITE_LOGGING) + find_package(SQLite3 REQUIRED) +endif() + +if(USE_VENDORED_FLATBUFFERS) + add_subdirectory(3rdparty/flatbuffers) +else() + find_package(flatbuffers REQUIRED) +endif() -add_subdirectory(3rdparty/lexy) +if(USE_VENDORED_LEXY) + add_subdirectory(3rdparty/lexy) +else() + find_package(lexy REQUIRED) +endif() -add_library(minitrace STATIC 3rdparty/minitrace/minitrace.cpp) -target_compile_definitions(minitrace PRIVATE MTR_ENABLED=True) -set_property(TARGET minitrace PROPERTY POSITION_INDEPENDENT_CODE ON) +if(USE_VENDORED_MINICORO) + add_subdirectory(3rdparty/minicoro) +else() + find_package(minicoro REQUIRED) +endif() + +if(USE_VENDORED_MINITRACE) + add_subdirectory(3rdparty/minitrace) +else() + find_package(minitrace REQUIRED) +endif() + +if(USE_VENDORED_TINYXML2) + add_subdirectory(3rdparty/tinyxml2) +else() + find_package(tinyxml2 REQUIRED) +endif() list(APPEND BT_SOURCE src/action_node.cpp @@ -141,8 +184,6 @@ list(APPEND BT_SOURCE src/loggers/bt_file_logger_v2.cpp src/loggers/bt_minitrace_logger.cpp src/loggers/bt_observer.cpp - - 3rdparty/tinyxml2/tinyxml2.cpp ) @@ -180,8 +221,13 @@ target_link_libraries(${BTCPP_LIBRARY} PRIVATE Threads::Threads ${CMAKE_DL_LIBS} - $ - $ + foonathan::lexy + minitrace::minitrace + tinyxml2::tinyxml2 + minicoro::minicoro + flatbuffers::flatbuffers + $<$:cppzmq> + $<$:SQLite::SQLite3> PUBLIC ${BTCPP_EXTRA_LIBRARIES} ) @@ -191,8 +237,6 @@ target_include_directories(${BTCPP_LIBRARY} $ $ PRIVATE - $ - $ ${BTCPP_EXTRA_INCLUDE_DIRS} ) diff --git a/README.md b/README.md index f1ddc4082..b30335ffc 100644 --- a/README.md +++ b/README.md @@ -61,13 +61,15 @@ Three build systems are supported: Compiling with [conan](https://conan.io/): -Assuming that you are in the **parent** directory of `BehaviorTree.CPP`: +> [!NOTE] +> Conan builds require CMake 3.23 or newer. + +Assuming that you are in the **root** directory of `BehaviorTree.CPP`: ``` -mkdir build_release -conan install . -of build_release -s build_type=Release -cmake -S . -B build_release -DCMAKE_TOOLCHAIN_FILE="build_release/conan_toolchain.cmake" -cmake --build build_release --parallel +conan install . -s build_type=Release --build=missing +cmake --preset conan-release +cmake --build --preset conan-release ``` If you have dependencies such as ZeroMQ and SQlite already installed and you don't want to diff --git a/cmake/FindZeroMQ.cmake b/cmake/FindZeroMQ.cmake index b11258812..1549d9948 100644 --- a/cmake/FindZeroMQ.cmake +++ b/cmake/FindZeroMQ.cmake @@ -63,5 +63,13 @@ else (ZeroMQ_LIBRARIES AND ZeroMQ_INCLUDE_DIRS) # show the ZeroMQ_INCLUDE_DIRS and ZeroMQ_LIBRARIES variables only in the advanced view mark_as_advanced(ZeroMQ_INCLUDE_DIRS ZeroMQ_LIBRARIES) + if(ZeroMQ_FOUND AND NOT TARGET libzmq) + add_library(libzmq UNKNOWN IMPORTED) + set_target_properties(libzmq PROPERTIES + IMPORTED_LOCATION "${ZeroMQ_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${ZeroMQ_INCLUDE_DIRS}" + ) + endif() + endif (ZeroMQ_LIBRARIES AND ZeroMQ_INCLUDE_DIRS) endif(ZeroMQ_FOUND) diff --git a/cmake/ament_build.cmake b/cmake/ament_build.cmake index ec1e0a66b..0bcfa64eb 100644 --- a/cmake/ament_build.cmake +++ b/cmake/ament_build.cmake @@ -2,14 +2,6 @@ set(CMAKE_CONFIG_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CONFIG_PATH}") -if(BTCPP_GROOT_INTERFACE) - find_package(ZeroMQ REQUIRED) -endif() - -if(BTCPP_SQLITE_LOGGING) - find_package(SQLite3 REQUIRED) -endif() - find_package(ament_index_cpp REQUIRED) set(BTCPP_EXTRA_INCLUDE_DIRS ${ZeroMQ_INCLUDE_DIRS} @@ -23,10 +15,6 @@ set( BTCPP_EXTRA_LIBRARIES ament_export_dependencies(ament_index_cpp) -set( BTCPP_LIB_DESTINATION lib ) -set( BTCPP_INCLUDE_DESTINATION include ) -set( BTCPP_BIN_DESTINATION bin ) - mark_as_advanced( BTCPP_EXTRA_LIBRARIES BTCPP_EXTRA_INCLUDE_DIRS diff --git a/cmake/conan_build.cmake b/cmake/conan_build.cmake index 83876cc61..d110fcceb 100644 --- a/cmake/conan_build.cmake +++ b/cmake/conan_build.cmake @@ -1,24 +1,19 @@ list(APPEND CMAKE_PREFIX_PATH "${CMAKE_BINARY_DIR}") if(BTCPP_GROOT_INTERFACE) - find_package(ZeroMQ REQUIRED) +# find_package(ZeroMQ REQUIRED) list(APPEND BTCPP_EXTRA_LIBRARIES ${ZeroMQ_LIBRARIES}) list(APPEND BTCPP_EXTRA_INCLUDE_DIRS ${ZeroMQ_INCLUDE_DIRS}) message(STATUS "ZeroMQ_LIBRARIES: ${ZeroMQ_LIBRARIES}") endif() if(BTCPP_SQLITE_LOGGING) - find_package(SQLite3 REQUIRED) +# find_package(SQLite3 REQUIRED) list(APPEND BTCPP_EXTRA_LIBRARIES ${SQLite3_LIBRARIES}) list(APPEND BTCPP_EXTRA_INCLUDE_DIRS ${SQLite3_INCLUDE_DIRS}) message(STATUS "SQLite3_LIBRARIES: ${SQLite3_LIBRARIES}") endif() - -set( BTCPP_LIB_DESTINATION lib ) -set( BTCPP_INCLUDE_DESTINATION include ) -set( BTCPP_BIN_DESTINATION bin ) - mark_as_advanced( BTCPP_EXTRA_LIBRARIES BTCPP_LIB_DESTINATION diff --git a/conanfile.py b/conanfile.py new file mode 100644 index 000000000..ff1d57b5e --- /dev/null +++ b/conanfile.py @@ -0,0 +1,39 @@ +from conan import ConanFile +from conan.tools.cmake import CMakeToolchain, CMakeDeps, cmake_layout + +class BehaviortreeCppConan(ConanFile): + name = "behaviortree.cpp" + settings = "os", "arch", "compiler", "build_type" + + default_options = { + "flatbuffers/*:header_only": True, + } + + def layout(self): + cmake_layout(self) + + def build_requirements(self): + self.test_requires("gtest/1.14.0") + + def requirements(self): + self.requires("flatbuffers/24.12.23") + self.requires("minicoro/0.1.3") + self.requires("minitrace/cci.20230905") + self.requires("sqlite3/3.40.1") + self.requires("tinyxml2/10.0.0") + self.requires("cppzmq/4.11.0") + self.requires("foonathan-lexy/2022.12.1") + + def generate(self): + tc = CMakeToolchain(self) + + tc.cache_variables["USE_VENDORED_CPPZMQ"] = False + tc.cache_variables["USE_VENDORED_FLATBUFFERS"] = False + tc.cache_variables["USE_VENDORED_LEXY"] = False + tc.cache_variables["USE_VENDORED_MINICORO"] = False + tc.cache_variables["USE_VENDORED_MINITRACE"] = False + tc.cache_variables["USE_VENDORED_TINYXML2"] = False + tc.generate() + + deps = CMakeDeps(self) + deps.generate() diff --git a/conanfile.txt b/conanfile.txt deleted file mode 100644 index 7b81d1d6d..000000000 --- a/conanfile.txt +++ /dev/null @@ -1,8 +0,0 @@ -[requires] -gtest/1.14.0 -zeromq/4.3.4 -sqlite3/3.40.1 - -[generators] -CMakeDeps -CMakeToolchain diff --git a/sample_nodes/CMakeLists.txt b/sample_nodes/CMakeLists.txt index 217fec77d..d6a78f7a7 100644 --- a/sample_nodes/CMakeLists.txt +++ b/sample_nodes/CMakeLists.txt @@ -1,7 +1,4 @@ -include_directories( ../include ) - # compile as static libraries - set(CMAKE_DEBUG_POSTFIX "") add_library(bt_sample_nodes STATIC diff --git a/src/action_node.cpp b/src/action_node.cpp index 61dff35ef..2bf78f964 100644 --- a/src/action_node.cpp +++ b/src/action_node.cpp @@ -12,7 +12,7 @@ */ #define MINICORO_IMPL -#include "minicoro/minicoro.h" +#include "minicoro.h" #include "behaviortree_cpp/action_node.h" using namespace BT; diff --git a/src/loggers/bt_minitrace_logger.cpp b/src/loggers/bt_minitrace_logger.cpp index 69d6d0b16..ad2c9ec86 100644 --- a/src/loggers/bt_minitrace_logger.cpp +++ b/src/loggers/bt_minitrace_logger.cpp @@ -2,7 +2,7 @@ #include "behaviortree_cpp/loggers/bt_minitrace_logger.h" #define MTR_ENABLED true -#include "minitrace/minitrace.h" +#include "minitrace.h" namespace BT { diff --git a/src/loggers/groot2_publisher.cpp b/src/loggers/groot2_publisher.cpp index 6146507e8..f6f0afd0d 100644 --- a/src/loggers/groot2_publisher.cpp +++ b/src/loggers/groot2_publisher.cpp @@ -1,8 +1,7 @@ #include "behaviortree_cpp/loggers/groot2_publisher.h" #include "behaviortree_cpp/loggers/groot2_protocol.h" #include "behaviortree_cpp/xml_parsing.h" -#include "cppzmq/zmq.hpp" -#include "cppzmq/zmq_addon.hpp" +#include "zmq_addon.hpp" namespace BT { diff --git a/src/loggers/zmq.hpp b/src/loggers/zmq.hpp deleted file mode 100644 index 26c2c6f42..000000000 --- a/src/loggers/zmq.hpp +++ /dev/null @@ -1,2815 +0,0 @@ -/* - Copyright (c) 2016-2017 ZeroMQ community - Copyright (c) 2009-2011 250bpm s.r.o. - Copyright (c) 2011 Botond Ballo - Copyright (c) 2007-2009 iMatix Corporation - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to - deal in the Software without restriction, including without limitation the - rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - sell copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - IN THE SOFTWARE. -*/ - -#ifndef __ZMQ_HPP_INCLUDED__ -#define __ZMQ_HPP_INCLUDED__ - -#ifdef _WIN32 -#ifndef NOMINMAX -#define NOMINMAX -#endif -#endif - -// included here for _HAS_CXX* macros -#include - -#if defined(_MSVC_LANG) -#define CPPZMQ_LANG _MSVC_LANG -#else -#define CPPZMQ_LANG __cplusplus -#endif -// overwrite if specific language macros indicate higher version -#if defined(_HAS_CXX14) && _HAS_CXX14 && CPPZMQ_LANG < 201402L -#undef CPPZMQ_LANG -#define CPPZMQ_LANG 201402L -#endif -#if defined(_HAS_CXX17) && _HAS_CXX17 && CPPZMQ_LANG < 201703L -#undef CPPZMQ_LANG -#define CPPZMQ_LANG 201703L -#endif - -// macros defined if has a specific standard or greater -#if CPPZMQ_LANG >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) -#define ZMQ_CPP11 -#endif -#if CPPZMQ_LANG >= 201402L -#define ZMQ_CPP14 -#endif -#if CPPZMQ_LANG >= 201703L -#define ZMQ_CPP17 -#endif - -#if defined(ZMQ_CPP14) && !defined(_MSC_VER) -#define ZMQ_DEPRECATED(msg) [[deprecated(msg)]] -#elif defined(_MSC_VER) -#define ZMQ_DEPRECATED(msg) __declspec(deprecated(msg)) -#elif defined(__GNUC__) -#define ZMQ_DEPRECATED(msg) __attribute__((deprecated(msg))) -#endif - -#if defined(ZMQ_CPP17) -#define ZMQ_NODISCARD [[nodiscard]] -#else -#define ZMQ_NODISCARD -#endif - -#if defined(ZMQ_CPP11) -#define ZMQ_NOTHROW noexcept -#define ZMQ_EXPLICIT explicit -#define ZMQ_OVERRIDE override -#define ZMQ_NULLPTR nullptr -#define ZMQ_CONSTEXPR_FN constexpr -#define ZMQ_CONSTEXPR_VAR constexpr -#define ZMQ_CPP11_DEPRECATED(msg) ZMQ_DEPRECATED(msg) -#else -#define ZMQ_NOTHROW throw() -#define ZMQ_EXPLICIT -#define ZMQ_OVERRIDE -#define ZMQ_NULLPTR 0 -#define ZMQ_CONSTEXPR_FN -#define ZMQ_CONSTEXPR_VAR const -#define ZMQ_CPP11_DEPRECATED(msg) -#endif -#if defined(ZMQ_CPP14) && (!defined(_MSC_VER) || _MSC_VER > 1900) -#define ZMQ_EXTENDED_CONSTEXPR -#endif -#if defined(ZMQ_CPP17) -#define ZMQ_INLINE_VAR inline -#else -#define ZMQ_INLINE_VAR -#endif - -#include -#include - -#include -#include -#include -#include -#include -#include -#ifdef ZMQ_CPP11 -#include -#include -#include -#include -#endif - -#if defined(__has_include) && defined(ZMQ_CPP17) -#define CPPZMQ_HAS_INCLUDE_CPP17(X) __has_include(X) -#else -#define CPPZMQ_HAS_INCLUDE_CPP17(X) 0 -#endif - -#if CPPZMQ_HAS_INCLUDE_CPP17() && !defined(CPPZMQ_HAS_OPTIONAL) -#define CPPZMQ_HAS_OPTIONAL 1 -#endif -#ifndef CPPZMQ_HAS_OPTIONAL -#define CPPZMQ_HAS_OPTIONAL 0 -#elif CPPZMQ_HAS_OPTIONAL -#include -#endif - -#if CPPZMQ_HAS_INCLUDE_CPP17() && !defined(CPPZMQ_HAS_STRING_VIEW) -#define CPPZMQ_HAS_STRING_VIEW 1 -#endif -#ifndef CPPZMQ_HAS_STRING_VIEW -#define CPPZMQ_HAS_STRING_VIEW 0 -#elif CPPZMQ_HAS_STRING_VIEW -#include -#endif - -/* Version macros for compile-time API version detection */ -#define CPPZMQ_VERSION_MAJOR 4 -#define CPPZMQ_VERSION_MINOR 7 -#define CPPZMQ_VERSION_PATCH 1 - -#define CPPZMQ_VERSION \ - ZMQ_MAKE_VERSION(CPPZMQ_VERSION_MAJOR, CPPZMQ_VERSION_MINOR, CPPZMQ_VERSION_PATCH) - -// Detect whether the compiler supports C++11 rvalue references. -#if(defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 2)) && \ - defined(__GXX_EXPERIMENTAL_CXX0X__)) -#define ZMQ_HAS_RVALUE_REFS -#define ZMQ_DELETED_FUNCTION = delete -#elif defined(__clang__) -#if __has_feature(cxx_rvalue_references) -#define ZMQ_HAS_RVALUE_REFS -#endif - -#if __has_feature(cxx_deleted_functions) -#define ZMQ_DELETED_FUNCTION = delete -#else -#define ZMQ_DELETED_FUNCTION -#endif -#elif defined(_MSC_VER) && (_MSC_VER >= 1900) -#define ZMQ_HAS_RVALUE_REFS -#define ZMQ_DELETED_FUNCTION = delete -#elif defined(_MSC_VER) && (_MSC_VER >= 1600) -#define ZMQ_HAS_RVALUE_REFS -#define ZMQ_DELETED_FUNCTION -#else -#define ZMQ_DELETED_FUNCTION -#endif - -#if defined(ZMQ_CPP11) && !defined(__llvm__) && !defined(__INTEL_COMPILER) && \ - defined(__GNUC__) && __GNUC__ < 5 -#define ZMQ_CPP11_PARTIAL -#elif defined(__GLIBCXX__) && __GLIBCXX__ < 20160805 -//the date here is the last date of gcc 4.9.4, which -// effectively means libstdc++ from gcc 5.5 and higher won't trigger this branch -#define ZMQ_CPP11_PARTIAL -#endif - -#ifdef ZMQ_CPP11 -#ifdef ZMQ_CPP11_PARTIAL -#define ZMQ_IS_TRIVIALLY_COPYABLE(T) __has_trivial_copy(T) -#else -#include -#define ZMQ_IS_TRIVIALLY_COPYABLE(T) std::is_trivially_copyable::value -#endif -#endif - -#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 3, 0) -#define ZMQ_NEW_MONITOR_EVENT_LAYOUT -#endif - -#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0) -#define ZMQ_HAS_PROXY_STEERABLE -/* Socket event data */ -typedef struct -{ - uint16_t event; // id of the event as bitfield - int32_t value; // value is either error code, fd or reconnect interval -} zmq_event_t; -#endif - -// Avoid using deprecated message receive function when possible -#if ZMQ_VERSION < ZMQ_MAKE_VERSION(3, 2, 0) -#define zmq_msg_recv(msg, socket, flags) zmq_recvmsg(socket, msg, flags) -#endif - -// In order to prevent unused variable warnings when building in non-debug -// mode use this macro to make assertions. -#ifndef NDEBUG -#define ZMQ_ASSERT(expression) assert(expression) -#else -#define ZMQ_ASSERT(expression) (void)(expression) -#endif - -namespace zmq -{ -#ifdef ZMQ_CPP11 -namespace detail -{ -namespace ranges -{ -using std::begin; -using std::end; -template -auto begin(T&& r) -> decltype(begin(std::forward(r))) -{ - return begin(std::forward(r)); -} -template -auto end(T&& r) -> decltype(end(std::forward(r))) -{ - return end(std::forward(r)); -} -} // namespace ranges - -template -using void_t = void; - -template -using iter_value_t = typename std::iterator_traits::value_type; - -template -using range_iter_t = - decltype(ranges::begin(std::declval::type&>())); - -template -using range_value_t = iter_value_t>; - -template -struct is_range : std::false_type -{ -}; - -template -struct is_range< - T, void_t::type&>()) == - ranges::end( - std::declval::type&>()))>> - : std::true_type -{ -}; - -} // namespace detail -#endif - -typedef zmq_free_fn free_fn; -typedef zmq_pollitem_t pollitem_t; - -class error_t : public std::exception -{ -public: - error_t() ZMQ_NOTHROW : errnum(zmq_errno()) - {} - explicit error_t(int err) ZMQ_NOTHROW : errnum(err) - {} - virtual const char* what() const ZMQ_NOTHROW ZMQ_OVERRIDE - { - return zmq_strerror(errnum); - } - int num() const ZMQ_NOTHROW - { - return errnum; - } - -private: - int errnum; -}; - -inline int poll(zmq_pollitem_t* items_, size_t nitems_, long timeout_ = -1) -{ - int rc = zmq_poll(items_, static_cast(nitems_), timeout_); - if(rc < 0) - throw error_t(); - return rc; -} - -ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items") -inline int poll(zmq_pollitem_t const* items_, size_t nitems_, long timeout_ = -1) -{ - return poll(const_cast(items_), nitems_, timeout_); -} - -#ifdef ZMQ_CPP11 -ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items") -inline int poll(zmq_pollitem_t const* items, size_t nitems, - std::chrono::milliseconds timeout) -{ - return poll(const_cast(items), nitems, - static_cast(timeout.count())); -} - -ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items") -inline int poll(std::vector const& items, - std::chrono::milliseconds timeout) -{ - return poll(const_cast(items.data()), items.size(), - static_cast(timeout.count())); -} - -ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items") -inline int poll(std::vector const& items, long timeout_ = -1) -{ - return poll(const_cast(items.data()), items.size(), timeout_); -} - -inline int poll(zmq_pollitem_t* items, size_t nitems, std::chrono::milliseconds timeout) -{ - return poll(items, nitems, static_cast(timeout.count())); -} - -inline int poll(std::vector& items, std::chrono::milliseconds timeout) -{ - return poll(items.data(), items.size(), static_cast(timeout.count())); -} - -ZMQ_DEPRECATED("from 4.3.1, use poll taking std::chrono instead of long") -inline int poll(std::vector& items, long timeout_ = -1) -{ - return poll(items.data(), items.size(), timeout_); -} - -template -inline int poll(std::array& items, - std::chrono::milliseconds timeout) -{ - return poll(items.data(), items.size(), static_cast(timeout.count())); -} -#endif - -inline void version(int* major_, int* minor_, int* patch_) -{ - zmq_version(major_, minor_, patch_); -} - -#ifdef ZMQ_CPP11 -inline std::tuple version() -{ - std::tuple v; - zmq_version(&std::get<0>(v), &std::get<1>(v), &std::get<2>(v)); - return v; -} - -#if !defined(ZMQ_CPP11_PARTIAL) -namespace detail -{ -template -struct is_char_type -{ - // true if character type for string literals in C++11 - static constexpr bool value = - std::is_same::value || std::is_same::value || - std::is_same::value || std::is_same::value; -}; -} // namespace detail -#endif - -#endif - -class message_t -{ -public: - message_t() ZMQ_NOTHROW - { - int rc = zmq_msg_init(&msg); - ZMQ_ASSERT(rc == 0); - } - - explicit message_t(size_t size_) - { - int rc = zmq_msg_init_size(&msg, size_); - if(rc != 0) - throw error_t(); - } - - template - message_t(ForwardIter first, ForwardIter last) - { - typedef typename std::iterator_traits::value_type value_t; - - assert(std::distance(first, last) >= 0); - size_t const size_ = - static_cast(std::distance(first, last)) * sizeof(value_t); - int const rc = zmq_msg_init_size(&msg, size_); - if(rc != 0) - throw error_t(); - std::copy(first, last, data()); - } - - message_t(const void* data_, size_t size_) - { - int rc = zmq_msg_init_size(&msg, size_); - if(rc != 0) - throw error_t(); - if(size_) - { - // this constructor allows (nullptr, 0), - // memcpy with a null pointer is UB - memcpy(data(), data_, size_); - } - } - - message_t(void* data_, size_t size_, free_fn* ffn_, void* hint_ = ZMQ_NULLPTR) - { - int rc = zmq_msg_init_data(&msg, data_, size_, ffn_, hint_); - if(rc != 0) - throw error_t(); - } - - // overload set of string-like types and generic containers -#if defined(ZMQ_CPP11) && !defined(ZMQ_CPP11_PARTIAL) - // NOTE this constructor will include the null terminator - // when called with a string literal. - // An overload taking const char* can not be added because - // it would be preferred over this function and break compatibility. - template ::value>::type> - ZMQ_DEPRECATED("from 4.7.0, use constructors taking iterators, (pointer, size) " - "or strings instead") - explicit message_t(const Char (&data)[N]) - : message_t(detail::ranges::begin(data), detail::ranges::end(data)) - {} - - template ::value && - ZMQ_IS_TRIVIALLY_COPYABLE(detail::range_value_t) && - !detail::is_char_type>::value && - !std::is_same::value>::type> - explicit message_t(const Range& rng) - : message_t(detail::ranges::begin(rng), detail::ranges::end(rng)) - {} - - explicit message_t(const std::string& str) : message_t(str.data(), str.size()) - {} - -#if CPPZMQ_HAS_STRING_VIEW - explicit message_t(std::string_view str) : message_t(str.data(), str.size()) - {} -#endif - -#endif - -#ifdef ZMQ_HAS_RVALUE_REFS - message_t(message_t&& rhs) ZMQ_NOTHROW : msg(rhs.msg) - { - int rc = zmq_msg_init(&rhs.msg); - ZMQ_ASSERT(rc == 0); - } - - message_t& operator=(message_t&& rhs) ZMQ_NOTHROW - { - std::swap(msg, rhs.msg); - return *this; - } -#endif - - ~message_t() ZMQ_NOTHROW - { - int rc = zmq_msg_close(&msg); - ZMQ_ASSERT(rc == 0); - } - - void rebuild() - { - int rc = zmq_msg_close(&msg); - if(rc != 0) - throw error_t(); - rc = zmq_msg_init(&msg); - ZMQ_ASSERT(rc == 0); - } - - void rebuild(size_t size_) - { - int rc = zmq_msg_close(&msg); - if(rc != 0) - throw error_t(); - rc = zmq_msg_init_size(&msg, size_); - if(rc != 0) - throw error_t(); - } - - void rebuild(const void* data_, size_t size_) - { - int rc = zmq_msg_close(&msg); - if(rc != 0) - throw error_t(); - rc = zmq_msg_init_size(&msg, size_); - if(rc != 0) - throw error_t(); - memcpy(data(), data_, size_); - } - - void rebuild(void* data_, size_t size_, free_fn* ffn_, void* hint_ = ZMQ_NULLPTR) - { - int rc = zmq_msg_close(&msg); - if(rc != 0) - throw error_t(); - rc = zmq_msg_init_data(&msg, data_, size_, ffn_, hint_); - if(rc != 0) - throw error_t(); - } - - ZMQ_DEPRECATED("from 4.3.1, use move taking non-const reference instead") - void move(message_t const* msg_) - { - int rc = zmq_msg_move(&msg, const_cast(msg_->handle())); - if(rc != 0) - throw error_t(); - } - - void move(message_t& msg_) - { - int rc = zmq_msg_move(&msg, msg_.handle()); - if(rc != 0) - throw error_t(); - } - - ZMQ_DEPRECATED("from 4.3.1, use copy taking non-const reference instead") - void copy(message_t const* msg_) - { - int rc = zmq_msg_copy(&msg, const_cast(msg_->handle())); - if(rc != 0) - throw error_t(); - } - - void copy(message_t& msg_) - { - int rc = zmq_msg_copy(&msg, msg_.handle()); - if(rc != 0) - throw error_t(); - } - - bool more() const ZMQ_NOTHROW - { - int rc = zmq_msg_more(const_cast(&msg)); - return rc != 0; - } - - void* data() ZMQ_NOTHROW - { - return zmq_msg_data(&msg); - } - - const void* data() const ZMQ_NOTHROW - { - return zmq_msg_data(const_cast(&msg)); - } - - size_t size() const ZMQ_NOTHROW - { - return zmq_msg_size(const_cast(&msg)); - } - - ZMQ_NODISCARD bool empty() const ZMQ_NOTHROW - { - return size() == 0u; - } - - template - T* data() ZMQ_NOTHROW - { - return static_cast(data()); - } - - template - T const* data() const ZMQ_NOTHROW - { - return static_cast(data()); - } - - ZMQ_DEPRECATED("from 4.3.0, use operator== instead") - bool equal(const message_t* other) const ZMQ_NOTHROW - { - return *this == *other; - } - - bool operator==(const message_t& other) const ZMQ_NOTHROW - { - const size_t my_size = size(); - return my_size == other.size() && 0 == memcmp(data(), other.data(), my_size); - } - - bool operator!=(const message_t& other) const ZMQ_NOTHROW - { - return !(*this == other); - } - -#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 2, 0) - int get(int property_) - { - int value = zmq_msg_get(&msg, property_); - if(value == -1) - throw error_t(); - return value; - } -#endif - -#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0) - const char* gets(const char* property_) - { - const char* value = zmq_msg_gets(&msg, property_); - if(value == ZMQ_NULLPTR) - throw error_t(); - return value; - } -#endif - -#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0) - uint32_t routing_id() const - { - return zmq_msg_routing_id(const_cast(&msg)); - } - - void set_routing_id(uint32_t routing_id) - { - int rc = zmq_msg_set_routing_id(&msg, routing_id); - if(rc != 0) - throw error_t(); - } - - const char* group() const - { - return zmq_msg_group(const_cast(&msg)); - } - - void set_group(const char* group) - { - int rc = zmq_msg_set_group(&msg, group); - if(rc != 0) - throw error_t(); - } -#endif - - // interpret message content as a string - std::string to_string() const - { - return std::string(static_cast(data()), size()); - } -#if CPPZMQ_HAS_STRING_VIEW - // interpret message content as a string - std::string_view to_string_view() const noexcept - { - return std::string_view(static_cast(data()), size()); - } -#endif - - /** Dump content to string for debugging. - * Ascii chars are readable, the rest is printed as hex. - * Probably ridiculously slow. - * Use to_string() or to_string_view() for - * interpreting the message as a string. - */ - std::string str() const - { - // Partly mutuated from the same method in zmq::multipart_t - std::stringstream os; - - const unsigned char* msg_data = this->data(); - unsigned char byte; - size_t size = this->size(); - int is_ascii[2] = { 0, 0 }; - - os << "zmq::message_t [size " << std::dec << std::setw(3) << std::setfill('0') << size - << "] ("; - // Totally arbitrary - if(size >= 1000) - { - os << "... too big to print)"; - } - else - { - while(size--) - { - byte = *msg_data++; - - is_ascii[1] = (byte >= 32 && byte < 127); - if(is_ascii[1] != is_ascii[0]) - os << " "; // Separate text/non text - - if(is_ascii[1]) - { - os << byte; - } - else - { - os << std::hex << std::uppercase << std::setw(2) << std::setfill('0') - << static_cast(byte); - } - is_ascii[0] = is_ascii[1]; - } - os << ")"; - } - return os.str(); - } - - void swap(message_t& other) ZMQ_NOTHROW - { - // this assumes zmq::msg_t from libzmq is trivially relocatable - std::swap(msg, other.msg); - } - - ZMQ_NODISCARD zmq_msg_t* handle() ZMQ_NOTHROW - { - return &msg; - } - ZMQ_NODISCARD const zmq_msg_t* handle() const ZMQ_NOTHROW - { - return &msg; - } - -private: - // The underlying message - zmq_msg_t msg; - - // Disable implicit message copying, so that users won't use shared - // messages (less efficient) without being aware of the fact. - message_t(const message_t&) ZMQ_DELETED_FUNCTION; - void operator=(const message_t&) ZMQ_DELETED_FUNCTION; -}; - -inline void swap(message_t& a, message_t& b) ZMQ_NOTHROW -{ - a.swap(b); -} - -#ifdef ZMQ_CPP11 -enum class ctxopt -{ -#ifdef ZMQ_BLOCKY - blocky = ZMQ_BLOCKY, -#endif -#ifdef ZMQ_IO_THREADS - io_threads = ZMQ_IO_THREADS, -#endif -#ifdef ZMQ_THREAD_SCHED_POLICY - thread_sched_policy = ZMQ_THREAD_SCHED_POLICY, -#endif -#ifdef ZMQ_THREAD_PRIORITY - thread_priority = ZMQ_THREAD_PRIORITY, -#endif -#ifdef ZMQ_THREAD_AFFINITY_CPU_ADD - thread_affinity_cpu_add = ZMQ_THREAD_AFFINITY_CPU_ADD, -#endif -#ifdef ZMQ_THREAD_AFFINITY_CPU_REMOVE - thread_affinity_cpu_remove = ZMQ_THREAD_AFFINITY_CPU_REMOVE, -#endif -#ifdef ZMQ_THREAD_NAME_PREFIX - thread_name_prefix = ZMQ_THREAD_NAME_PREFIX, -#endif -#ifdef ZMQ_MAX_MSGSZ - max_msgsz = ZMQ_MAX_MSGSZ, -#endif -#ifdef ZMQ_ZERO_COPY_RECV - zero_copy_recv = ZMQ_ZERO_COPY_RECV, -#endif -#ifdef ZMQ_MAX_SOCKETS - max_sockets = ZMQ_MAX_SOCKETS, -#endif -#ifdef ZMQ_SOCKET_LIMIT - socket_limit = ZMQ_SOCKET_LIMIT, -#endif -#ifdef ZMQ_IPV6 - ipv6 = ZMQ_IPV6, -#endif -#ifdef ZMQ_MSG_T_SIZE - msg_t_size = ZMQ_MSG_T_SIZE -#endif -}; -#endif - -class context_t -{ -public: - context_t() - { - ptr = zmq_ctx_new(); - if(ptr == ZMQ_NULLPTR) - throw error_t(); - } - - explicit context_t(int io_threads_, int max_sockets_ = ZMQ_MAX_SOCKETS_DFLT) - { - ptr = zmq_ctx_new(); - if(ptr == ZMQ_NULLPTR) - throw error_t(); - - int rc = zmq_ctx_set(ptr, ZMQ_IO_THREADS, io_threads_); - ZMQ_ASSERT(rc == 0); - - rc = zmq_ctx_set(ptr, ZMQ_MAX_SOCKETS, max_sockets_); - ZMQ_ASSERT(rc == 0); - } - -#ifdef ZMQ_HAS_RVALUE_REFS - context_t(context_t&& rhs) ZMQ_NOTHROW : ptr(rhs.ptr) - { - rhs.ptr = ZMQ_NULLPTR; - } - context_t& operator=(context_t&& rhs) ZMQ_NOTHROW - { - close(); - std::swap(ptr, rhs.ptr); - return *this; - } -#endif - - ~context_t() ZMQ_NOTHROW - { - close(); - } - - ZMQ_CPP11_DEPRECATED("from 4.7.0, use set taking zmq::ctxopt instead") - int setctxopt(int option_, int optval_) - { - int rc = zmq_ctx_set(ptr, option_, optval_); - ZMQ_ASSERT(rc == 0); - return rc; - } - - ZMQ_CPP11_DEPRECATED("from 4.7.0, use get taking zmq::ctxopt instead") - int getctxopt(int option_) - { - return zmq_ctx_get(ptr, option_); - } - -#ifdef ZMQ_CPP11 - void set(ctxopt option, int optval) - { - int rc = zmq_ctx_set(ptr, static_cast(option), optval); - if(rc == -1) - throw error_t(); - } - - ZMQ_NODISCARD int get(ctxopt option) - { - int rc = zmq_ctx_get(ptr, static_cast(option)); - // some options have a default value of -1 - // which is unfortunate, and may result in errors - // that don't make sense - if(rc == -1) - throw error_t(); - return rc; - } -#endif - - // Terminates context (see also shutdown()). - void close() ZMQ_NOTHROW - { - if(ptr == ZMQ_NULLPTR) - return; - - int rc; - do - { - rc = zmq_ctx_destroy(ptr); - } while(rc == -1 && errno == EINTR); - - ZMQ_ASSERT(rc == 0); - ptr = ZMQ_NULLPTR; - } - - // Shutdown context in preparation for termination (close()). - // Causes all blocking socket operations and any further - // socket operations to return with ETERM. - void shutdown() ZMQ_NOTHROW - { - if(ptr == ZMQ_NULLPTR) - return; - int rc = zmq_ctx_shutdown(ptr); - ZMQ_ASSERT(rc == 0); - } - - // Be careful with this, it's probably only useful for - // using the C api together with an existing C++ api. - // Normally you should never need to use this. - ZMQ_EXPLICIT operator void*() ZMQ_NOTHROW - { - return ptr; - } - - ZMQ_EXPLICIT operator void const*() const ZMQ_NOTHROW - { - return ptr; - } - - ZMQ_NODISCARD void* handle() ZMQ_NOTHROW - { - return ptr; - } - - ZMQ_DEPRECATED("from 4.7.0, use handle() != nullptr instead") - operator bool() const ZMQ_NOTHROW - { - return ptr != ZMQ_NULLPTR; - } - - void swap(context_t& other) ZMQ_NOTHROW - { - std::swap(ptr, other.ptr); - } - -private: - void* ptr; - - context_t(const context_t&) ZMQ_DELETED_FUNCTION; - void operator=(const context_t&) ZMQ_DELETED_FUNCTION; -}; - -inline void swap(context_t& a, context_t& b) ZMQ_NOTHROW -{ - a.swap(b); -} - -#ifdef ZMQ_CPP11 - -struct recv_buffer_size -{ - size_t size; // number of bytes written to buffer - size_t untruncated_size; // untruncated message size in bytes - - ZMQ_NODISCARD bool truncated() const noexcept - { - return size != untruncated_size; - } -}; - -#if CPPZMQ_HAS_OPTIONAL - -using send_result_t = std::optional; -using recv_result_t = std::optional; -using recv_buffer_result_t = std::optional; - -#else - -namespace detail -{ -// A C++11 type emulating the most basic -// operations of std::optional for trivial types -template -class trivial_optional -{ -public: - static_assert(std::is_trivial::value, "T must be trivial"); - using value_type = T; - - trivial_optional() = default; - trivial_optional(T value) noexcept : _value(value), _has_value(true) - {} - - const T* operator->() const noexcept - { - assert(_has_value); - return &_value; - } - T* operator->() noexcept - { - assert(_has_value); - return &_value; - } - - const T& operator*() const noexcept - { - assert(_has_value); - return _value; - } - T& operator*() noexcept - { - assert(_has_value); - return _value; - } - - T& value() - { - if(!_has_value) - throw std::exception(); - return _value; - } - const T& value() const - { - if(!_has_value) - throw std::exception(); - return _value; - } - - explicit operator bool() const noexcept - { - return _has_value; - } - bool has_value() const noexcept - { - return _has_value; - } - -private: - T _value{}; - bool _has_value{ false }; -}; -} // namespace detail - -using send_result_t = detail::trivial_optional; -using recv_result_t = detail::trivial_optional; -using recv_buffer_result_t = detail::trivial_optional; - -#endif - -namespace detail -{ -template -constexpr T enum_bit_or(T a, T b) noexcept -{ - static_assert(std::is_enum::value, "must be enum"); - using U = typename std::underlying_type::type; - return static_cast(static_cast(a) | static_cast(b)); -} -template -constexpr T enum_bit_and(T a, T b) noexcept -{ - static_assert(std::is_enum::value, "must be enum"); - using U = typename std::underlying_type::type; - return static_cast(static_cast(a) & static_cast(b)); -} -template -constexpr T enum_bit_xor(T a, T b) noexcept -{ - static_assert(std::is_enum::value, "must be enum"); - using U = typename std::underlying_type::type; - return static_cast(static_cast(a) ^ static_cast(b)); -} -template -constexpr T enum_bit_not(T a) noexcept -{ - static_assert(std::is_enum::value, "must be enum"); - using U = typename std::underlying_type::type; - return static_cast(~static_cast(a)); -} -} // namespace detail - -// partially satisfies named requirement BitmaskType -enum class send_flags : int -{ - none = 0, - dontwait = ZMQ_DONTWAIT, - sndmore = ZMQ_SNDMORE -}; - -constexpr send_flags operator|(send_flags a, send_flags b) noexcept -{ - return detail::enum_bit_or(a, b); -} -constexpr send_flags operator&(send_flags a, send_flags b) noexcept -{ - return detail::enum_bit_and(a, b); -} -constexpr send_flags operator^(send_flags a, send_flags b) noexcept -{ - return detail::enum_bit_xor(a, b); -} -constexpr send_flags operator~(send_flags a) noexcept -{ - return detail::enum_bit_not(a); -} - -// partially satisfies named requirement BitmaskType -enum class recv_flags : int -{ - none = 0, - dontwait = ZMQ_DONTWAIT -}; - -constexpr recv_flags operator|(recv_flags a, recv_flags b) noexcept -{ - return detail::enum_bit_or(a, b); -} -constexpr recv_flags operator&(recv_flags a, recv_flags b) noexcept -{ - return detail::enum_bit_and(a, b); -} -constexpr recv_flags operator^(recv_flags a, recv_flags b) noexcept -{ - return detail::enum_bit_xor(a, b); -} -constexpr recv_flags operator~(recv_flags a) noexcept -{ - return detail::enum_bit_not(a); -} - -// mutable_buffer, const_buffer and buffer are based on -// the Networking TS specification, draft: -// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4771.pdf - -class mutable_buffer -{ -public: - constexpr mutable_buffer() noexcept : _data(nullptr), _size(0) - {} - constexpr mutable_buffer(void* p, size_t n) noexcept : _data(p), _size(n) - { -#ifdef ZMQ_EXTENDED_CONSTEXPR - assert(p != nullptr || n == 0); -#endif - } - - constexpr void* data() const noexcept - { - return _data; - } - constexpr size_t size() const noexcept - { - return _size; - } - mutable_buffer& operator+=(size_t n) noexcept - { - // (std::min) is a workaround for when a min macro is defined - const auto shift = (std::min)(n, _size); - _data = static_cast(_data) + shift; - _size -= shift; - return *this; - } - -private: - void* _data; - size_t _size; -}; - -inline mutable_buffer operator+(const mutable_buffer& mb, size_t n) noexcept -{ - return mutable_buffer(static_cast(mb.data()) + (std::min)(n, mb.size()), - mb.size() - (std::min)(n, mb.size())); -} -inline mutable_buffer operator+(size_t n, const mutable_buffer& mb) noexcept -{ - return mb + n; -} - -class const_buffer -{ -public: - constexpr const_buffer() noexcept : _data(nullptr), _size(0) - {} - constexpr const_buffer(const void* p, size_t n) noexcept : _data(p), _size(n) - { -#ifdef ZMQ_EXTENDED_CONSTEXPR - assert(p != nullptr || n == 0); -#endif - } - constexpr const_buffer(const mutable_buffer& mb) noexcept - : _data(mb.data()), _size(mb.size()) - {} - - constexpr const void* data() const noexcept - { - return _data; - } - constexpr size_t size() const noexcept - { - return _size; - } - const_buffer& operator+=(size_t n) noexcept - { - const auto shift = (std::min)(n, _size); - _data = static_cast(_data) + shift; - _size -= shift; - return *this; - } - -private: - const void* _data; - size_t _size; -}; - -inline const_buffer operator+(const const_buffer& cb, size_t n) noexcept -{ - return const_buffer(static_cast(cb.data()) + (std::min)(n, cb.size()), - cb.size() - (std::min)(n, cb.size())); -} -inline const_buffer operator+(size_t n, const const_buffer& cb) noexcept -{ - return cb + n; -} - -// buffer creation - -constexpr mutable_buffer buffer(void* p, size_t n) noexcept -{ - return mutable_buffer(p, n); -} -constexpr const_buffer buffer(const void* p, size_t n) noexcept -{ - return const_buffer(p, n); -} -constexpr mutable_buffer buffer(const mutable_buffer& mb) noexcept -{ - return mb; -} -inline mutable_buffer buffer(const mutable_buffer& mb, size_t n) noexcept -{ - return mutable_buffer(mb.data(), (std::min)(mb.size(), n)); -} -constexpr const_buffer buffer(const const_buffer& cb) noexcept -{ - return cb; -} -inline const_buffer buffer(const const_buffer& cb, size_t n) noexcept -{ - return const_buffer(cb.data(), (std::min)(cb.size(), n)); -} - -namespace detail -{ -template -struct is_buffer -{ - static constexpr bool value = - std::is_same::value || std::is_same::value; -}; - -template -struct is_pod_like -{ - // NOTE: The networking draft N4771 section 16.11 requires - // T in the buffer functions below to be - // trivially copyable OR standard layout. - // Here we decide to be conservative and require both. - static constexpr bool value = - ZMQ_IS_TRIVIALLY_COPYABLE(T) && std::is_standard_layout::value; -}; - -template -constexpr auto seq_size(const C& c) noexcept -> decltype(c.size()) -{ - return c.size(); -} -template -constexpr size_t seq_size(const T (& /*array*/)[N]) noexcept -{ - return N; -} - -template -auto buffer_contiguous_sequence(Seq&& seq) noexcept - -> decltype(buffer(std::addressof(*std::begin(seq)), size_t{})) -{ - using T = typename std::remove_cv< - typename std::remove_reference::type>::type; - static_assert(detail::is_pod_like::value, "T must be POD"); - - const auto size = seq_size(seq); - return buffer(size != 0u ? std::addressof(*std::begin(seq)) : nullptr, - size * sizeof(T)); -} -template -auto buffer_contiguous_sequence(Seq&& seq, size_t n_bytes) noexcept - -> decltype(buffer_contiguous_sequence(seq)) -{ - using T = typename std::remove_cv< - typename std::remove_reference::type>::type; - static_assert(detail::is_pod_like::value, "T must be POD"); - - const auto size = seq_size(seq); - return buffer(size != 0u ? std::addressof(*std::begin(seq)) : nullptr, - (std::min)(size * sizeof(T), n_bytes)); -} - -} // namespace detail - -// C array -template -mutable_buffer buffer(T (&data)[N]) noexcept -{ - return detail::buffer_contiguous_sequence(data); -} -template -mutable_buffer buffer(T (&data)[N], size_t n_bytes) noexcept -{ - return detail::buffer_contiguous_sequence(data, n_bytes); -} -template -const_buffer buffer(const T (&data)[N]) noexcept -{ - return detail::buffer_contiguous_sequence(data); -} -template -const_buffer buffer(const T (&data)[N], size_t n_bytes) noexcept -{ - return detail::buffer_contiguous_sequence(data, n_bytes); -} -// std::array -template -mutable_buffer buffer(std::array& data) noexcept -{ - return detail::buffer_contiguous_sequence(data); -} -template -mutable_buffer buffer(std::array& data, size_t n_bytes) noexcept -{ - return detail::buffer_contiguous_sequence(data, n_bytes); -} -template -const_buffer buffer(std::array& data) noexcept -{ - return detail::buffer_contiguous_sequence(data); -} -template -const_buffer buffer(std::array& data, size_t n_bytes) noexcept -{ - return detail::buffer_contiguous_sequence(data, n_bytes); -} -template -const_buffer buffer(const std::array& data) noexcept -{ - return detail::buffer_contiguous_sequence(data); -} -template -const_buffer buffer(const std::array& data, size_t n_bytes) noexcept -{ - return detail::buffer_contiguous_sequence(data, n_bytes); -} -// std::vector -template -mutable_buffer buffer(std::vector& data) noexcept -{ - return detail::buffer_contiguous_sequence(data); -} -template -mutable_buffer buffer(std::vector& data, size_t n_bytes) noexcept -{ - return detail::buffer_contiguous_sequence(data, n_bytes); -} -template -const_buffer buffer(const std::vector& data) noexcept -{ - return detail::buffer_contiguous_sequence(data); -} -template -const_buffer buffer(const std::vector& data, size_t n_bytes) noexcept -{ - return detail::buffer_contiguous_sequence(data, n_bytes); -} -// std::basic_string -template -mutable_buffer buffer(std::basic_string& data) noexcept -{ - return detail::buffer_contiguous_sequence(data); -} -template -mutable_buffer buffer(std::basic_string& data, - size_t n_bytes) noexcept -{ - return detail::buffer_contiguous_sequence(data, n_bytes); -} -template -const_buffer buffer(const std::basic_string& data) noexcept -{ - return detail::buffer_contiguous_sequence(data); -} -template -const_buffer buffer(const std::basic_string& data, - size_t n_bytes) noexcept -{ - return detail::buffer_contiguous_sequence(data, n_bytes); -} - -#if CPPZMQ_HAS_STRING_VIEW -// std::basic_string_view -template -const_buffer buffer(std::basic_string_view data) noexcept -{ - return detail::buffer_contiguous_sequence(data); -} -template -const_buffer buffer(std::basic_string_view data, size_t n_bytes) noexcept -{ - return detail::buffer_contiguous_sequence(data, n_bytes); -} -#endif - -// Buffer for a string literal (null terminated) -// where the buffer size excludes the terminating character. -// Equivalent to zmq::buffer(std::string_view("...")). -template -constexpr const_buffer str_buffer(const Char (&data)[N]) noexcept -{ - static_assert(detail::is_pod_like::value, "Char must be POD"); -#ifdef ZMQ_EXTENDED_CONSTEXPR - assert(data[N - 1] == Char{ 0 }); -#endif - return const_buffer(static_cast(data), (N - 1) * sizeof(Char)); -} - -namespace literals -{ -constexpr const_buffer operator"" _zbuf(const char* str, size_t len) noexcept -{ - return const_buffer(str, len * sizeof(char)); -} -constexpr const_buffer operator"" _zbuf(const wchar_t* str, size_t len) noexcept -{ - return const_buffer(str, len * sizeof(wchar_t)); -} -constexpr const_buffer operator"" _zbuf(const char16_t* str, size_t len) noexcept -{ - return const_buffer(str, len * sizeof(char16_t)); -} -constexpr const_buffer operator"" _zbuf(const char32_t* str, size_t len) noexcept -{ - return const_buffer(str, len * sizeof(char32_t)); -} -} // namespace literals - -#endif // ZMQ_CPP11 - -#ifdef ZMQ_CPP11 -namespace sockopt -{ -// There are two types of options, -// integral type with known compiler time size (int, bool, int64_t, uint64_t) -// and arrays with dynamic size (strings, binary data). - -// BoolUnit: if true accepts values of type bool (but passed as T into libzmq) -template -struct integral_option -{ -}; - -// NullTerm: -// 0: binary data -// 1: null-terminated string (`getsockopt` size includes null) -// 2: binary (size 32) or Z85 encoder string of size 41 (null included) -template -struct array_option -{ -}; - -#define ZMQ_DEFINE_INTEGRAL_OPT(OPT, NAME, TYPE) \ - using NAME##_t = integral_option; \ - ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME \ - {} -#define ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(OPT, NAME, TYPE) \ - using NAME##_t = integral_option; \ - ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME \ - {} -#define ZMQ_DEFINE_ARRAY_OPT(OPT, NAME) \ - using NAME##_t = array_option; \ - ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME \ - {} -#define ZMQ_DEFINE_ARRAY_OPT_BINARY(OPT, NAME) \ - using NAME##_t = array_option; \ - ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME \ - {} -#define ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(OPT, NAME) \ - using NAME##_t = array_option; \ - ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME \ - {} - -// duplicate definition from libzmq 4.3.3 -#if defined _WIN32 -#if defined _WIN64 -typedef unsigned __int64 cppzmq_fd_t; -#else -typedef unsigned int cppzmq_fd_t; -#endif -#else -typedef int cppzmq_fd_t; -#endif - -#ifdef ZMQ_AFFINITY -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_AFFINITY, affinity, uint64_t); -#endif -#ifdef ZMQ_BACKLOG -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_BACKLOG, backlog, int); -#endif -#ifdef ZMQ_BINDTODEVICE -ZMQ_DEFINE_ARRAY_OPT_BINARY(ZMQ_BINDTODEVICE, bindtodevice); -#endif -#ifdef ZMQ_CONFLATE -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_CONFLATE, conflate, int); -#endif -#ifdef ZMQ_CONNECT_ROUTING_ID -ZMQ_DEFINE_ARRAY_OPT(ZMQ_CONNECT_ROUTING_ID, connect_routing_id); -#endif -#ifdef ZMQ_CONNECT_TIMEOUT -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_CONNECT_TIMEOUT, connect_timeout, int); -#endif -#ifdef ZMQ_CURVE_PUBLICKEY -ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(ZMQ_CURVE_PUBLICKEY, curve_publickey); -#endif -#ifdef ZMQ_CURVE_SECRETKEY -ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(ZMQ_CURVE_SECRETKEY, curve_secretkey); -#endif -#ifdef ZMQ_CURVE_SERVER -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_CURVE_SERVER, curve_server, int); -#endif -#ifdef ZMQ_CURVE_SERVERKEY -ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(ZMQ_CURVE_SERVERKEY, curve_serverkey); -#endif -#ifdef ZMQ_EVENTS -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_EVENTS, events, int); -#endif -#ifdef ZMQ_FD -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_FD, fd, cppzmq_fd_t); -#endif -#ifdef ZMQ_GSSAPI_PLAINTEXT -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_GSSAPI_PLAINTEXT, gssapi_plaintext, int); -#endif -#ifdef ZMQ_GSSAPI_SERVER -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_GSSAPI_SERVER, gssapi_server, int); -#endif -#ifdef ZMQ_GSSAPI_SERVICE_PRINCIPAL -ZMQ_DEFINE_ARRAY_OPT(ZMQ_GSSAPI_SERVICE_PRINCIPAL, gssapi_service_principal); -#endif -#ifdef ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE, - gssapi_service_principal_nametype, int); -#endif -#ifdef ZMQ_GSSAPI_PRINCIPAL -ZMQ_DEFINE_ARRAY_OPT(ZMQ_GSSAPI_PRINCIPAL, gssapi_principal); -#endif -#ifdef ZMQ_GSSAPI_PRINCIPAL_NAMETYPE -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_GSSAPI_PRINCIPAL_NAMETYPE, gssapi_principal_nametype, int); -#endif -#ifdef ZMQ_HANDSHAKE_IVL -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HANDSHAKE_IVL, handshake_ivl, int); -#endif -#ifdef ZMQ_HEARTBEAT_IVL -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_IVL, heartbeat_ivl, int); -#endif -#ifdef ZMQ_HEARTBEAT_TIMEOUT -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_TIMEOUT, heartbeat_timeout, int); -#endif -#ifdef ZMQ_HEARTBEAT_TTL -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_TTL, heartbeat_ttl, int); -#endif -#ifdef ZMQ_IMMEDIATE -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_IMMEDIATE, immediate, int); -#endif -#ifdef ZMQ_INVERT_MATCHING -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_INVERT_MATCHING, invert_matching, int); -#endif -#ifdef ZMQ_IPV6 -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_IPV6, ipv6, int); -#endif -#ifdef ZMQ_LAST_ENDPOINT -ZMQ_DEFINE_ARRAY_OPT(ZMQ_LAST_ENDPOINT, last_endpoint); -#endif -#ifdef ZMQ_LINGER -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_LINGER, linger, int); -#endif -#ifdef ZMQ_MAXMSGSIZE -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MAXMSGSIZE, maxmsgsize, int64_t); -#endif -#ifdef ZMQ_MECHANISM -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MECHANISM, mechanism, int); -#endif -#ifdef ZMQ_METADATA -ZMQ_DEFINE_ARRAY_OPT(ZMQ_METADATA, metadata); -#endif -#ifdef ZMQ_MULTICAST_HOPS -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MULTICAST_HOPS, multicast_hops, int); -#endif -#ifdef ZMQ_MULTICAST_LOOP -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_MULTICAST_LOOP, multicast_loop, int); -#endif -#ifdef ZMQ_MULTICAST_MAXTPDU -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MULTICAST_MAXTPDU, multicast_maxtpdu, int); -#endif -#ifdef ZMQ_PLAIN_SERVER -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_PLAIN_SERVER, plain_server, int); -#endif -#ifdef ZMQ_PLAIN_PASSWORD -ZMQ_DEFINE_ARRAY_OPT(ZMQ_PLAIN_PASSWORD, plain_password); -#endif -#ifdef ZMQ_PLAIN_USERNAME -ZMQ_DEFINE_ARRAY_OPT(ZMQ_PLAIN_USERNAME, plain_username); -#endif -#ifdef ZMQ_USE_FD -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_USE_FD, use_fd, int); -#endif -#ifdef ZMQ_PROBE_ROUTER -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_PROBE_ROUTER, probe_router, int); -#endif -#ifdef ZMQ_RATE -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RATE, rate, int); -#endif -#ifdef ZMQ_RCVBUF -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RCVBUF, rcvbuf, int); -#endif -#ifdef ZMQ_RCVHWM -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RCVHWM, rcvhwm, int); -#endif -#ifdef ZMQ_RCVMORE -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_RCVMORE, rcvmore, int); -#endif -#ifdef ZMQ_RCVTIMEO -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RCVTIMEO, rcvtimeo, int); -#endif -#ifdef ZMQ_RECONNECT_IVL -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECONNECT_IVL, reconnect_ivl, int); -#endif -#ifdef ZMQ_RECONNECT_IVL_MAX -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECONNECT_IVL_MAX, reconnect_ivl_max, int); -#endif -#ifdef ZMQ_RECOVERY_IVL -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECOVERY_IVL, recovery_ivl, int); -#endif -#ifdef ZMQ_REQ_CORRELATE -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_REQ_CORRELATE, req_correlate, int); -#endif -#ifdef ZMQ_REQ_RELAXED -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_REQ_RELAXED, req_relaxed, int); -#endif -#ifdef ZMQ_ROUTER_HANDOVER -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ROUTER_HANDOVER, router_handover, int); -#endif -#ifdef ZMQ_ROUTER_MANDATORY -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ROUTER_MANDATORY, router_mandatory, int); -#endif -#ifdef ZMQ_ROUTER_NOTIFY -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_ROUTER_NOTIFY, router_notify, int); -#endif -#ifdef ZMQ_ROUTING_ID -ZMQ_DEFINE_ARRAY_OPT_BINARY(ZMQ_ROUTING_ID, routing_id); -#endif -#ifdef ZMQ_SNDBUF -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDBUF, sndbuf, int); -#endif -#ifdef ZMQ_SNDHWM -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDHWM, sndhwm, int); -#endif -#ifdef ZMQ_SNDTIMEO -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDTIMEO, sndtimeo, int); -#endif -#ifdef ZMQ_SOCKS_PROXY -ZMQ_DEFINE_ARRAY_OPT(ZMQ_SOCKS_PROXY, socks_proxy); -#endif -#ifdef ZMQ_STREAM_NOTIFY -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_STREAM_NOTIFY, stream_notify, int); -#endif -#ifdef ZMQ_SUBSCRIBE -ZMQ_DEFINE_ARRAY_OPT(ZMQ_SUBSCRIBE, subscribe); -#endif -#ifdef ZMQ_TCP_KEEPALIVE -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE, tcp_keepalive, int); -#endif -#ifdef ZMQ_TCP_KEEPALIVE_CNT -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE_CNT, tcp_keepalive_cnt, int); -#endif -#ifdef ZMQ_TCP_KEEPALIVE_IDLE -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE_IDLE, tcp_keepalive_idle, int); -#endif -#ifdef ZMQ_TCP_KEEPALIVE_INTVL -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE_INTVL, tcp_keepalive_intvl, int); -#endif -#ifdef ZMQ_TCP_MAXRT -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_MAXRT, tcp_maxrt, int); -#endif -#ifdef ZMQ_THREAD_SAFE -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_THREAD_SAFE, thread_safe, int); -#endif -#ifdef ZMQ_TOS -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TOS, tos, int); -#endif -#ifdef ZMQ_TYPE -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TYPE, type, int); -#endif -#ifdef ZMQ_UNSUBSCRIBE -ZMQ_DEFINE_ARRAY_OPT(ZMQ_UNSUBSCRIBE, unsubscribe); -#endif -#ifdef ZMQ_VMCI_BUFFER_SIZE -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_BUFFER_SIZE, vmci_buffer_size, uint64_t); -#endif -#ifdef ZMQ_VMCI_BUFFER_MIN_SIZE -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_BUFFER_MIN_SIZE, vmci_buffer_min_size, uint64_t); -#endif -#ifdef ZMQ_VMCI_BUFFER_MAX_SIZE -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_BUFFER_MAX_SIZE, vmci_buffer_max_size, uint64_t); -#endif -#ifdef ZMQ_VMCI_CONNECT_TIMEOUT -ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_CONNECT_TIMEOUT, vmci_connect_timeout, int); -#endif -#ifdef ZMQ_XPUB_VERBOSE -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_VERBOSE, xpub_verbose, int); -#endif -#ifdef ZMQ_XPUB_VERBOSER -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_VERBOSER, xpub_verboser, int); -#endif -#ifdef ZMQ_XPUB_MANUAL -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_MANUAL, xpub_manual, int); -#endif -#ifdef ZMQ_XPUB_NODROP -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_NODROP, xpub_nodrop, int); -#endif -#ifdef ZMQ_XPUB_WELCOME_MSG -ZMQ_DEFINE_ARRAY_OPT(ZMQ_XPUB_WELCOME_MSG, xpub_welcome_msg); -#endif -#ifdef ZMQ_ZAP_ENFORCE_DOMAIN -ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ZAP_ENFORCE_DOMAIN, zap_enforce_domain, int); -#endif -#ifdef ZMQ_ZAP_DOMAIN -ZMQ_DEFINE_ARRAY_OPT(ZMQ_ZAP_DOMAIN, zap_domain); -#endif - -} // namespace sockopt -#endif // ZMQ_CPP11 - -namespace detail -{ -class socket_base -{ -public: - socket_base() ZMQ_NOTHROW : _handle(ZMQ_NULLPTR) - {} - ZMQ_EXPLICIT socket_base(void* handle) ZMQ_NOTHROW : _handle(handle) - {} - - template - ZMQ_CPP11_DEPRECATED("from 4.7.0, use `set` taking option from zmq::sockopt") - void setsockopt(int option_, T const& optval) - { - setsockopt(option_, &optval, sizeof(T)); - } - - ZMQ_CPP11_DEPRECATED("from 4.7.0, use `set` taking option from zmq::sockopt") - void setsockopt(int option_, const void* optval_, size_t optvallen_) - { - int rc = zmq_setsockopt(_handle, option_, optval_, optvallen_); - if(rc != 0) - throw error_t(); - } - - ZMQ_CPP11_DEPRECATED("from 4.7.0, use `get` taking option from zmq::sockopt") - void getsockopt(int option_, void* optval_, size_t* optvallen_) const - { - int rc = zmq_getsockopt(_handle, option_, optval_, optvallen_); - if(rc != 0) - throw error_t(); - } - - template - ZMQ_CPP11_DEPRECATED("from 4.7.0, use `get` taking option from zmq::sockopt") - T getsockopt(int option_) const - { - T optval; - size_t optlen = sizeof(T); - getsockopt(option_, &optval, &optlen); - return optval; - } - -#ifdef ZMQ_CPP11 - // Set integral socket option, e.g. - // `socket.set(zmq::sockopt::linger, 0)` - template - void set(sockopt::integral_option, const T& val) - { - static_assert(std::is_integral::value, "T must be integral"); - set_option(Opt, &val, sizeof val); - } - - // Set integral socket option from boolean, e.g. - // `socket.set(zmq::sockopt::immediate, false)` - template - void set(sockopt::integral_option, bool val) - { - static_assert(std::is_integral::value, "T must be integral"); - T rep_val = val; - set_option(Opt, &rep_val, sizeof rep_val); - } - - // Set array socket option, e.g. - // `socket.set(zmq::sockopt::plain_username, "foo123")` - template - void set(sockopt::array_option, const char* buf) - { - set_option(Opt, buf, std::strlen(buf)); - } - - // Set array socket option, e.g. - // `socket.set(zmq::sockopt::routing_id, zmq::buffer(id))` - template - void set(sockopt::array_option, const_buffer buf) - { - set_option(Opt, buf.data(), buf.size()); - } - - // Set array socket option, e.g. - // `socket.set(zmq::sockopt::routing_id, id_str)` - template - void set(sockopt::array_option, const std::string& buf) - { - set_option(Opt, buf.data(), buf.size()); - } - -#if CPPZMQ_HAS_STRING_VIEW - // Set array socket option, e.g. - // `socket.set(zmq::sockopt::routing_id, id_str)` - template - void set(sockopt::array_option, std::string_view buf) - { - set_option(Opt, buf.data(), buf.size()); - } -#endif - - // Get scalar socket option, e.g. - // `auto opt = socket.get(zmq::sockopt::linger)` - template - ZMQ_NODISCARD T get(sockopt::integral_option) const - { - static_assert(std::is_integral::value, "T must be integral"); - T val; - size_t size = sizeof val; - get_option(Opt, &val, &size); - assert(size == sizeof val); - return val; - } - - // Get array socket option, writes to buf, returns option size in bytes, e.g. - // `size_t optsize = socket.get(zmq::sockopt::routing_id, zmq::buffer(id))` - template - ZMQ_NODISCARD size_t get(sockopt::array_option, mutable_buffer buf) const - { - size_t size = buf.size(); - get_option(Opt, buf.data(), &size); - return size; - } - - // Get array socket option as string (initializes the string buffer size to init_size) e.g. - // `auto s = socket.get(zmq::sockopt::routing_id)` - // Note: removes the null character from null-terminated string options, - // i.e. the string size excludes the null character. - template - ZMQ_NODISCARD std::string get(sockopt::array_option, - size_t init_size = 1024) const - { - if(NullTerm == 2 && init_size == 1024) - { - init_size = 41; // get as Z85 string - } - std::string str(init_size, '\0'); - size_t size = get(sockopt::array_option{}, buffer(str)); - if(NullTerm == 1) - { - if(size > 0) - { - assert(str[size - 1] == '\0'); - --size; - } - } - else if(NullTerm == 2) - { - assert(size == 32 || size == 41); - if(size == 41) - { - assert(str[size - 1] == '\0'); - --size; - } - } - str.resize(size); - return str; - } -#endif - - void bind(std::string const& addr) - { - bind(addr.c_str()); - } - - void bind(const char* addr_) - { - int rc = zmq_bind(_handle, addr_); - if(rc != 0) - throw error_t(); - } - - void unbind(std::string const& addr) - { - unbind(addr.c_str()); - } - - void unbind(const char* addr_) - { - int rc = zmq_unbind(_handle, addr_); - if(rc != 0) - throw error_t(); - } - - void connect(std::string const& addr) - { - connect(addr.c_str()); - } - - void connect(const char* addr_) - { - int rc = zmq_connect(_handle, addr_); - if(rc != 0) - throw error_t(); - } - - void disconnect(std::string const& addr) - { - disconnect(addr.c_str()); - } - - void disconnect(const char* addr_) - { - int rc = zmq_disconnect(_handle, addr_); - if(rc != 0) - throw error_t(); - } - - bool connected() const ZMQ_NOTHROW - { - return (_handle != ZMQ_NULLPTR); - } - - ZMQ_CPP11_DEPRECATED("from 4.3.1, use send taking a const_buffer and send_flags") - size_t send(const void* buf_, size_t len_, int flags_ = 0) - { - int nbytes = zmq_send(_handle, buf_, len_, flags_); - if(nbytes >= 0) - return static_cast(nbytes); - if(zmq_errno() == EAGAIN) - return 0; - throw error_t(); - } - - ZMQ_CPP11_DEPRECATED("from 4.3.1, use send taking message_t and send_flags") - bool send(message_t& msg_, - int flags_ = 0) // default until removed - { - int nbytes = zmq_msg_send(msg_.handle(), _handle, flags_); - if(nbytes >= 0) - return true; - if(zmq_errno() == EAGAIN) - return false; - throw error_t(); - } - - template - ZMQ_CPP11_DEPRECATED("from 4.4.1, use send taking message_t or buffer (for contiguous " - "ranges), and send_flags") - bool send(T first, T last, int flags_ = 0) - { - zmq::message_t msg(first, last); - int nbytes = zmq_msg_send(msg.handle(), _handle, flags_); - if(nbytes >= 0) - return true; - if(zmq_errno() == EAGAIN) - return false; - throw error_t(); - } - -#ifdef ZMQ_HAS_RVALUE_REFS - ZMQ_CPP11_DEPRECATED("from 4.3.1, use send taking message_t and send_flags") - bool send(message_t&& msg_, - int flags_ = 0) // default until removed - { -#ifdef ZMQ_CPP11 - return send(msg_, static_cast(flags_)).has_value(); -#else - return send(msg_, flags_); -#endif - } -#endif - -#ifdef ZMQ_CPP11 - send_result_t send(const_buffer buf, send_flags flags = send_flags::none) - { - const int nbytes = zmq_send(_handle, buf.data(), buf.size(), static_cast(flags)); - if(nbytes >= 0) - return static_cast(nbytes); - if(zmq_errno() == EAGAIN) - return {}; - throw error_t(); - } - - send_result_t send(message_t& msg, send_flags flags) - { - int nbytes = zmq_msg_send(msg.handle(), _handle, static_cast(flags)); - if(nbytes >= 0) - return static_cast(nbytes); - if(zmq_errno() == EAGAIN) - return {}; - throw error_t(); - } - - send_result_t send(message_t&& msg, send_flags flags) - { - return send(msg, flags); - } -#endif - - ZMQ_CPP11_DEPRECATED("from 4.3.1, use recv taking a mutable_buffer and recv_flags") - size_t recv(void* buf_, size_t len_, int flags_ = 0) - { - int nbytes = zmq_recv(_handle, buf_, len_, flags_); - if(nbytes >= 0) - return static_cast(nbytes); - if(zmq_errno() == EAGAIN) - return 0; - throw error_t(); - } - - ZMQ_CPP11_DEPRECATED("from 4.3.1, use recv taking a reference to message_t and " - "recv_flags") - bool recv(message_t* msg_, int flags_ = 0) - { - int nbytes = zmq_msg_recv(msg_->handle(), _handle, flags_); - if(nbytes >= 0) - return true; - if(zmq_errno() == EAGAIN) - return false; - throw error_t(); - } - -#ifdef ZMQ_CPP11 - ZMQ_NODISCARD - recv_buffer_result_t recv(mutable_buffer buf, recv_flags flags = recv_flags::none) - { - const int nbytes = zmq_recv(_handle, buf.data(), buf.size(), static_cast(flags)); - if(nbytes >= 0) - { - return recv_buffer_size{ (std::min)(static_cast(nbytes), buf.size()), - static_cast(nbytes) }; - } - if(zmq_errno() == EAGAIN) - return {}; - throw error_t(); - } - - ZMQ_NODISCARD - recv_result_t recv(message_t& msg, recv_flags flags = recv_flags::none) - { - const int nbytes = zmq_msg_recv(msg.handle(), _handle, static_cast(flags)); - if(nbytes >= 0) - { - assert(msg.size() == static_cast(nbytes)); - return static_cast(nbytes); - } - if(zmq_errno() == EAGAIN) - return {}; - throw error_t(); - } -#endif - -#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0) - void join(const char* group) - { - int rc = zmq_join(_handle, group); - if(rc != 0) - throw error_t(); - } - - void leave(const char* group) - { - int rc = zmq_leave(_handle, group); - if(rc != 0) - throw error_t(); - } -#endif - - ZMQ_NODISCARD void* handle() ZMQ_NOTHROW - { - return _handle; - } - ZMQ_NODISCARD const void* handle() const ZMQ_NOTHROW - { - return _handle; - } - - ZMQ_EXPLICIT operator bool() const ZMQ_NOTHROW - { - return _handle != ZMQ_NULLPTR; - } - // note: non-const operator bool can be removed once - // operator void* is removed from socket_t - ZMQ_EXPLICIT operator bool() ZMQ_NOTHROW - { - return _handle != ZMQ_NULLPTR; - } - -protected: - void* _handle; - -private: - void set_option(int option_, const void* optval_, size_t optvallen_) - { - int rc = zmq_setsockopt(_handle, option_, optval_, optvallen_); - if(rc != 0) - throw error_t(); - } - - void get_option(int option_, void* optval_, size_t* optvallen_) const - { - int rc = zmq_getsockopt(_handle, option_, optval_, optvallen_); - if(rc != 0) - throw error_t(); - } -}; -} // namespace detail - -#ifdef ZMQ_CPP11 -enum class socket_type : int -{ - req = ZMQ_REQ, - rep = ZMQ_REP, - dealer = ZMQ_DEALER, - router = ZMQ_ROUTER, - pub = ZMQ_PUB, - sub = ZMQ_SUB, - xpub = ZMQ_XPUB, - xsub = ZMQ_XSUB, - push = ZMQ_PUSH, - pull = ZMQ_PULL, -#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0) - server = ZMQ_SERVER, - client = ZMQ_CLIENT, - radio = ZMQ_RADIO, - dish = ZMQ_DISH, -#endif -#if ZMQ_VERSION_MAJOR >= 4 - stream = ZMQ_STREAM, -#endif - pair = ZMQ_PAIR -}; -#endif - -struct from_handle_t -{ - struct _private - { - }; // disabling use other than with from_handle - ZMQ_CONSTEXPR_FN ZMQ_EXPLICIT from_handle_t(_private /*p*/) ZMQ_NOTHROW - {} -}; - -ZMQ_CONSTEXPR_VAR from_handle_t from_handle = from_handle_t(from_handle_t::_private()); - -// A non-owning nullable reference to a socket. -// The reference is invalidated on socket close or destruction. -class socket_ref : public detail::socket_base -{ -public: - socket_ref() ZMQ_NOTHROW : detail::socket_base() - {} -#ifdef ZMQ_CPP11 - socket_ref(std::nullptr_t) ZMQ_NOTHROW : detail::socket_base() - {} -#endif - socket_ref(from_handle_t /*fh*/, void* handle) ZMQ_NOTHROW : detail::socket_base(handle) - {} -}; - -#ifdef ZMQ_CPP11 -inline bool operator==(socket_ref sr, std::nullptr_t /*p*/) ZMQ_NOTHROW -{ - return sr.handle() == nullptr; -} -inline bool operator==(std::nullptr_t /*p*/, socket_ref sr) ZMQ_NOTHROW -{ - return sr.handle() == nullptr; -} -inline bool operator!=(socket_ref sr, std::nullptr_t /*p*/) ZMQ_NOTHROW -{ - return !(sr == nullptr); -} -inline bool operator!=(std::nullptr_t /*p*/, socket_ref sr) ZMQ_NOTHROW -{ - return !(sr == nullptr); -} -#endif - -inline bool operator==(socket_ref a, socket_ref b) ZMQ_NOTHROW -{ - return std::equal_to()(a.handle(), b.handle()); -} -inline bool operator!=(socket_ref a, socket_ref b) ZMQ_NOTHROW -{ - return !(a == b); -} -inline bool operator<(socket_ref a, socket_ref b) ZMQ_NOTHROW -{ - return std::less()(a.handle(), b.handle()); -} -inline bool operator>(socket_ref a, socket_ref b) ZMQ_NOTHROW -{ - return b < a; -} -inline bool operator<=(socket_ref a, socket_ref b) ZMQ_NOTHROW -{ - return !(a > b); -} -inline bool operator>=(socket_ref a, socket_ref b) ZMQ_NOTHROW -{ - return !(a < b); -} - -} // namespace zmq - -#ifdef ZMQ_CPP11 -namespace std -{ -template <> -struct hash -{ - size_t operator()(zmq::socket_ref sr) const ZMQ_NOTHROW - { - return hash()(sr.handle()); - } -}; -} // namespace std -#endif - -namespace zmq -{ -class socket_t : public detail::socket_base -{ - friend class monitor_t; - -public: - socket_t() ZMQ_NOTHROW : detail::socket_base(ZMQ_NULLPTR), ctxptr(ZMQ_NULLPTR) - {} - - socket_t(context_t& context_, int type_) - : detail::socket_base(zmq_socket(context_.handle(), type_)), ctxptr(context_.handle()) - { - if(_handle == ZMQ_NULLPTR) - throw error_t(); - } - -#ifdef ZMQ_CPP11 - socket_t(context_t& context_, socket_type type_) - : socket_t(context_, static_cast(type_)) - {} -#endif - -#ifdef ZMQ_HAS_RVALUE_REFS - socket_t(socket_t&& rhs) ZMQ_NOTHROW : detail::socket_base(rhs._handle), - ctxptr(rhs.ctxptr) - { - rhs._handle = ZMQ_NULLPTR; - rhs.ctxptr = ZMQ_NULLPTR; - } - socket_t& operator=(socket_t&& rhs) ZMQ_NOTHROW - { - close(); - std::swap(_handle, rhs._handle); - std::swap(ctxptr, rhs.ctxptr); - return *this; - } -#endif - - ~socket_t() ZMQ_NOTHROW - { - close(); - } - - operator void*() ZMQ_NOTHROW - { - return _handle; - } - - operator void const*() const ZMQ_NOTHROW - { - return _handle; - } - - void close() ZMQ_NOTHROW - { - if(_handle == ZMQ_NULLPTR) - // already closed - return; - int rc = zmq_close(_handle); - ZMQ_ASSERT(rc == 0); - _handle = ZMQ_NULLPTR; - ctxptr = ZMQ_NULLPTR; - } - - void swap(socket_t& other) ZMQ_NOTHROW - { - std::swap(_handle, other._handle); - std::swap(ctxptr, other.ctxptr); - } - - operator socket_ref() ZMQ_NOTHROW - { - return socket_ref(from_handle, _handle); - } - -private: - void* ctxptr; - - socket_t(const socket_t&) ZMQ_DELETED_FUNCTION; - void operator=(const socket_t&) ZMQ_DELETED_FUNCTION; - - // used by monitor_t - socket_t(void* context_, int type_) - : detail::socket_base(zmq_socket(context_, type_)), ctxptr(context_) - { - if(_handle == ZMQ_NULLPTR) - throw error_t(); - if(ctxptr == ZMQ_NULLPTR) - throw error_t(); - } -}; - -inline void swap(socket_t& a, socket_t& b) ZMQ_NOTHROW -{ - a.swap(b); -} - -ZMQ_DEPRECATED("from 4.3.1, use proxy taking socket_t objects") -inline void proxy(void* frontend, void* backend, void* capture) -{ - int rc = zmq_proxy(frontend, backend, capture); - if(rc != 0) - throw error_t(); -} - -inline void proxy(socket_ref frontend, socket_ref backend, - socket_ref capture = socket_ref()) -{ - int rc = zmq_proxy(frontend.handle(), backend.handle(), capture.handle()); - if(rc != 0) - throw error_t(); -} - -#ifdef ZMQ_HAS_PROXY_STEERABLE -ZMQ_DEPRECATED("from 4.3.1, use proxy_steerable taking socket_t objects") -inline void proxy_steerable(void* frontend, void* backend, void* capture, void* control) -{ - int rc = zmq_proxy_steerable(frontend, backend, capture, control); - if(rc != 0) - throw error_t(); -} - -inline void proxy_steerable(socket_ref frontend, socket_ref backend, socket_ref capture, - socket_ref control) -{ - int rc = zmq_proxy_steerable(frontend.handle(), backend.handle(), capture.handle(), - control.handle()); - if(rc != 0) - throw error_t(); -} -#endif - -class monitor_t -{ -public: - monitor_t() : _socket(), _monitor_socket() - {} - - virtual ~monitor_t() - { - close(); - } - -#ifdef ZMQ_HAS_RVALUE_REFS - monitor_t(monitor_t&& rhs) ZMQ_NOTHROW : _socket(), _monitor_socket() - { - std::swap(_socket, rhs._socket); - std::swap(_monitor_socket, rhs._monitor_socket); - } - - monitor_t& operator=(monitor_t&& rhs) ZMQ_NOTHROW - { - close(); - _socket = socket_ref(); - std::swap(_socket, rhs._socket); - std::swap(_monitor_socket, rhs._monitor_socket); - return *this; - } -#endif - - void monitor(socket_t& socket, std::string const& addr, int events = ZMQ_EVENT_ALL) - { - monitor(socket, addr.c_str(), events); - } - - void monitor(socket_t& socket, const char* addr_, int events = ZMQ_EVENT_ALL) - { - init(socket, addr_, events); - while(true) - { - check_event(-1); - } - } - - void init(socket_t& socket, std::string const& addr, int events = ZMQ_EVENT_ALL) - { - init(socket, addr.c_str(), events); - } - - void init(socket_t& socket, const char* addr_, int events = ZMQ_EVENT_ALL) - { - int rc = zmq_socket_monitor(socket.handle(), addr_, events); - if(rc != 0) - throw error_t(); - - _socket = socket; - _monitor_socket = socket_t(socket.ctxptr, ZMQ_PAIR); - _monitor_socket.connect(addr_); - - on_monitor_started(); - } - - bool check_event(int timeout = 0) - { - assert(_monitor_socket); - - zmq_msg_t eventMsg; - zmq_msg_init(&eventMsg); - - zmq::pollitem_t items[] = { - { _monitor_socket.handle(), 0, ZMQ_POLLIN, 0 }, - }; - - zmq::poll(&items[0], 1, timeout); - - if(items[0].revents & ZMQ_POLLIN) - { - int rc = zmq_msg_recv(&eventMsg, _monitor_socket.handle(), 0); - if(rc == -1 && zmq_errno() == ETERM) - return false; - assert(rc != -1); - } - else - { - zmq_msg_close(&eventMsg); - return false; - } - -#if ZMQ_VERSION_MAJOR >= 4 - const char* data = static_cast(zmq_msg_data(&eventMsg)); - zmq_event_t msgEvent; - memcpy(&msgEvent.event, data, sizeof(uint16_t)); - data += sizeof(uint16_t); - memcpy(&msgEvent.value, data, sizeof(int32_t)); - zmq_event_t* event = &msgEvent; -#else - zmq_event_t* event = static_cast(zmq_msg_data(&eventMsg)); -#endif - -#ifdef ZMQ_NEW_MONITOR_EVENT_LAYOUT - zmq_msg_t addrMsg; - zmq_msg_init(&addrMsg); - int rc = zmq_msg_recv(&addrMsg, _monitor_socket.handle(), 0); - if(rc == -1 && zmq_errno() == ETERM) - { - zmq_msg_close(&eventMsg); - return false; - } - - assert(rc != -1); - const char* str = static_cast(zmq_msg_data(&addrMsg)); - std::string address(str, str + zmq_msg_size(&addrMsg)); - zmq_msg_close(&addrMsg); -#else - // Bit of a hack, but all events in the zmq_event_t union have the same layout so this will work for all event types. - std::string address = event->data.connected.addr; -#endif - -#ifdef ZMQ_EVENT_MONITOR_STOPPED - if(event->event == ZMQ_EVENT_MONITOR_STOPPED) - { - zmq_msg_close(&eventMsg); - return false; - } - -#endif - - switch(event->event) - { - case ZMQ_EVENT_CONNECTED: - on_event_connected(*event, address.c_str()); - break; - case ZMQ_EVENT_CONNECT_DELAYED: - on_event_connect_delayed(*event, address.c_str()); - break; - case ZMQ_EVENT_CONNECT_RETRIED: - on_event_connect_retried(*event, address.c_str()); - break; - case ZMQ_EVENT_LISTENING: - on_event_listening(*event, address.c_str()); - break; - case ZMQ_EVENT_BIND_FAILED: - on_event_bind_failed(*event, address.c_str()); - break; - case ZMQ_EVENT_ACCEPTED: - on_event_accepted(*event, address.c_str()); - break; - case ZMQ_EVENT_ACCEPT_FAILED: - on_event_accept_failed(*event, address.c_str()); - break; - case ZMQ_EVENT_CLOSED: - on_event_closed(*event, address.c_str()); - break; - case ZMQ_EVENT_CLOSE_FAILED: - on_event_close_failed(*event, address.c_str()); - break; - case ZMQ_EVENT_DISCONNECTED: - on_event_disconnected(*event, address.c_str()); - break; -#ifdef ZMQ_BUILD_DRAFT_API -#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3) - case ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL: - on_event_handshake_failed_no_detail(*event, address.c_str()); - break; - case ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL: - on_event_handshake_failed_protocol(*event, address.c_str()); - break; - case ZMQ_EVENT_HANDSHAKE_FAILED_AUTH: - on_event_handshake_failed_auth(*event, address.c_str()); - break; - case ZMQ_EVENT_HANDSHAKE_SUCCEEDED: - on_event_handshake_succeeded(*event, address.c_str()); - break; -#elif ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 1) - case ZMQ_EVENT_HANDSHAKE_FAILED: - on_event_handshake_failed(*event, address.c_str()); - break; - case ZMQ_EVENT_HANDSHAKE_SUCCEED: - on_event_handshake_succeed(*event, address.c_str()); - break; -#endif -#endif - default: - on_event_unknown(*event, address.c_str()); - break; - } - zmq_msg_close(&eventMsg); - - return true; - } - -#ifdef ZMQ_EVENT_MONITOR_STOPPED - void abort() - { - if(_socket) - zmq_socket_monitor(_socket.handle(), ZMQ_NULLPTR, 0); - - _socket = socket_ref(); - } -#endif - virtual void on_monitor_started() - {} - virtual void on_event_connected(const zmq_event_t& event_, const char* addr_) - { - (void)event_; - (void)addr_; - } - virtual void on_event_connect_delayed(const zmq_event_t& event_, const char* addr_) - { - (void)event_; - (void)addr_; - } - virtual void on_event_connect_retried(const zmq_event_t& event_, const char* addr_) - { - (void)event_; - (void)addr_; - } - virtual void on_event_listening(const zmq_event_t& event_, const char* addr_) - { - (void)event_; - (void)addr_; - } - virtual void on_event_bind_failed(const zmq_event_t& event_, const char* addr_) - { - (void)event_; - (void)addr_; - } - virtual void on_event_accepted(const zmq_event_t& event_, const char* addr_) - { - (void)event_; - (void)addr_; - } - virtual void on_event_accept_failed(const zmq_event_t& event_, const char* addr_) - { - (void)event_; - (void)addr_; - } - virtual void on_event_closed(const zmq_event_t& event_, const char* addr_) - { - (void)event_; - (void)addr_; - } - virtual void on_event_close_failed(const zmq_event_t& event_, const char* addr_) - { - (void)event_; - (void)addr_; - } - virtual void on_event_disconnected(const zmq_event_t& event_, const char* addr_) - { - (void)event_; - (void)addr_; - } -#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3) - virtual void on_event_handshake_failed_no_detail(const zmq_event_t& event_, - const char* addr_) - { - (void)event_; - (void)addr_; - } - virtual void on_event_handshake_failed_protocol(const zmq_event_t& event_, - const char* addr_) - { - (void)event_; - (void)addr_; - } - virtual void on_event_handshake_failed_auth(const zmq_event_t& event_, - const char* addr_) - { - (void)event_; - (void)addr_; - } - virtual void on_event_handshake_succeeded(const zmq_event_t& event_, const char* addr_) - { - (void)event_; - (void)addr_; - } -#elif ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 1) - virtual void on_event_handshake_failed(const zmq_event_t& event_, const char* addr_) - { - (void)event_; - (void)addr_; - } - virtual void on_event_handshake_succeed(const zmq_event_t& event_, const char* addr_) - { - (void)event_; - (void)addr_; - } -#endif - virtual void on_event_unknown(const zmq_event_t& event_, const char* addr_) - { - (void)event_; - (void)addr_; - } - -private: - monitor_t(const monitor_t&) ZMQ_DELETED_FUNCTION; - void operator=(const monitor_t&) ZMQ_DELETED_FUNCTION; - - socket_ref _socket; - socket_t _monitor_socket; - - void close() ZMQ_NOTHROW - { - if(_socket) - zmq_socket_monitor(_socket.handle(), ZMQ_NULLPTR, 0); - _monitor_socket.close(); - } -}; - -#if defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER) - -// polling events -enum class event_flags : short -{ - none = 0, - pollin = ZMQ_POLLIN, - pollout = ZMQ_POLLOUT, - pollerr = ZMQ_POLLERR, - pollpri = ZMQ_POLLPRI -}; - -constexpr event_flags operator|(event_flags a, event_flags b) noexcept -{ - return detail::enum_bit_or(a, b); -} -constexpr event_flags operator&(event_flags a, event_flags b) noexcept -{ - return detail::enum_bit_and(a, b); -} -constexpr event_flags operator^(event_flags a, event_flags b) noexcept -{ - return detail::enum_bit_xor(a, b); -} -constexpr event_flags operator~(event_flags a) noexcept -{ - return detail::enum_bit_not(a); -} - -struct no_user_data; - -// layout compatible with zmq_poller_event_t -template -struct poller_event -{ - socket_ref socket; -#ifdef _WIN32 - SOCKET fd; -#else - int fd; -#endif - T* user_data; - event_flags events; -}; - -template -class poller_t -{ -public: - using event_type = poller_event; - - poller_t() : poller_ptr(zmq_poller_new()) - { - if(!poller_ptr) - throw error_t(); - } - - template ::value, - Dummy>::type> - void add(zmq::socket_ref socket, event_flags events, T* user_data) - { - add_impl(socket, events, user_data); - } - - void add(zmq::socket_ref socket, event_flags events) - { - add_impl(socket, events, nullptr); - } - - void remove(zmq::socket_ref socket) - { - if(0 != zmq_poller_remove(poller_ptr.get(), socket.handle())) - { - throw error_t(); - } - } - - void modify(zmq::socket_ref socket, event_flags events) - { - if(0 != - zmq_poller_modify(poller_ptr.get(), socket.handle(), static_cast(events))) - { - throw error_t(); - } - } - - size_t wait_all(std::vector& poller_events, - const std::chrono::milliseconds timeout) - { - int rc = zmq_poller_wait_all( - poller_ptr.get(), reinterpret_cast(poller_events.data()), - static_cast(poller_events.size()), static_cast(timeout.count())); - if(rc > 0) - return static_cast(rc); - -#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3) - if(zmq_errno() == EAGAIN) -#else - if(zmq_errno() == ETIMEDOUT) -#endif - return 0; - - throw error_t(); - } - -private: - struct destroy_poller_t - { - void operator()(void* ptr) noexcept - { - int rc = zmq_poller_destroy(&ptr); - ZMQ_ASSERT(rc == 0); - } - }; - - std::unique_ptr poller_ptr; - - void add_impl(zmq::socket_ref socket, event_flags events, T* user_data) - { - if(0 != zmq_poller_add(poller_ptr.get(), socket.handle(), user_data, - static_cast(events))) - { - throw error_t(); - } - } -}; -#endif // defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER) - -inline std::ostream& operator<<(std::ostream& os, const message_t& msg) -{ - return os << msg.str(); -} - -} // namespace zmq - -#endif // __ZMQ_HPP_INCLUDED__ diff --git a/src/xml_parsing.cpp b/src/xml_parsing.cpp index 8ab66b1fc..da68be99a 100644 --- a/src/xml_parsing.cpp +++ b/src/xml_parsing.cpp @@ -33,7 +33,7 @@ #include #include "behaviortree_cpp/xml_parsing.h" -#include "tinyxml2/tinyxml2.h" +#include "tinyxml2.h" #include #ifdef USING_ROS2 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c4b982576..9c62b335e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -55,6 +55,6 @@ else() endif() +target_include_directories(behaviortree_cpp_test PRIVATE include) target_link_libraries(behaviortree_cpp_test ${BTCPP_LIBRARY} bt_sample_nodes foonathan::lexy) -target_include_directories(behaviortree_cpp_test PRIVATE include ${PROJECT_SOURCE_DIR}/3rdparty) target_compile_definitions(behaviortree_cpp_test PRIVATE BT_TEST_FOLDER="${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index b3d0875f3..b88db7561 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,17 +1,24 @@ -include_directories(${PROJECT_SOURCE_DIR}/3rdparty) - # add_executable(bt4_log_cat bt_log_cat.cpp ) # target_link_libraries(bt4_log_cat ${BTCPP_LIBRARY} ) # install(TARGETS bt4_log_cat # DESTINATION ${BTCPP_BIN_DESTINATION} ) -if( ZMQ_FOUND ) - add_executable(bt4_recorder bt_recorder.cpp ) - target_link_libraries(bt4_recorder ${BTCPP_LIBRARY} ${ZMQ_LIBRARIES}) - install(TARGETS bt4_recorder - DESTINATION ${BTCPP_BIN_DESTINATION} ) -endif() +# FIXME! This target doesn't build because behaviortree_cpp/flatbuffers/BT_logger_generated.h +# doesn't get generated. +# It was being silently ignored because it was included only if ZMQ_FOUND was set, but that check +# was wrong for two reasons: +# 1) The actual variable name set on FindZeroMQ.cmake is ZeroMQ_FOUND not ZMQ_FOUND +# 2) This target does not depend on ZeroMQ, but actually on its C++ wrapper cppzmq. +# Ideally we should check for cppzmq_FOUND, but because that would be only set in non-vendored mode +# and furthermore it would not be set on this scope, I chose to use BTCPP_GROOT_INTERFACE as the check +# for now. +#if( BTCPP_GROOT_INTERFACE ) +# add_executable(bt4_recorder bt_recorder.cpp ) +# target_link_libraries(bt4_recorder ${BTCPP_LIBRARY} cppzmq) +# install(TARGETS bt4_recorder +# DESTINATION ${BTCPP_BIN_DESTINATION} ) +#endif() add_executable(bt4_plugin_manifest bt_plugin_manifest.cpp ) target_link_libraries(bt4_plugin_manifest ${BTCPP_LIBRARY} ) diff --git a/tools/bt_recorder.cpp b/tools/bt_recorder.cpp index c652f9a7f..a1266def2 100644 --- a/tools/bt_recorder.cpp +++ b/tools/bt_recorder.cpp @@ -3,7 +3,7 @@ #include #include #include -#include "cppzmq/zmq.hpp" +#include "zmq.hpp" #include "behaviortree_cpp/flatbuffers/BT_logger_generated.h" // http://zguide.zeromq.org/cpp:interrupt From bd1dd2513042a61e4c51c73c85d6e95a148981d3 Mon Sep 17 00:00:00 2001 From: Eric Riff <57375845+ericriff@users.noreply.github.com> Date: Mon, 6 Oct 2025 12:04:09 -0300 Subject: [PATCH 019/147] Remove unused conan.cmake (#1016) --- cmake/conan.cmake | 1146 --------------------------------------------- 1 file changed, 1146 deletions(-) delete mode 100644 cmake/conan.cmake diff --git a/cmake/conan.cmake b/cmake/conan.cmake deleted file mode 100644 index d36c5ed44..000000000 --- a/cmake/conan.cmake +++ /dev/null @@ -1,1146 +0,0 @@ -# The MIT License (MIT) - -# Copyright (c) 2018 JFrog - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - - - -# This file comes from: https://github.com/conan-io/cmake-conan. Please refer -# to this repository for issues and documentation. - -# Its purpose is to wrap and launch Conan C/C++ Package Manager when cmake is called. -# It will take CMake current settings (os, compiler, compiler version, architecture) -# and translate them to conan settings for installing and retrieving dependencies. - -# It is intended to facilitate developers building projects that have conan dependencies, -# but it is only necessary on the end-user side. It is not necessary to create conan -# packages, in fact it shouldn't be use for that. Check the project documentation. - -# version: 0.19.0-dev - -include(CMakeParseArguments) - -function(_get_msvc_ide_version result) - set(${result} "" PARENT_SCOPE) - if(NOT MSVC_VERSION VERSION_LESS 1400 AND MSVC_VERSION VERSION_LESS 1500) - set(${result} 8 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1500 AND MSVC_VERSION VERSION_LESS 1600) - set(${result} 9 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1600 AND MSVC_VERSION VERSION_LESS 1700) - set(${result} 10 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1700 AND MSVC_VERSION VERSION_LESS 1800) - set(${result} 11 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1800 AND MSVC_VERSION VERSION_LESS 1900) - set(${result} 12 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1900 AND MSVC_VERSION VERSION_LESS 1910) - set(${result} 14 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1910 AND MSVC_VERSION VERSION_LESS 1920) - set(${result} 15 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1920 AND MSVC_VERSION VERSION_LESS 1930) - set(${result} 16 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1930 AND MSVC_VERSION VERSION_LESS 1940) - set(${result} 17 PARENT_SCOPE) - else() - message(FATAL_ERROR "Conan: Unknown MSVC compiler version [${MSVC_VERSION}]") - endif() -endfunction() - -macro(_conan_detect_build_type) - conan_parse_arguments(${ARGV}) - - if(ARGUMENTS_BUILD_TYPE) - set(_CONAN_SETTING_BUILD_TYPE ${ARGUMENTS_BUILD_TYPE}) - elseif(CMAKE_BUILD_TYPE) - set(_CONAN_SETTING_BUILD_TYPE ${CMAKE_BUILD_TYPE}) - else() - message(FATAL_ERROR "Please specify in command line CMAKE_BUILD_TYPE (-DCMAKE_BUILD_TYPE=Release)") - endif() - - string(TOUPPER ${_CONAN_SETTING_BUILD_TYPE} _CONAN_SETTING_BUILD_TYPE_UPPER) - if (_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "DEBUG") - set(_CONAN_SETTING_BUILD_TYPE "Debug") - elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "RELEASE") - set(_CONAN_SETTING_BUILD_TYPE "Release") - elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "RELWITHDEBINFO") - set(_CONAN_SETTING_BUILD_TYPE "RelWithDebInfo") - elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "MINSIZEREL") - set(_CONAN_SETTING_BUILD_TYPE "MinSizeRel") - endif() -endmacro() - -macro(_conan_check_system_name) - #handle -s os setting - if(CMAKE_SYSTEM_NAME AND NOT CMAKE_SYSTEM_NAME STREQUAL "Generic") - #use default conan os setting if CMAKE_SYSTEM_NAME is not defined - set(CONAN_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}) - if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") - set(CONAN_SYSTEM_NAME Macos) - endif() - if(${CMAKE_SYSTEM_NAME} STREQUAL "QNX") - set(CONAN_SYSTEM_NAME Neutrino) - endif() - set(CONAN_SUPPORTED_PLATFORMS Windows Linux Macos Android iOS FreeBSD WindowsStore WindowsCE watchOS tvOS FreeBSD SunOS AIX Arduino Emscripten Neutrino) - list (FIND CONAN_SUPPORTED_PLATFORMS "${CONAN_SYSTEM_NAME}" _index) - if (${_index} GREATER -1) - #check if the cmake system is a conan supported one - set(_CONAN_SETTING_OS ${CONAN_SYSTEM_NAME}) - else() - message(FATAL_ERROR "cmake system ${CONAN_SYSTEM_NAME} is not supported by conan. Use one of ${CONAN_SUPPORTED_PLATFORMS}") - endif() - endif() -endmacro() - -macro(_conan_check_language) - get_property(_languages GLOBAL PROPERTY ENABLED_LANGUAGES) - if (";${_languages};" MATCHES ";CXX;") - set(LANGUAGE CXX) - set(USING_CXX 1) - elseif (";${_languages};" MATCHES ";C;") - set(LANGUAGE C) - set(USING_CXX 0) - else () - message(FATAL_ERROR "Conan: Neither C or C++ was detected as a language for the project. Unable to detect compiler version.") - endif() -endmacro() - -macro(_conan_detect_compiler) - - conan_parse_arguments(${ARGV}) - - if(ARGUMENTS_ARCH) - set(_CONAN_SETTING_ARCH ${ARGUMENTS_ARCH}) - endif() - - if(USING_CXX) - set(_CONAN_SETTING_COMPILER_CPPSTD ${CMAKE_CXX_STANDARD}) - endif() - - if (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL GNU OR ${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL QCC) - # using GCC or QCC - # TODO: Handle other params - string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) - list(GET VERSION_LIST 0 MAJOR) - list(GET VERSION_LIST 1 MINOR) - - if (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL GNU) - set(_CONAN_SETTING_COMPILER gcc) - # mimic Conan client autodetection - if (${MAJOR} GREATER_EQUAL 5) - set(COMPILER_VERSION ${MAJOR}) - else() - set(COMPILER_VERSION ${MAJOR}.${MINOR}) - endif() - elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL QCC) - set(_CONAN_SETTING_COMPILER qcc) - set(COMPILER_VERSION ${MAJOR}.${MINOR}) - endif () - - set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION}) - - if (USING_CXX) - conan_cmake_detect_unix_libcxx(_LIBCXX) - set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) - endif () - elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL Intel) - string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) - list(GET VERSION_LIST 0 MAJOR) - list(GET VERSION_LIST 1 MINOR) - set(COMPILER_VERSION ${MAJOR}) - set(_CONAN_SETTING_COMPILER intel) - set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION}) - if (USING_CXX) - conan_cmake_detect_unix_libcxx(_LIBCXX) - set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) - endif () - elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL AppleClang) - # using AppleClang - string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) - list(GET VERSION_LIST 0 MAJOR) - list(GET VERSION_LIST 1 MINOR) - - # mimic Conan client autodetection - if (${MAJOR} GREATER_EQUAL 13) - set(COMPILER_VERSION ${MAJOR}) - else() - set(COMPILER_VERSION ${MAJOR}.${MINOR}) - endif() - - set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION}) - - set(_CONAN_SETTING_COMPILER apple-clang) - if (USING_CXX) - conan_cmake_detect_unix_libcxx(_LIBCXX) - set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) - endif () - elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL Clang - AND NOT "${CMAKE_${LANGUAGE}_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC" - AND NOT "${CMAKE_${LANGUAGE}_SIMULATE_ID}" STREQUAL "MSVC") - - string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) - list(GET VERSION_LIST 0 MAJOR) - list(GET VERSION_LIST 1 MINOR) - set(_CONAN_SETTING_COMPILER clang) - - # mimic Conan client autodetection - if (${MAJOR} GREATER_EQUAL 8) - set(COMPILER_VERSION ${MAJOR}) - else() - set(COMPILER_VERSION ${MAJOR}.${MINOR}) - endif() - - set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION}) - - if(APPLE) - cmake_policy(GET CMP0025 APPLE_CLANG_POLICY) - if(NOT APPLE_CLANG_POLICY STREQUAL NEW) - message(STATUS "Conan: APPLE and Clang detected. Assuming apple-clang compiler. Set CMP0025 to avoid it") - set(_CONAN_SETTING_COMPILER apple-clang) - endif() - endif() - if (USING_CXX) - conan_cmake_detect_unix_libcxx(_LIBCXX) - set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) - endif () - elseif(${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL MSVC - OR (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL Clang - AND "${CMAKE_${LANGUAGE}_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC" - AND "${CMAKE_${LANGUAGE}_SIMULATE_ID}" STREQUAL "MSVC")) - - set(_VISUAL "Visual Studio") - _get_msvc_ide_version(_VISUAL_VERSION) - if("${_VISUAL_VERSION}" STREQUAL "") - message(FATAL_ERROR "Conan: Visual Studio not recognized") - else() - set(_CONAN_SETTING_COMPILER ${_VISUAL}) - set(_CONAN_SETTING_COMPILER_VERSION ${_VISUAL_VERSION}) - endif() - - if(NOT _CONAN_SETTING_ARCH) - if (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "64") - set(_CONAN_SETTING_ARCH x86_64) - elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "^ARM") - message(STATUS "Conan: Using default ARM architecture from MSVC") - set(_CONAN_SETTING_ARCH armv6) - elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "86") - set(_CONAN_SETTING_ARCH x86) - else () - message(FATAL_ERROR "Conan: Unknown MSVC architecture [${MSVC_${LANGUAGE}_ARCHITECTURE_ID}]") - endif() - endif() - - conan_cmake_detect_vs_runtime(_vs_runtime ${ARGV}) - message(STATUS "Conan: Detected VS runtime: ${_vs_runtime}") - set(_CONAN_SETTING_COMPILER_RUNTIME ${_vs_runtime}) - - if (CMAKE_GENERATOR_TOOLSET) - set(_CONAN_SETTING_COMPILER_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET}) - elseif(CMAKE_VS_PLATFORM_TOOLSET AND (CMAKE_GENERATOR STREQUAL "Ninja")) - set(_CONAN_SETTING_COMPILER_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET}) - endif() - else() - message(FATAL_ERROR "Conan: compiler setup not recognized") - endif() - -endmacro() - -function(conan_cmake_settings result) - #message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER}) - #message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER_ID}) - #message(STATUS "VERSION " ${CMAKE_CXX_COMPILER_VERSION}) - #message(STATUS "FLAGS " ${CMAKE_LANG_FLAGS}) - #message(STATUS "LIB ARCH " ${CMAKE_CXX_LIBRARY_ARCHITECTURE}) - #message(STATUS "BUILD TYPE " ${CMAKE_BUILD_TYPE}) - #message(STATUS "GENERATOR " ${CMAKE_GENERATOR}) - #message(STATUS "GENERATOR WIN64 " ${CMAKE_CL_64}) - - message(STATUS "Conan: Automatic detection of conan settings from cmake") - - conan_parse_arguments(${ARGV}) - - _conan_detect_build_type(${ARGV}) - - _conan_check_system_name() - - _conan_check_language() - - _conan_detect_compiler(${ARGV}) - - # If profile is defined it is used - if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND ARGUMENTS_DEBUG_PROFILE) - set(_APPLIED_PROFILES ${ARGUMENTS_DEBUG_PROFILE}) - elseif(CMAKE_BUILD_TYPE STREQUAL "Release" AND ARGUMENTS_RELEASE_PROFILE) - set(_APPLIED_PROFILES ${ARGUMENTS_RELEASE_PROFILE}) - elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" AND ARGUMENTS_RELWITHDEBINFO_PROFILE) - set(_APPLIED_PROFILES ${ARGUMENTS_RELWITHDEBINFO_PROFILE}) - elseif(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" AND ARGUMENTS_MINSIZEREL_PROFILE) - set(_APPLIED_PROFILES ${ARGUMENTS_MINSIZEREL_PROFILE}) - elseif(ARGUMENTS_PROFILE) - set(_APPLIED_PROFILES ${ARGUMENTS_PROFILE}) - endif() - - foreach(ARG ${_APPLIED_PROFILES}) - set(_SETTINGS ${_SETTINGS} -pr=${ARG}) - endforeach() - foreach(ARG ${ARGUMENTS_PROFILE_BUILD}) - conan_check(VERSION 1.24.0 REQUIRED DETECT_QUIET) - set(_SETTINGS ${_SETTINGS} -pr:b=${ARG}) - endforeach() - - if(NOT _SETTINGS OR ARGUMENTS_PROFILE_AUTO STREQUAL "ALL") - set(ARGUMENTS_PROFILE_AUTO arch build_type compiler compiler.version - compiler.runtime compiler.libcxx compiler.toolset) - endif() - - # remove any manually specified settings from the autodetected settings - foreach(ARG ${ARGUMENTS_SETTINGS}) - string(REGEX MATCH "[^=]*" MANUAL_SETTING "${ARG}") - message(STATUS "Conan: ${MANUAL_SETTING} was added as an argument. Not using the autodetected one.") - list(REMOVE_ITEM ARGUMENTS_PROFILE_AUTO "${MANUAL_SETTING}") - endforeach() - - # Automatic from CMake - foreach(ARG ${ARGUMENTS_PROFILE_AUTO}) - string(TOUPPER ${ARG} _arg_name) - string(REPLACE "." "_" _arg_name ${_arg_name}) - if(_CONAN_SETTING_${_arg_name}) - set(_SETTINGS ${_SETTINGS} -s ${ARG}=${_CONAN_SETTING_${_arg_name}}) - endif() - endforeach() - - foreach(ARG ${ARGUMENTS_SETTINGS}) - set(_SETTINGS ${_SETTINGS} -s ${ARG}) - endforeach() - - message(STATUS "Conan: Settings= ${_SETTINGS}") - - set(${result} ${_SETTINGS} PARENT_SCOPE) -endfunction() - - -function(conan_cmake_detect_unix_libcxx result) - # Take into account any -stdlib in compile options - get_directory_property(compile_options DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_OPTIONS) - string(GENEX_STRIP "${compile_options}" compile_options) - - # Take into account any _GLIBCXX_USE_CXX11_ABI in compile definitions - get_directory_property(defines DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_DEFINITIONS) - string(GENEX_STRIP "${defines}" defines) - - foreach(define ${defines}) - if(define MATCHES "_GLIBCXX_USE_CXX11_ABI") - if(define MATCHES "^-D") - set(compile_options ${compile_options} "${define}") - else() - set(compile_options ${compile_options} "-D${define}") - endif() - endif() - endforeach() - - # add additional compiler options ala cmRulePlaceholderExpander::ExpandRuleVariable - set(EXPAND_CXX_COMPILER ${CMAKE_CXX_COMPILER}) - if(CMAKE_CXX_COMPILER_ARG1) - # CMake splits CXX="foo bar baz" into CMAKE_CXX_COMPILER="foo", CMAKE_CXX_COMPILER_ARG1="bar baz" - # without this, ccache, winegcc, or other wrappers might lose all their arguments - separate_arguments(SPLIT_CXX_COMPILER_ARG1 NATIVE_COMMAND ${CMAKE_CXX_COMPILER_ARG1}) - list(APPEND EXPAND_CXX_COMPILER ${SPLIT_CXX_COMPILER_ARG1}) - endif() - - if(CMAKE_CXX_COMPILE_OPTIONS_TARGET AND CMAKE_CXX_COMPILER_TARGET) - # without --target= we may be calling the wrong underlying GCC - list(APPEND EXPAND_CXX_COMPILER "${CMAKE_CXX_COMPILE_OPTIONS_TARGET}${CMAKE_CXX_COMPILER_TARGET}") - endif() - - if(CMAKE_CXX_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN AND CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN) - list(APPEND EXPAND_CXX_COMPILER "${CMAKE_CXX_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN}${CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN}") - endif() - - if(CMAKE_CXX_COMPILE_OPTIONS_SYSROOT) - # without --sysroot= we may find the wrong #include - if(CMAKE_SYSROOT_COMPILE) - list(APPEND EXPAND_CXX_COMPILER "${CMAKE_CXX_COMPILE_OPTIONS_SYSROOT}${CMAKE_SYSROOT_COMPILE}") - elseif(CMAKE_SYSROOT) - list(APPEND EXPAND_CXX_COMPILER "${CMAKE_CXX_COMPILE_OPTIONS_SYSROOT}${CMAKE_SYSROOT}") - endif() - endif() - - separate_arguments(SPLIT_CXX_FLAGS NATIVE_COMMAND ${CMAKE_CXX_FLAGS}) - - if(CMAKE_OSX_SYSROOT) - set(xcode_sysroot_option "--sysroot=${CMAKE_OSX_SYSROOT}") - endif() - - execute_process( - COMMAND ${CMAKE_COMMAND} -E echo "#include " - COMMAND ${EXPAND_CXX_COMPILER} ${SPLIT_CXX_FLAGS} -x c++ ${xcode_sysroot_option} ${compile_options} -E -dM - - OUTPUT_VARIABLE string_defines - ) - - if(string_defines MATCHES "#define __GLIBCXX__") - # Allow -D_GLIBCXX_USE_CXX11_ABI=ON/OFF as argument to cmake - if(DEFINED _GLIBCXX_USE_CXX11_ABI) - if(_GLIBCXX_USE_CXX11_ABI) - set(${result} libstdc++11 PARENT_SCOPE) - return() - else() - set(${result} libstdc++ PARENT_SCOPE) - return() - endif() - endif() - - if(string_defines MATCHES "#define _GLIBCXX_USE_CXX11_ABI 1\n") - set(${result} libstdc++11 PARENT_SCOPE) - else() - # Either the compiler is missing the define because it is old, and so - # it can't use the new abi, or the compiler was configured to use the - # old abi by the user or distro (e.g. devtoolset on RHEL/CentOS) - set(${result} libstdc++ PARENT_SCOPE) - endif() - else() - set(${result} libc++ PARENT_SCOPE) - endif() -endfunction() - -function(conan_cmake_detect_vs_runtime result) - - conan_parse_arguments(${ARGV}) - if(ARGUMENTS_BUILD_TYPE) - set(build_type "${ARGUMENTS_BUILD_TYPE}") - elseif(CMAKE_BUILD_TYPE) - set(build_type "${CMAKE_BUILD_TYPE}") - else() - message(FATAL_ERROR "Please specify in command line CMAKE_BUILD_TYPE (-DCMAKE_BUILD_TYPE=Release)") - endif() - - if(build_type) - string(TOUPPER "${build_type}" build_type) - endif() - set(variables CMAKE_CXX_FLAGS_${build_type} CMAKE_C_FLAGS_${build_type} CMAKE_CXX_FLAGS CMAKE_C_FLAGS) - foreach(variable ${variables}) - if(NOT "${${variable}}" STREQUAL "") - string(REPLACE " " ";" flags "${${variable}}") - foreach (flag ${flags}) - if("${flag}" STREQUAL "/MD" OR "${flag}" STREQUAL "/MDd" OR "${flag}" STREQUAL "/MT" OR "${flag}" STREQUAL "/MTd") - string(SUBSTRING "${flag}" 1 -1 runtime) - set(${result} "${runtime}" PARENT_SCOPE) - return() - endif() - endforeach() - endif() - endforeach() - if("${build_type}" STREQUAL "DEBUG") - set(${result} "MDd" PARENT_SCOPE) - else() - set(${result} "MD" PARENT_SCOPE) - endif() -endfunction() - -function(_collect_settings result) - set(ARGUMENTS_PROFILE_AUTO arch build_type compiler compiler.version - compiler.runtime compiler.libcxx compiler.toolset - compiler.cppstd) - foreach(ARG ${ARGUMENTS_PROFILE_AUTO}) - string(TOUPPER ${ARG} _arg_name) - string(REPLACE "." "_" _arg_name ${_arg_name}) - if(_CONAN_SETTING_${_arg_name}) - set(detected_setings ${detected_setings} ${ARG}=${_CONAN_SETTING_${_arg_name}}) - endif() - endforeach() - set(${result} ${detected_setings} PARENT_SCOPE) -endfunction() - -function(conan_cmake_autodetect detected_settings) - _conan_detect_build_type(${ARGV}) - _conan_check_system_name() - _conan_check_language() - _conan_detect_compiler(${ARGV}) - _collect_settings(collected_settings) - set(${detected_settings} ${collected_settings} PARENT_SCOPE) -endfunction() - -macro(conan_parse_arguments) - set(options BASIC_SETUP CMAKE_TARGETS UPDATE KEEP_RPATHS NO_LOAD NO_OUTPUT_DIRS - OUTPUT_QUIET NO_IMPORTS SKIP_STD) - set(oneValueArgs CONANFILE ARCH BUILD_TYPE INSTALL_FOLDER OUTPUT_FOLDER CONAN_COMMAND) - set(multiValueArgs DEBUG_PROFILE RELEASE_PROFILE RELWITHDEBINFO_PROFILE MINSIZEREL_PROFILE - PROFILE REQUIRES OPTIONS IMPORTS SETTINGS BUILD ENV GENERATORS PROFILE_AUTO - INSTALL_ARGS CONFIGURATION_TYPES PROFILE_BUILD BUILD_REQUIRES) - cmake_parse_arguments(ARGUMENTS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) -endmacro() - -function(old_conan_cmake_install) - # Calls "conan install" - # Argument BUILD is equivalent to --build={missing, PkgName,...} or - # --build when argument is 'BUILD all' (which builds all packages from source) - # Argument CONAN_COMMAND, to specify the conan path, e.g. in case of running from source - # cmake does not identify conan as command, even if it is +x and it is in the path - conan_parse_arguments(${ARGV}) - - if(CONAN_CMAKE_MULTI) - set(ARGUMENTS_GENERATORS ${ARGUMENTS_GENERATORS} cmake_multi) - else() - set(ARGUMENTS_GENERATORS ${ARGUMENTS_GENERATORS} cmake) - endif() - - set(CONAN_BUILD_POLICY "") - foreach(ARG ${ARGUMENTS_BUILD}) - if(${ARG} STREQUAL "all") - set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build) - break() - else() - set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build=${ARG}) - endif() - endforeach() - if(ARGUMENTS_CONAN_COMMAND) - set(CONAN_CMD ${ARGUMENTS_CONAN_COMMAND}) - else() - conan_check(REQUIRED) - endif() - set(CONAN_OPTIONS "") - if(ARGUMENTS_CONANFILE) - if(IS_ABSOLUTE ${ARGUMENTS_CONANFILE}) - set(CONANFILE ${ARGUMENTS_CONANFILE}) - else() - set(CONANFILE ${CMAKE_CURRENT_SOURCE_DIR}/${ARGUMENTS_CONANFILE}) - endif() - else() - set(CONANFILE ".") - endif() - foreach(ARG ${ARGUMENTS_OPTIONS}) - set(CONAN_OPTIONS ${CONAN_OPTIONS} -o=${ARG}) - endforeach() - if(ARGUMENTS_UPDATE) - set(CONAN_INSTALL_UPDATE --update) - endif() - if(ARGUMENTS_NO_IMPORTS) - set(CONAN_INSTALL_NO_IMPORTS --no-imports) - endif() - set(CONAN_INSTALL_FOLDER "") - if(ARGUMENTS_INSTALL_FOLDER) - set(CONAN_INSTALL_FOLDER -if=${ARGUMENTS_INSTALL_FOLDER}) - endif() - set(CONAN_OUTPUT_FOLDER "") - if(ARGUMENTS_OUTPUT_FOLDER) - set(CONAN_OUTPUT_FOLDER -of=${ARGUMENTS_OUTPUT_FOLDER}) - endif() - foreach(ARG ${ARGUMENTS_GENERATORS}) - set(CONAN_GENERATORS ${CONAN_GENERATORS} -g=${ARG}) - endforeach() - foreach(ARG ${ARGUMENTS_ENV}) - set(CONAN_ENV_VARS ${CONAN_ENV_VARS} -e=${ARG}) - endforeach() - set(conan_args install ${CONANFILE} ${settings} ${CONAN_ENV_VARS} ${CONAN_GENERATORS} ${CONAN_BUILD_POLICY} ${CONAN_INSTALL_UPDATE} ${CONAN_INSTALL_NO_IMPORTS} ${CONAN_OPTIONS} ${CONAN_INSTALL_FOLDER} ${ARGUMENTS_INSTALL_ARGS}) - - string (REPLACE ";" " " _conan_args "${conan_args}") - message(STATUS "Conan executing: ${CONAN_CMD} ${_conan_args}") - - if(ARGUMENTS_OUTPUT_QUIET) - execute_process(COMMAND ${CONAN_CMD} ${conan_args} - RESULT_VARIABLE return_code - OUTPUT_VARIABLE conan_output - ERROR_VARIABLE conan_output - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - else() - execute_process(COMMAND ${CONAN_CMD} ${conan_args} - RESULT_VARIABLE return_code - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - endif() - - if(NOT "${return_code}" STREQUAL "0") - message(FATAL_ERROR "Conan install failed='${return_code}'") - endif() - -endfunction() - -function(conan_cmake_install) - if(DEFINED CONAN_COMMAND) - set(CONAN_CMD ${CONAN_COMMAND}) - else() - conan_check(REQUIRED) - endif() - - set(installOptions UPDATE NO_IMPORTS OUTPUT_QUIET ERROR_QUIET) - set(installOneValueArgs PATH_OR_REFERENCE REFERENCE REMOTE LOCKFILE LOCKFILE_OUT LOCKFILE_NODE_ID INSTALL_FOLDER OUTPUT_FOLDER) - set(installMultiValueArgs GENERATOR BUILD ENV ENV_HOST ENV_BUILD OPTIONS_HOST OPTIONS OPTIONS_BUILD PROFILE - PROFILE_HOST PROFILE_BUILD SETTINGS SETTINGS_HOST SETTINGS_BUILD CONF CONF_HOST CONF_BUILD) - cmake_parse_arguments(ARGS "${installOptions}" "${installOneValueArgs}" "${installMultiValueArgs}" ${ARGN}) - foreach(arg ${installOptions}) - if(ARGS_${arg}) - set(${arg} ${${arg}} ${ARGS_${arg}}) - endif() - endforeach() - foreach(arg ${installOneValueArgs}) - if(DEFINED ARGS_${arg}) - if("${arg}" STREQUAL "REMOTE") - set(flag "--remote") - elseif("${arg}" STREQUAL "LOCKFILE") - set(flag "--lockfile") - elseif("${arg}" STREQUAL "LOCKFILE_OUT") - set(flag "--lockfile-out") - elseif("${arg}" STREQUAL "LOCKFILE_NODE_ID") - set(flag "--lockfile-node-id") - elseif("${arg}" STREQUAL "INSTALL_FOLDER") - set(flag "--install-folder") - elseif("${arg}" STREQUAL "OUTPUT_FOLDER") - set(flag "--output-folder") - endif() - set(${arg} ${${arg}} ${flag} ${ARGS_${arg}}) - endif() - endforeach() - foreach(arg ${installMultiValueArgs}) - if(DEFINED ARGS_${arg}) - if("${arg}" STREQUAL "GENERATOR") - set(flag "--generator") - elseif("${arg}" STREQUAL "BUILD") - set(flag "--build") - elseif("${arg}" STREQUAL "ENV") - set(flag "--env") - elseif("${arg}" STREQUAL "ENV_HOST") - set(flag "--env:host") - elseif("${arg}" STREQUAL "ENV_BUILD") - set(flag "--env:build") - elseif("${arg}" STREQUAL "OPTIONS") - set(flag "--options") - elseif("${arg}" STREQUAL "OPTIONS_HOST") - set(flag "--options:host") - elseif("${arg}" STREQUAL "OPTIONS_BUILD") - set(flag "--options:build") - elseif("${arg}" STREQUAL "PROFILE") - set(flag "--profile") - elseif("${arg}" STREQUAL "PROFILE_HOST") - set(flag "--profile:host") - elseif("${arg}" STREQUAL "PROFILE_BUILD") - set(flag "--profile:build") - elseif("${arg}" STREQUAL "SETTINGS") - set(flag "--settings") - elseif("${arg}" STREQUAL "SETTINGS_HOST") - set(flag "--settings:host") - elseif("${arg}" STREQUAL "SETTINGS_BUILD") - set(flag "--settings:build") - elseif("${arg}" STREQUAL "CONF") - set(flag "--conf") - elseif("${arg}" STREQUAL "CONF_HOST") - set(flag "--conf:host") - elseif("${arg}" STREQUAL "CONF_BUILD") - set(flag "--conf:build") - endif() - list(LENGTH ARGS_${arg} numargs) - foreach(item ${ARGS_${arg}}) - if(${item} STREQUAL "all" AND ${arg} STREQUAL "BUILD") - set(${arg} "--build") - break() - endif() - set(${arg} ${${arg}} ${flag} ${item}) - endforeach() - endif() - endforeach() - if(DEFINED UPDATE) - set(UPDATE --update) - endif() - if(DEFINED NO_IMPORTS) - set(NO_IMPORTS --no-imports) - endif() - set(install_args install ${PATH_OR_REFERENCE} ${REFERENCE} ${UPDATE} ${NO_IMPORTS} ${REMOTE} - ${LOCKFILE} ${LOCKFILE_OUT} ${LOCKFILE_NODE_ID} ${INSTALL_FOLDER} - ${OUTPUT_FOLDER} ${GENERATOR} ${BUILD} ${ENV} ${ENV_HOST} ${ENV_BUILD} - ${OPTIONS} ${OPTIONS_HOST} ${OPTIONS_BUILD} ${PROFILE} ${PROFILE_HOST} - ${PROFILE_BUILD} ${SETTINGS} ${SETTINGS_HOST} ${SETTINGS_BUILD} - ${CONF} ${CONF_HOST} ${CONF_BUILD}) - - string(REPLACE ";" " " _install_args "${install_args}") - message(STATUS "Conan executing: ${CONAN_CMD} ${_install_args}") - - if(ARGS_OUTPUT_QUIET) - set(OUTPUT_OPT OUTPUT_QUIET) - endif() - if(ARGS_ERROR_QUIET) - set(ERROR_OPT ERROR_QUIET) - endif() - - execute_process(COMMAND ${CONAN_CMD} ${install_args} - RESULT_VARIABLE return_code - ${OUTPUT_OPT} - ${ERROR_OPT} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - - if(NOT "${return_code}" STREQUAL "0") - if (ARGS_ERROR_QUIET) - message(WARNING "Conan install failed='${return_code}'") - else() - message(FATAL_ERROR "Conan install failed='${return_code}'") - endif() - endif() - -endfunction() - -function(conan_cmake_lock_create) - if(DEFINED CONAN_COMMAND) - set(CONAN_CMD ${CONAN_COMMAND}) - else() - conan_check(REQUIRED) - endif() - - set(lockCreateOptions UPDATE BASE OUTPUT_QUIET ERROR_QUIET) - set(lockCreateOneValueArgs PATH REFERENCE REMOTE LOCKFILE LOCKFILE_OUT) - set(lockCreateMultiValueArgs BUILD ENV ENV_HOST ENV_BUILD OPTIONS_HOST OPTIONS OPTIONS_BUILD PROFILE - PROFILE_HOST PROFILE_BUILD SETTINGS SETTINGS_HOST SETTINGS_BUILD) - cmake_parse_arguments(ARGS "${lockCreateOptions}" "${lockCreateOneValueArgs}" "${lockCreateMultiValueArgs}" ${ARGN}) - foreach(arg ${lockCreateOptions}) - if(ARGS_${arg}) - set(${arg} ${${arg}} ${ARGS_${arg}}) - endif() - endforeach() - foreach(arg ${lockCreateOneValueArgs}) - if(DEFINED ARGS_${arg}) - if("${arg}" STREQUAL "REMOTE") - set(flag "--remote") - elseif("${arg}" STREQUAL "LOCKFILE") - set(flag "--lockfile") - elseif("${arg}" STREQUAL "LOCKFILE_OUT") - set(flag "--lockfile-out") - endif() - set(${arg} ${${arg}} ${flag} ${ARGS_${arg}}) - endif() - endforeach() - foreach(arg ${lockCreateMultiValueArgs}) - if(DEFINED ARGS_${arg}) - if("${arg}" STREQUAL "BUILD") - set(flag "--build") - elseif("${arg}" STREQUAL "ENV") - set(flag "--env") - elseif("${arg}" STREQUAL "ENV_HOST") - set(flag "--env:host") - elseif("${arg}" STREQUAL "ENV_BUILD") - set(flag "--env:build") - elseif("${arg}" STREQUAL "OPTIONS") - set(flag "--options") - elseif("${arg}" STREQUAL "OPTIONS_HOST") - set(flag "--options:host") - elseif("${arg}" STREQUAL "OPTIONS_BUILD") - set(flag "--options:build") - elseif("${arg}" STREQUAL "PROFILE") - set(flag "--profile") - elseif("${arg}" STREQUAL "PROFILE_HOST") - set(flag "--profile:host") - elseif("${arg}" STREQUAL "PROFILE_BUILD") - set(flag "--profile:build") - elseif("${arg}" STREQUAL "SETTINGS") - set(flag "--settings") - elseif("${arg}" STREQUAL "SETTINGS_HOST") - set(flag "--settings:host") - elseif("${arg}" STREQUAL "SETTINGS_BUILD") - set(flag "--settings:build") - endif() - list(LENGTH ARGS_${arg} numargs) - foreach(item ${ARGS_${arg}}) - if(${item} STREQUAL "all" AND ${arg} STREQUAL "BUILD") - set(${arg} "--build") - break() - endif() - set(${arg} ${${arg}} ${flag} ${item}) - endforeach() - endif() - endforeach() - if(DEFINED UPDATE) - set(UPDATE --update) - endif() - if(DEFINED BASE) - set(BASE --base) - endif() - set(lock_create_Args lock create ${PATH} ${REFERENCE} ${UPDATE} ${BASE} ${REMOTE} ${LOCKFILE} ${LOCKFILE_OUT} ${LOCKFILE_NODE_ID} ${INSTALL_FOLDER} - ${GENERATOR} ${BUILD} ${ENV} ${ENV_HOST} ${ENV_BUILD} ${OPTIONS} ${OPTIONS_HOST} ${OPTIONS_BUILD} - ${PROFILE} ${PROFILE_HOST} ${PROFILE_BUILD} ${SETTINGS} ${SETTINGS_HOST} ${SETTINGS_BUILD}) - - string(REPLACE ";" " " _lock_create_Args "${lock_create_Args}") - message(STATUS "Conan executing: ${CONAN_CMD} ${_lock_create_Args}") - - if(ARGS_OUTPUT_QUIET) - set(OUTPUT_OPT OUTPUT_QUIET) - endif() - if(ARGS_ERROR_QUIET) - set(ERROR_OPT ERROR_QUIET) - endif() - - execute_process(COMMAND ${CONAN_CMD} ${lock_create_Args} - RESULT_VARIABLE return_code - ${OUTPUT_OPT} - ${ERROR_OPT} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - - if(NOT "${return_code}" STREQUAL "0") - if (ARGS_ERROR_QUIET) - message(WARNING "Conan lock create failed='${return_code}'") - else() - message(FATAL_ERROR "Conan lock create failed='${return_code}'") - endif() - endif() -endfunction() - -function(conan_cmake_setup_conanfile) - conan_parse_arguments(${ARGV}) - if(ARGUMENTS_CONANFILE) - get_filename_component(_CONANFILE_NAME ${ARGUMENTS_CONANFILE} NAME) - # configure_file will make sure cmake re-runs when conanfile is updated - configure_file(${ARGUMENTS_CONANFILE} ${CMAKE_CURRENT_BINARY_DIR}/${_CONANFILE_NAME}.junk COPYONLY) - file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/${_CONANFILE_NAME}.junk) - else() - conan_cmake_generate_conanfile(ON ${ARGV}) - endif() -endfunction() - -function(conan_cmake_configure) - conan_cmake_generate_conanfile(OFF ${ARGV}) -endfunction() - -# Generate, writing in disk a conanfile.txt with the requires, options, and imports -# specified as arguments -# This will be considered as temporary file, generated in CMAKE_CURRENT_BINARY_DIR) -function(conan_cmake_generate_conanfile DEFAULT_GENERATOR) - - conan_parse_arguments(${ARGV}) - - set(_FN "${CMAKE_CURRENT_BINARY_DIR}/conanfile.txt") - file(WRITE ${_FN} "") - - if(DEFINED ARGUMENTS_REQUIRES) - file(APPEND ${_FN} "[requires]\n") - foreach(REQUIRE ${ARGUMENTS_REQUIRES}) - file(APPEND ${_FN} ${REQUIRE} "\n") - endforeach() - endif() - - if (DEFAULT_GENERATOR OR DEFINED ARGUMENTS_GENERATORS) - file(APPEND ${_FN} "[generators]\n") - if (DEFAULT_GENERATOR) - file(APPEND ${_FN} "cmake\n") - endif() - if (DEFINED ARGUMENTS_GENERATORS) - foreach(GENERATOR ${ARGUMENTS_GENERATORS}) - file(APPEND ${_FN} ${GENERATOR} "\n") - endforeach() - endif() - endif() - - if(DEFINED ARGUMENTS_BUILD_REQUIRES) - file(APPEND ${_FN} "[build_requires]\n") - foreach(BUILD_REQUIRE ${ARGUMENTS_BUILD_REQUIRES}) - file(APPEND ${_FN} ${BUILD_REQUIRE} "\n") - endforeach() - endif() - - if(DEFINED ARGUMENTS_IMPORTS) - file(APPEND ${_FN} "[imports]\n") - foreach(IMPORTS ${ARGUMENTS_IMPORTS}) - file(APPEND ${_FN} ${IMPORTS} "\n") - endforeach() - endif() - - if(DEFINED ARGUMENTS_OPTIONS) - file(APPEND ${_FN} "[options]\n") - foreach(OPTION ${ARGUMENTS_OPTIONS}) - file(APPEND ${_FN} ${OPTION} "\n") - endforeach() - endif() - -endfunction() - - -macro(conan_load_buildinfo) - if(CONAN_CMAKE_MULTI) - set(_CONANBUILDINFO conanbuildinfo_multi.cmake) - else() - set(_CONANBUILDINFO conanbuildinfo.cmake) - endif() - if(ARGUMENTS_INSTALL_FOLDER) - set(_CONANBUILDINFOFOLDER ${ARGUMENTS_INSTALL_FOLDER}) - else() - set(_CONANBUILDINFOFOLDER ${CMAKE_CURRENT_BINARY_DIR}) - endif() - # Checks for the existence of conanbuildinfo.cmake, and loads it - # important that it is macro, so variables defined at parent scope - if(EXISTS "${_CONANBUILDINFOFOLDER}/${_CONANBUILDINFO}") - message(STATUS "Conan: Loading ${_CONANBUILDINFO}") - include(${_CONANBUILDINFOFOLDER}/${_CONANBUILDINFO}) - else() - message(FATAL_ERROR "${_CONANBUILDINFO} doesn't exist in ${CMAKE_CURRENT_BINARY_DIR}") - endif() -endmacro() - - -macro(conan_cmake_run) - conan_parse_arguments(${ARGV}) - - if(ARGUMENTS_CONFIGURATION_TYPES AND NOT CMAKE_CONFIGURATION_TYPES) - message(WARNING "CONFIGURATION_TYPES should only be specified for multi-configuration generators") - elseif(ARGUMENTS_CONFIGURATION_TYPES AND ARGUMENTS_BUILD_TYPE) - message(WARNING "CONFIGURATION_TYPES and BUILD_TYPE arguments should not be defined at the same time.") - endif() - - if(CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE AND NOT CONAN_EXPORTED - AND NOT ARGUMENTS_BUILD_TYPE) - set(CONAN_CMAKE_MULTI ON) - if (NOT ARGUMENTS_CONFIGURATION_TYPES) - set(ARGUMENTS_CONFIGURATION_TYPES "Release;Debug") - endif() - message(STATUS "Conan: Using cmake-multi generator") - else() - set(CONAN_CMAKE_MULTI OFF) - endif() - - if(NOT CONAN_EXPORTED) - conan_cmake_setup_conanfile(${ARGV}) - if(CONAN_CMAKE_MULTI) - foreach(CMAKE_BUILD_TYPE ${ARGUMENTS_CONFIGURATION_TYPES}) - set(ENV{CONAN_IMPORT_PATH} ${CMAKE_BUILD_TYPE}) - conan_cmake_settings(settings ${ARGV}) - old_conan_cmake_install(SETTINGS ${settings} ${ARGV}) - endforeach() - set(CMAKE_BUILD_TYPE) - else() - conan_cmake_settings(settings ${ARGV}) - old_conan_cmake_install(SETTINGS ${settings} ${ARGV}) - endif() - endif() - - if (NOT ARGUMENTS_NO_LOAD) - conan_load_buildinfo() - endif() - - if(ARGUMENTS_BASIC_SETUP) - foreach(_option CMAKE_TARGETS KEEP_RPATHS NO_OUTPUT_DIRS SKIP_STD) - if(ARGUMENTS_${_option}) - if(${_option} STREQUAL "CMAKE_TARGETS") - list(APPEND _setup_options "TARGETS") - else() - list(APPEND _setup_options ${_option}) - endif() - endif() - endforeach() - conan_basic_setup(${_setup_options}) - endif() -endmacro() - -function(conan_version result) - set(${result} "" PARENT_SCOPE) - - if(NOT CONAN_CMD) - find_program(CONAN_CMD conan) - if(NOT CONAN_CMD AND CONAN_REQUIRED) - message(FATAL_ERROR "Conan executable not found! Please install conan.") - endif() - endif() - - execute_process(COMMAND ${CONAN_CMD} --version - RESULT_VARIABLE return_code - OUTPUT_VARIABLE CONAN_VERSION_OUTPUT - ERROR_VARIABLE CONAN_VERSION_OUTPUT) - - if(NOT "${return_code}" STREQUAL "0") - message(FATAL_ERROR "Conan --version failed='${return_code}'") - endif() - - string(REGEX MATCH ".*Conan version ([0-9]+\\.[0-9]+\\.[0-9]+)" FOO "${CONAN_VERSION_OUTPUT}") - - set(${result} ${CMAKE_MATCH_1} PARENT_SCOPE) -endfunction() - -macro(conan_check) - # Checks conan availability in PATH - # Arguments REQUIRED, DETECT_QUIET and VERSION are optional - # Example usage: - # conan_check(VERSION 1.0.0 REQUIRED) - set(options REQUIRED DETECT_QUIET) - set(oneValueArgs VERSION) - cmake_parse_arguments(CONAN "${options}" "${oneValueArgs}" "" ${ARGN}) - if(NOT CONAN_DETECT_QUIET) - message(STATUS "Conan: checking conan executable") - endif() - - find_program(CONAN_CMD conan) - if(NOT CONAN_CMD AND CONAN_REQUIRED) - message(FATAL_ERROR "Conan executable not found! Please install conan.") - endif() - if(NOT CONAN_DETECT_QUIET) - message(STATUS "Conan: Found program ${CONAN_CMD}") - endif() - - conan_version(CONAN_DETECTED_VERSION) - - if(NOT CONAN_DETECT_QUIET) - message(STATUS "Conan: Version found ${CONAN_DETECTED_VERSION}") - endif() - - if(DEFINED CONAN_VERSION) - if(${CONAN_DETECTED_VERSION} VERSION_LESS ${CONAN_VERSION}) - message(FATAL_ERROR "Conan outdated. Installed: ${CONAN_DETECTED_VERSION}, \ - required: ${CONAN_VERSION}. Consider updating via 'pip \ - install conan==${CONAN_VERSION}'.") - endif() - endif() -endmacro() - -function(conan_add_remote) - # Adds a remote - # Arguments URL and NAME are required, INDEX, COMMAND and VERIFY_SSL are optional - # Example usage: - # conan_add_remote(NAME bincrafters INDEX 1 - # URL https://api.bintray.com/conan/bincrafters/public-conan - # VERIFY_SSL True) - set(oneValueArgs URL NAME INDEX COMMAND VERIFY_SSL) - cmake_parse_arguments(CONAN "" "${oneValueArgs}" "" ${ARGN}) - - if(DEFINED CONAN_INDEX) - set(CONAN_INDEX_ARG "-i ${CONAN_INDEX}") - endif() - if(DEFINED CONAN_COMMAND) - set(CONAN_CMD ${CONAN_COMMAND}) - else() - conan_check(REQUIRED DETECT_QUIET) - endif() - set(CONAN_VERIFY_SSL_ARG "True") - if(DEFINED CONAN_VERIFY_SSL) - set(CONAN_VERIFY_SSL_ARG ${CONAN_VERIFY_SSL}) - endif() - message(STATUS "Conan: Adding ${CONAN_NAME} remote repository (${CONAN_URL}) verify ssl (${CONAN_VERIFY_SSL_ARG})") - execute_process(COMMAND ${CONAN_CMD} remote add ${CONAN_NAME} ${CONAN_INDEX_ARG} -f ${CONAN_URL} ${CONAN_VERIFY_SSL_ARG} - RESULT_VARIABLE return_code) - if(NOT "${return_code}" STREQUAL "0") - message(FATAL_ERROR "Conan remote failed='${return_code}'") - endif() -endfunction() - -macro(conan_config_install) - # install a full configuration from a local or remote zip file - # Argument ITEM is required, arguments TYPE, SOURCE, TARGET and VERIFY_SSL are optional - # Example usage: - # conan_config_install(ITEM https://github.com/conan-io/cmake-conan.git - # TYPE git SOURCE source-folder TARGET target-folder VERIFY_SSL false) - set(oneValueArgs ITEM TYPE SOURCE TARGET VERIFY_SSL) - set(multiValueArgs ARGS) - cmake_parse_arguments(CONAN "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - if(DEFINED CONAN_COMMAND) - set(CONAN_CMD ${CONAN_COMMAND}) - else() - conan_check(REQUIRED) - endif() - - if(DEFINED CONAN_VERIFY_SSL) - set(CONAN_VERIFY_SSL_ARG "--verify-ssl=${CONAN_VERIFY_SSL}") - endif() - - if(DEFINED CONAN_TYPE) - set(CONAN_TYPE_ARG "--type=${CONAN_TYPE}") - endif() - - if(DEFINED CONAN_ARGS) - # Convert ; separated multi arg list into space separated string - string(REPLACE ";" " " l_CONAN_ARGS "${CONAN_ARGS}") - set(CONAN_ARGS_ARGS "--args=${l_CONAN_ARGS}") - endif() - - if(DEFINED CONAN_SOURCE) - set(CONAN_SOURCE_ARGS "--source-folder=${CONAN_SOURCE}") - endif() - - if(DEFINED CONAN_TARGET) - set(CONAN_TARGET_ARGS "--target-folder=${CONAN_TARGET}") - endif() - - set (CONAN_CONFIG_INSTALL_ARGS ${CONAN_VERIFY_SSL_ARG} - ${CONAN_TYPE_ARG} - ${CONAN_ARGS_ARGS} - ${CONAN_SOURCE_ARGS} - ${CONAN_TARGET_ARGS}) - - message(STATUS "Conan: Installing config from ${CONAN_ITEM}") - execute_process(COMMAND ${CONAN_CMD} config install ${CONAN_ITEM} ${CONAN_CONFIG_INSTALL_ARGS} - RESULT_VARIABLE return_code) - if(NOT "${return_code}" STREQUAL "0") - message(FATAL_ERROR "Conan config failed='${return_code}'") - endif() -endmacro() - - -function(conan_cmake_profile) - set(profileOneValueArgs FILEPATH INCLUDE) - set(profileMultiValueArgs SETTINGS OPTIONS CONF ENV BUILDENV RUNENV TOOL_REQUIRES) - cmake_parse_arguments(ARGS "" "${profileOneValueArgs}" "${profileMultiValueArgs}" ${ARGN}) - - if(DEFINED ARGS_FILEPATH) - set(_FN "${ARGS_FILEPATH}") - else() - set(_FN "${CMAKE_CURRENT_BINARY_DIR}/profile") - endif() - message(STATUS "Conan: Creating profile ${_FN}") - file(WRITE ${_FN} "") - - if(DEFINED ARGS_INCLUDE) - file(APPEND ${_FN} "include(${ARGS_INCLUDE})\n") - endif() - - if(DEFINED ARGS_SETTINGS) - file(APPEND ${_FN} "[settings]\n") - foreach(SETTING ${ARGS_SETTINGS}) - file(APPEND ${_FN} ${SETTING} "\n") - endforeach() - endif() - - if(DEFINED ARGS_OPTIONS) - file(APPEND ${_FN} "[options]\n") - foreach(OPTION ${ARGS_OPTIONS}) - file(APPEND ${_FN} ${OPTION} "\n") - endforeach() - endif() - - if(DEFINED ARGS_CONF) - file(APPEND ${_FN} "[conf]\n") - foreach(CONF ${ARGS_CONF}) - file(APPEND ${_FN} ${CONF} "\n") - endforeach() - endif() - - if(DEFINED ARGS_ENV) - file(APPEND ${_FN} "[env]\n") - foreach(ENV ${ARGS_ENV}) - file(APPEND ${_FN} ${ENV} "\n") - endforeach() - endif() - - if(DEFINED ARGS_BUILDENV) - file(APPEND ${_FN} "[buildenv]\n") - foreach(BUILDENV ${ARGS_BUILDENV}) - file(APPEND ${_FN} ${BUILDENV} "\n") - endforeach() - endif() - - if(DEFINED ARGS_RUNENV) - file(APPEND ${_FN} "[runenv]\n") - foreach(RUNENV ${ARGS_RUNENV}) - file(APPEND ${_FN} ${RUNENV} "\n") - endforeach() - endif() - - if(DEFINED ARGS_TOOL_REQUIRES) - file(APPEND ${_FN} "[tool_requires]\n") - foreach(TOOL_REQUIRE ${ARGS_TOOL_REQUIRES}) - file(APPEND ${_FN} ${TOOL_REQUIRE} "\n") - endforeach() - endif() -endfunction() From 1ed4f047946a91293dcec4a47f3df90df0669c64 Mon Sep 17 00:00:00 2001 From: Eric Riff Date: Mon, 6 Oct 2025 16:54:36 +0000 Subject: [PATCH 020/147] Add support for sanitizers including some GHAs --- .github/workflows/cmake_ubuntu_aubsan.yml | 57 +++++++++++++++++++++++ .github/workflows/cmake_ubuntu_tsan.yml | 56 ++++++++++++++++++++++ CMakeLists.txt | 5 ++ cmake/sanitizers.cmake | 30 ++++++++++++ 4 files changed, 148 insertions(+) create mode 100644 .github/workflows/cmake_ubuntu_aubsan.yml create mode 100644 .github/workflows/cmake_ubuntu_tsan.yml create mode 100644 cmake/sanitizers.cmake diff --git a/.github/workflows/cmake_ubuntu_aubsan.yml b/.github/workflows/cmake_ubuntu_aubsan.yml new file mode 100644 index 000000000..13db9ff15 --- /dev/null +++ b/.github/workflows/cmake_ubuntu_aubsan.yml @@ -0,0 +1,57 @@ +name: cmake Ubuntu with Address and Undefined Behavior Sanitizers + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Debug + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally + # well on Windows or Mac. You can convert this to a matrix build if you need + # cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-22.04] + + steps: + - uses: actions/checkout@v2 + + - name: Install Conan + id: conan + uses: turtlebrowser/get-conan@main + + - name: Create default profile + run: conan profile detect + + - name: Install conan dependencies + run: conan install conanfile.py -s build_type=${{env.BUILD_TYPE}} --build=missing + + - name: Normalize build type + shell: bash + # The build type is Capitalized, e.g. Release, but the preset is all lowercase, e.g. release. + # There is no built in way to do string manipulations on GHA as far as I know.` + run: echo "BUILD_TYPE_LOWERCASE=$(echo "${BUILD_TYPE}" | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV + + - name: Configure CMake + shell: bash + run: cmake --preset conan-${{ env.BUILD_TYPE_LOWERCASE }} -DBTCPP_ENABLE_ASAN:BOOL=ON -DBTCPP_ENABLE_UBSAN:BOOL=ON + + - name: Build + shell: bash + run: cmake --build --preset conan-${{ env.BUILD_TYPE_LOWERCASE }} + + - name: run test (Linux + Address and Undefined Behavior Sanitizers) + env: + GTEST_COLOR: "On" + ASAN_OPTIONS: "color=always" + UBSAN_OPTIONS: "halt_on_error=1:print_stacktrace=1:color=always" + run: ctest --test-dir build/${{env.BUILD_TYPE}} diff --git a/.github/workflows/cmake_ubuntu_tsan.yml b/.github/workflows/cmake_ubuntu_tsan.yml new file mode 100644 index 000000000..197f18ebe --- /dev/null +++ b/.github/workflows/cmake_ubuntu_tsan.yml @@ -0,0 +1,56 @@ +name: cmake Ubuntu with Thread Sanitizer + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Debug + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally + # well on Windows or Mac. You can convert this to a matrix build if you need + # cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-22.04] + + steps: + - uses: actions/checkout@v2 + + - name: Install Conan + id: conan + uses: turtlebrowser/get-conan@main + + - name: Create default profile + run: conan profile detect + + - name: Install conan dependencies + run: conan install conanfile.py -s build_type=${{env.BUILD_TYPE}} --build=missing + + - name: Normalize build type + shell: bash + # The build type is Capitalized, e.g. Release, but the preset is all lowercase, e.g. release. + # There is no built in way to do string manipulations on GHA as far as I know.` + run: echo "BUILD_TYPE_LOWERCASE=$(echo "${BUILD_TYPE}" | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV + + - name: Configure CMake + shell: bash + run: cmake --preset conan-${{ env.BUILD_TYPE_LOWERCASE }} -DBTCPP_ENABLE_TSAN:BOOL=ON + + - name: Build + shell: bash + run: cmake --build --preset conan-${{ env.BUILD_TYPE_LOWERCASE }} + + - name: run test (Linux + Thread Sanitizer) + env: + GTEST_COLOR: "On" + TSAN_OPTIONS: "color=always" + run: sudo sysctl vm.mmap_rnd_bits=28 && ctest --test-dir build/${{env.BUILD_TYPE}} diff --git a/CMakeLists.txt b/CMakeLists.txt index a0cb7b202..320877a91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,9 @@ option(BTCPP_EXAMPLES "Build tutorials and examples" ON) option(BUILD_TESTING "Build the unit tests" ON) option(BTCPP_GROOT_INTERFACE "Add Groot2 connection. Requires ZeroMQ" ON) option(BTCPP_SQLITE_LOGGING "Add SQLite logging." ON) +option(BTCPP_ENABLE_ASAN "Enable Address Sanitizer" OFF) +option(BTCPP_ENABLE_UBSAN "Enable Undefined Behavior Sanitizer" OFF) +option(BTCPP_ENABLE_TSAN "Enable Thread Sanitizer" OFF) option(USE_V3_COMPATIBLE_NAMES "Use some alias to compile more easily old 3.x code" OFF) option(ENABLE_FUZZING "Enable fuzzing builds" OFF) @@ -54,6 +57,8 @@ endif() set(CMAKE_CONFIG_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CONFIG_PATH}") +include(sanitizers) + set(BTCPP_LIBRARY ${PROJECT_NAME}) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) diff --git a/cmake/sanitizers.cmake b/cmake/sanitizers.cmake new file mode 100644 index 000000000..fdb542e68 --- /dev/null +++ b/cmake/sanitizers.cmake @@ -0,0 +1,30 @@ +if(BTCPP_ENABLE_ASAN OR BTCPP_ENABLE_UBSAN OR BTCPP_ENABLE_TSAN) + if(NOT CMAKE_BUILD_TYPE MATCHES "Debug|RelWithDebInfo") + message(FATAL_ERROR "Sanitizers require debug symbols. Please set CMAKE_BUILD_TYPE to Debug or RelWithDebInfo.") + endif() + add_compile_options(-fno-omit-frame-pointer) +endif() + +# Address Sanitizer and Undefined Behavior Sanitizer can be run at the same time. +# Thread Sanitizer requires its own build. +if(BTCPP_ENABLE_TSAN AND (BTCPP_ENABLE_ASAN OR BTCPP_ENABLE_UBSAN)) + message(FATAL_ERROR "TSAN is not compatible with ASAN or UBSAN. Please enable only one of them.") +endif() + +if(BTCPP_ENABLE_ASAN) + message(STATUS "Address Sanitizer enabled") + add_compile_options(-fsanitize=address) + add_link_options(-fsanitize=address) +endif() + +if(BTCPP_ENABLE_UBSAN) + message(STATUS "Undefined Behavior Sanitizer enabled") + add_compile_options(-fsanitize=undefined) + add_link_options(-fsanitize=undefined) +endif() + +if(BTCPP_ENABLE_TSAN) + message(STATUS "Thread Sanitizer enabled") + add_compile_options(-fsanitize=thread) + add_link_options(-fsanitize=thread) +endif() From 92feada203d31f94bb1c85973811d125149a4500 Mon Sep 17 00:00:00 2001 From: Eric Riff Date: Tue, 7 Oct 2025 18:24:50 +0000 Subject: [PATCH 021/147] use gtest_discover_tests to regiester the unit tests this modern approach registers many individual tests instead of a single monolitic test so if one fails the rest continue running which allows the developer to flag multiple failing tests on a single run It also speeds up testing since tests run in parallel --- tests/CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9c62b335e..56a03e501 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -43,16 +43,19 @@ if(ament_cmake_FOUND) else() + enable_testing() + find_package(GTest REQUIRED) + include(GoogleTest) - enable_testing() add_executable(behaviortree_cpp_test ${BT_TESTS}) - add_test(NAME btcpp_test COMMAND behaviortree_cpp_test) target_link_libraries(behaviortree_cpp_test GTest::gtest GTest::gtest_main) + gtest_discover_tests(behaviortree_cpp_test) + endif() target_include_directories(behaviortree_cpp_test PRIVATE include) From 2523528fcd68b197f2604fda5266d3db6a478eeb Mon Sep 17 00:00:00 2001 From: Eric Riff Date: Tue, 7 Oct 2025 18:55:31 +0000 Subject: [PATCH 022/147] Combine sanitizer actions into a single file --- ...aubsan.yml => cmake_ubuntu_sanitizers.yml} | 15 ++++- .github/workflows/cmake_ubuntu_tsan.yml | 56 ------------------- 2 files changed, 12 insertions(+), 59 deletions(-) rename .github/workflows/{cmake_ubuntu_aubsan.yml => cmake_ubuntu_sanitizers.yml} (75%) delete mode 100644 .github/workflows/cmake_ubuntu_tsan.yml diff --git a/.github/workflows/cmake_ubuntu_aubsan.yml b/.github/workflows/cmake_ubuntu_sanitizers.yml similarity index 75% rename from .github/workflows/cmake_ubuntu_aubsan.yml rename to .github/workflows/cmake_ubuntu_sanitizers.yml index 13db9ff15..4c0b3bd17 100644 --- a/.github/workflows/cmake_ubuntu_aubsan.yml +++ b/.github/workflows/cmake_ubuntu_sanitizers.yml @@ -1,4 +1,4 @@ -name: cmake Ubuntu with Address and Undefined Behavior Sanitizers +name: cmake Ubuntu Sanitizers on: push: @@ -21,6 +21,7 @@ jobs: strategy: matrix: os: [ubuntu-22.04] + sanitizer: [asan_ubsan, tsan] steps: - uses: actions/checkout@v2 @@ -43,7 +44,14 @@ jobs: - name: Configure CMake shell: bash - run: cmake --preset conan-${{ env.BUILD_TYPE_LOWERCASE }} -DBTCPP_ENABLE_ASAN:BOOL=ON -DBTCPP_ENABLE_UBSAN:BOOL=ON + run: | + if [[ "${{ matrix.sanitizer }}" == "asan_ubsan" ]]; then + cmake --preset conan-${{ env.BUILD_TYPE_LOWERCASE }} \ + -DBTCPP_ENABLE_ASAN:BOOL=ON -DBTCPP_ENABLE_UBSAN:BOOL=ON + else + cmake --preset conan-${{ env.BUILD_TYPE_LOWERCASE }} \ + -DBTCPP_ENABLE_TSAN:BOOL=ON + fi - name: Build shell: bash @@ -54,4 +62,5 @@ jobs: GTEST_COLOR: "On" ASAN_OPTIONS: "color=always" UBSAN_OPTIONS: "halt_on_error=1:print_stacktrace=1:color=always" - run: ctest --test-dir build/${{env.BUILD_TYPE}} + TSAN_OPTIONS: "color=always" + run: sudo sysctl vm.mmap_rnd_bits=28 && ctest --test-dir build/${{env.BUILD_TYPE}} --output-on-failure diff --git a/.github/workflows/cmake_ubuntu_tsan.yml b/.github/workflows/cmake_ubuntu_tsan.yml deleted file mode 100644 index 197f18ebe..000000000 --- a/.github/workflows/cmake_ubuntu_tsan.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: cmake Ubuntu with Thread Sanitizer - -on: - push: - branches: - - master - pull_request: - types: [opened, synchronize, reopened] - -env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Debug - -jobs: - build: - # The CMake configure and build commands are platform agnostic and should work equally - # well on Windows or Mac. You can convert this to a matrix build if you need - # cross-platform coverage. - # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-22.04] - - steps: - - uses: actions/checkout@v2 - - - name: Install Conan - id: conan - uses: turtlebrowser/get-conan@main - - - name: Create default profile - run: conan profile detect - - - name: Install conan dependencies - run: conan install conanfile.py -s build_type=${{env.BUILD_TYPE}} --build=missing - - - name: Normalize build type - shell: bash - # The build type is Capitalized, e.g. Release, but the preset is all lowercase, e.g. release. - # There is no built in way to do string manipulations on GHA as far as I know.` - run: echo "BUILD_TYPE_LOWERCASE=$(echo "${BUILD_TYPE}" | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV - - - name: Configure CMake - shell: bash - run: cmake --preset conan-${{ env.BUILD_TYPE_LOWERCASE }} -DBTCPP_ENABLE_TSAN:BOOL=ON - - - name: Build - shell: bash - run: cmake --build --preset conan-${{ env.BUILD_TYPE_LOWERCASE }} - - - name: run test (Linux + Thread Sanitizer) - env: - GTEST_COLOR: "On" - TSAN_OPTIONS: "color=always" - run: sudo sysctl vm.mmap_rnd_bits=28 && ctest --test-dir build/${{env.BUILD_TYPE}} From 1b989f35fbf711a3ec65534a408b8b731db541b1 Mon Sep 17 00:00:00 2001 From: Eric Riff Date: Tue, 7 Oct 2025 19:22:29 +0000 Subject: [PATCH 023/147] Do not fail fast. We want results of both sanitizer runs --- .github/workflows/cmake_ubuntu_sanitizers.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cmake_ubuntu_sanitizers.yml b/.github/workflows/cmake_ubuntu_sanitizers.yml index 4c0b3bd17..e14a34e47 100644 --- a/.github/workflows/cmake_ubuntu_sanitizers.yml +++ b/.github/workflows/cmake_ubuntu_sanitizers.yml @@ -19,6 +19,7 @@ jobs: # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: [ubuntu-22.04] sanitizer: [asan_ubsan, tsan] From 390b99bb79ee637a9bbf52eaf7182b77aa055c41 Mon Sep 17 00:00:00 2001 From: Eric Riff Date: Tue, 7 Oct 2025 19:03:47 +0000 Subject: [PATCH 024/147] Fix windows builds --- tests/CMakeLists.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 56a03e501..b268235df 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -54,7 +54,16 @@ else() GTest::gtest GTest::gtest_main) - gtest_discover_tests(behaviortree_cpp_test) + # gtest_discover_tests queries the test executable for available tests and registers them on ctest individually + # On Windows it needs a little help to find the shared libraries + if(WIN32) + gtest_discover_tests(behaviortree_cpp_test + DISCOVERY_MODE PRE_TEST + DISCOVERY_ENVIRONMENT "PATH=$;$ENV{PATH}" + ) + else() + gtest_discover_tests(behaviortree_cpp_test) + endif() endif() From d6221b0435fe6444257ab9bbe52bf5942613afa8 Mon Sep 17 00:00:00 2001 From: Eric Riff Date: Tue, 7 Oct 2025 19:40:25 +0000 Subject: [PATCH 025/147] Leave a note for posterity --- .github/workflows/cmake_ubuntu_sanitizers.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/cmake_ubuntu_sanitizers.yml b/.github/workflows/cmake_ubuntu_sanitizers.yml index e14a34e47..c379171a1 100644 --- a/.github/workflows/cmake_ubuntu_sanitizers.yml +++ b/.github/workflows/cmake_ubuntu_sanitizers.yml @@ -64,4 +64,6 @@ jobs: ASAN_OPTIONS: "color=always" UBSAN_OPTIONS: "halt_on_error=1:print_stacktrace=1:color=always" TSAN_OPTIONS: "color=always" + # There is a known issue with TSAN on recent kernel versions. Without the vm.mmap_rnd_bits=28 + # workaround all binaries with TSan enabled crash with "FATAL: ThreadSanitizer: unexpected memory mapping" run: sudo sysctl vm.mmap_rnd_bits=28 && ctest --test-dir build/${{env.BUILD_TYPE}} --output-on-failure From 24f3d93ceb8fa1d71a312c1910a21fe597bed316 Mon Sep 17 00:00:00 2001 From: Eric Riff Date: Tue, 7 Oct 2025 19:46:22 +0000 Subject: [PATCH 026/147] Improve error message --- cmake/sanitizers.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/sanitizers.cmake b/cmake/sanitizers.cmake index fdb542e68..347c2aa0d 100644 --- a/cmake/sanitizers.cmake +++ b/cmake/sanitizers.cmake @@ -8,7 +8,7 @@ endif() # Address Sanitizer and Undefined Behavior Sanitizer can be run at the same time. # Thread Sanitizer requires its own build. if(BTCPP_ENABLE_TSAN AND (BTCPP_ENABLE_ASAN OR BTCPP_ENABLE_UBSAN)) - message(FATAL_ERROR "TSAN is not compatible with ASAN or UBSAN. Please enable only one of them.") + message(FATAL_ERROR "TSan is not compatible with ASan or UBSan. ASan and UBSan can run together, but TSan requires its own separate build.") endif() if(BTCPP_ENABLE_ASAN) From 6f1644962cf860c47e8bf3e9f0f4dd1395318e0b Mon Sep 17 00:00:00 2001 From: Laurenz Date: Thu, 9 Oct 2025 18:52:41 +0200 Subject: [PATCH 027/147] s/ncurses.h/curses.h/ (#1020) --- src/controls/manual_node.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controls/manual_node.cpp b/src/controls/manual_node.cpp index 18af9897e..82e7e443b 100644 --- a/src/controls/manual_node.cpp +++ b/src/controls/manual_node.cpp @@ -13,7 +13,7 @@ #include "behaviortree_cpp/controls/manual_node.h" #include "behaviortree_cpp/action_node.h" -#include +#include namespace BT { From 3d7e1e1fe2106b3adb10a6b28b779023c71f5211 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Tue, 14 Oct 2025 20:03:39 +0200 Subject: [PATCH 028/147] fix thread safety issues --- cmake/sanitizers.cmake | 9 +++--- include/behaviortree_cpp/blackboard.h | 12 ++++--- include/behaviortree_cpp/utils/timer_queue.h | 34 ++++++++++++-------- src/blackboard.cpp | 20 +++++++----- tests/gtest_blackboard.cpp | 3 ++ 5 files changed, 48 insertions(+), 30 deletions(-) diff --git a/cmake/sanitizers.cmake b/cmake/sanitizers.cmake index 347c2aa0d..fa5dccc84 100644 --- a/cmake/sanitizers.cmake +++ b/cmake/sanitizers.cmake @@ -15,16 +15,17 @@ if(BTCPP_ENABLE_ASAN) message(STATUS "Address Sanitizer enabled") add_compile_options(-fsanitize=address) add_link_options(-fsanitize=address) -endif() + endif() -if(BTCPP_ENABLE_UBSAN) + if(BTCPP_ENABLE_UBSAN) message(STATUS "Undefined Behavior Sanitizer enabled") add_compile_options(-fsanitize=undefined) add_link_options(-fsanitize=undefined) -endif() + endif() -if(BTCPP_ENABLE_TSAN) + if(BTCPP_ENABLE_TSAN) message(STATUS "Thread Sanitizer enabled") add_compile_options(-fsanitize=thread) add_link_options(-fsanitize=thread) + add_compile_definitions(USE_SANITIZE_THREAD) endif() diff --git a/include/behaviortree_cpp/blackboard.h b/include/behaviortree_cpp/blackboard.h index 1c3aa96c6..412360bf8 100644 --- a/include/behaviortree_cpp/blackboard.h +++ b/include/behaviortree_cpp/blackboard.h @@ -141,7 +141,7 @@ class Blackboard const Blackboard* rootBlackboard() const; private: - mutable std::mutex mutex_; + mutable std::mutex storage_mutex_; mutable std::recursive_mutex entry_mutex_; std::unordered_map> storage_; std::weak_ptr parent_bb_; @@ -186,7 +186,7 @@ inline T Blackboard::get(const std::string& key) const inline void Blackboard::unset(const std::string& key) { - std::unique_lock lock(mutex_); + std::unique_lock storage_lock(storage_mutex_); // check local storage auto it = storage_.find(key); @@ -207,7 +207,7 @@ inline void Blackboard::set(const std::string& key, const T& value) rootBlackboard()->set(key.substr(1, key.size() - 1), value); return; } - std::unique_lock lock(mutex_); + std::unique_lock storage_lock(storage_mutex_); // check local storage auto it = storage_.find(key); @@ -215,7 +215,7 @@ inline void Blackboard::set(const std::string& key, const T& value) { // create a new entry Any new_value(value); - lock.unlock(); + storage_lock.unlock(); std::shared_ptr entry; // if a new generic port is created with a string, it's type should be AnyTypeAllowed if constexpr(std::is_same_v) @@ -228,7 +228,7 @@ inline void Blackboard::set(const std::string& key, const T& value) GetAnyFromStringFunctor()); entry = createEntryImpl(key, new_port); } - lock.lock(); + storage_lock.lock(); entry->value = new_value; entry->sequence_id++; @@ -239,6 +239,8 @@ inline void Blackboard::set(const std::string& key, const T& value) // this is not the first time we set this entry, we need to check // if the type is the same or not. Entry& entry = *it->second; + storage_lock.unlock(); + std::scoped_lock scoped_lock(entry.entry_mutex); Any& previous_any = entry.value; diff --git a/include/behaviortree_cpp/utils/timer_queue.h b/include/behaviortree_cpp/utils/timer_queue.h index 9132277ba..56738ff56 100644 --- a/include/behaviortree_cpp/utils/timer_queue.h +++ b/include/behaviortree_cpp/utils/timer_queue.h @@ -23,10 +23,7 @@ class Semaphore void notify() { - { - std::lock_guard lock(m_mtx); - m_count++; - } + m_count.fetch_add(1); m_cv.notify_one(); } @@ -38,8 +35,15 @@ class Semaphore { return false; } - m_count--; + // Only decrement if there is a real count. If we woke because of manualUnlock, + // m_count may be zero and we must not decrement it. + if(m_count > 0) + { + m_count.fetch_sub(1); + } + // Clear the manual unlock flag m_unlock = false; + return true; } @@ -52,7 +56,7 @@ class Semaphore private: std::mutex m_mtx; std::condition_variable m_cv; - unsigned m_count = 0; + std::atomic_uint m_count = 0; std::atomic_bool m_unlock = false; }; } // namespace details @@ -74,15 +78,19 @@ class TimerQueue public: TimerQueue() { - m_th = std::thread([this] { run(); }); + m_finish.store(false); + m_thread = std::thread([this]() { run(); }); } ~TimerQueue() { - m_finish = true; + m_finish.store(true); cancelAll(); - m_checkWork.manualUnlock(); - m_th.join(); + + if(m_thread.joinable()) + { + m_thread.join(); + } } //! Adds a new timer @@ -174,7 +182,7 @@ class TimerQueue void run() { - while(!m_finish) + while(!m_finish.load()) { auto end = calcWaitTime(); if(end.first) @@ -239,8 +247,8 @@ class TimerQueue } details::Semaphore m_checkWork; - std::thread m_th; - bool m_finish = false; + std::thread m_thread; + std::atomic_bool m_finish = false; uint64_t m_idcounter = 0; struct WorkItem diff --git a/src/blackboard.cpp b/src/blackboard.cpp index 3b1ba9844..462e7b41e 100644 --- a/src/blackboard.cpp +++ b/src/blackboard.cpp @@ -52,11 +52,13 @@ Blackboard::getEntry(const std::string& key) const return rootBlackboard()->getEntry(key.substr(1, key.size() - 1)); } - std::unique_lock lock(mutex_); - auto it = storage_.find(key); - if(it != storage_.end()) { - return it->second; + std::unique_lock storage_lock(storage_mutex_); + auto it = storage_.find(key); + if(it != storage_.end()) + { + return it->second; + } } // not found. Try autoremapping if(auto parent = parent_bb_.lock()) @@ -130,7 +132,7 @@ std::vector Blackboard::getKeys() const void Blackboard::clear() { - std::unique_lock lock(mutex_); + std::unique_lock storage_lock(storage_mutex_); storage_.clear(); } @@ -157,8 +159,10 @@ void Blackboard::createEntry(const std::string& key, const TypeInfo& info) void Blackboard::cloneInto(Blackboard& dst) const { - std::unique_lock lk1(mutex_); - std::unique_lock lk2(dst.mutex_); + // Lock both mutexes without risking lock-order inversion. + std::unique_lock lk1(storage_mutex_, std::defer_lock); + std::unique_lock lk2(dst.storage_mutex_, std::defer_lock); + std::lock(lk1, lk2); // keys that are not updated must be removed. std::unordered_set keys_to_remove; @@ -212,7 +216,7 @@ Blackboard::Ptr Blackboard::parent() std::shared_ptr Blackboard::createEntryImpl(const std::string& key, const TypeInfo& info) { - std::unique_lock lock(mutex_); + std::unique_lock storage_lock(storage_mutex_); // This function might be called recursively, when we do remapping, because we move // to the top scope to find already existing entries diff --git a/tests/gtest_blackboard.cpp b/tests/gtest_blackboard.cpp index 35be37dab..75c3db6d1 100644 --- a/tests/gtest_blackboard.cpp +++ b/tests/gtest_blackboard.cpp @@ -294,6 +294,8 @@ TEST(BlackboardTest, CheckTypeSafety) ASSERT_TRUE(is); } +#ifndef USE_SANITIZE_THREAD + TEST(BlackboardTest, AnyPtrLocked) { auto blackboard = Blackboard::create(); @@ -346,6 +348,7 @@ TEST(BlackboardTest, AnyPtrLocked) ASSERT_NE(cycles, value); } } +#endif TEST(BlackboardTest, SetStringView) { From 06c856af3d91bef52e1c96a4fc7d48f29211912d Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Tue, 14 Oct 2025 20:19:16 +0200 Subject: [PATCH 029/147] fix memory leak --- src/tree_node.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/tree_node.cpp b/src/tree_node.cpp index 0d4dd66bd..946492318 100644 --- a/src/tree_node.cpp +++ b/src/tree_node.cpp @@ -106,19 +106,13 @@ NodeStatus TreeNode::executeTick() if(!substituted) { using namespace std::chrono; - - auto t1 = steady_clock::now(); - // trick to prevent the compile from reordering the order of execution. See #861 - // This makes sure that the code is executed at the end of this scope - std::shared_ptr execute_later(nullptr, [&](...) { - auto t2 = steady_clock::now(); - if(monitor_tick) - { - monitor_tick(*this, new_status, duration_cast(t2 - t1)); - } - }); - + const auto t1 = steady_clock::now(); new_status = tick(); + const auto t2 = steady_clock::now(); + if(monitor_tick) + { + monitor_tick(*this, new_status, duration_cast(t2 - t1)); + } } } From 831cadd0972e8ca66692ee7c053b7ede585fe831 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Tue, 14 Oct 2025 20:38:28 +0200 Subject: [PATCH 030/147] fix issue in destruction order --- include/behaviortree_cpp/utils/timer_queue.h | 1 - tests/gtest_decorator.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/behaviortree_cpp/utils/timer_queue.h b/include/behaviortree_cpp/utils/timer_queue.h index 56738ff56..2a68ba872 100644 --- a/include/behaviortree_cpp/utils/timer_queue.h +++ b/include/behaviortree_cpp/utils/timer_queue.h @@ -86,7 +86,6 @@ class TimerQueue { m_finish.store(true); cancelAll(); - if(m_thread.joinable()) { m_thread.join(); diff --git a/tests/gtest_decorator.cpp b/tests/gtest_decorator.cpp index 32abcff10..fde33ea9b 100644 --- a/tests/gtest_decorator.cpp +++ b/tests/gtest_decorator.cpp @@ -68,8 +68,8 @@ struct RetryTest : testing::Test struct TimeoutAndRetry : testing::Test { - BT::TimeoutNode timeout_root; BT::RetryNode retry; + BT::TimeoutNode timeout_root; BT::SyncActionTest action; TimeoutAndRetry() : timeout_root("deadline", 9), retry("retry", 1000), action("action") From 730bb6504764e88e49a8211f1939b6b92df2828c Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Tue, 14 Oct 2025 20:49:26 +0200 Subject: [PATCH 031/147] prepare release --- .pre-commit-config.yaml | 2 +- CHANGELOG.rst | 23 +++++++++++++++++++++++ CMakeLists.txt | 2 +- README.md | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d491f36d9..3f81aaaee 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ # # See https://github.com/pre-commit/pre-commit -exclude: ^3rdparty/|3rdparty|^include/behaviortree_cpp/contrib/ +exclude: ^3rdparty/|3rdparty|^include/behaviortree_cpp/contrib/|CHANGELOG.rst repos: # Standard hooks diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8a1b78774..7be98e8e9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,29 @@ Changelog for package behaviortree_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- +* fix issue in destruction order +* fix memory leak +* fix thread safety issues +* Improve error message +* Leave a note for posterity +* Fix windows builds +* Do not fail fast. We want results of both sanitizer runs +* Combine sanitizer actions into a single file +* use gtest_discover_tests to regiester the unit tests + this modern approach registers many individual tests instead of a single monolitic test + so if one fails the rest continue running which allows the developer to flag multiple + failing tests on a single run + It also speeds up testing since tests run in parallel +* Add support for sanitizers including some GHAs +* Remove unused conan.cmake (`#1016 `_) +* Improve handling of dependencies (`#1012 `_) +* update tinyxml to version 11.0 +* fix potential compilation errors +* compile for c++ 17 (`#1013 `_) +* Contributors: Davide Faconti, Eric Riff + 4.7.3 (2025-10-01) ------------------ * remove cpp-sqlite diff --git a/CMakeLists.txt b/CMakeLists.txt index 320877a91..7cdeea380 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16.3) # version on Ubuntu Focal -project(behaviortree_cpp VERSION 4.7.3 LANGUAGES C CXX) +project(behaviortree_cpp VERSION 4.8.0 LANGUAGES C CXX) # create compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/README.md b/README.md index b30335ffc..d80513ecf 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![ros2](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/ros2.yaml/badge.svg)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/ros2.yaml) [![pixi (Conda)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/pixi.yaml/badge.svg)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/pixi.yaml) -# BehaviorTree.CPP 4.7 +# BehaviorTree.CPP 4.8

From 22929a25473fee1e852fa06734e947e889b433b4 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Tue, 14 Oct 2025 20:49:35 +0200 Subject: [PATCH 032/147] 4.8.0 --- CHANGELOG.rst | 4 ++-- package.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7be98e8e9..8010a2074 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package behaviortree_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +4.8.0 (2025-10-14) +------------------ * fix issue in destruction order * fix memory leak * fix thread safety issues diff --git a/package.xml b/package.xml index 5e2b8f237..c0b0ecff8 100644 --- a/package.xml +++ b/package.xml @@ -1,7 +1,7 @@ behaviortree_cpp - 4.7.3 + 4.8.0 This package provides the Behavior Trees core library. From 7a884828392ed7441a4a2bcde5f13210e5a495b2 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Thu, 23 Oct 2025 10:55:22 +0200 Subject: [PATCH 033/147] fix warning --- src/xml_parsing.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/xml_parsing.cpp b/src/xml_parsing.cpp index da68be99a..5426cc2e4 100644 --- a/src/xml_parsing.cpp +++ b/src/xml_parsing.cpp @@ -29,11 +29,17 @@ #if defined(__linux) || defined(__linux__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wattributes" +#pragma GCC diagnostic ignored "-Wtype-limits" +#endif + +#include "tinyxml2.h" + +#if defined(__linux) || defined(__linux__) +#pragma GCC diagnostic pop #endif #include #include "behaviortree_cpp/xml_parsing.h" -#include "tinyxml2.h" #include #ifdef USING_ROS2 From 35802105fe4a8bfec05de7dd5fe36f606e7ef400 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Thu, 23 Oct 2025 10:55:45 +0200 Subject: [PATCH 034/147] fix private pedendencies --- CMakeLists.txt | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7cdeea380..117ff7daa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -226,17 +226,29 @@ target_link_libraries(${BTCPP_LIBRARY} PRIVATE Threads::Threads ${CMAKE_DL_LIBS} - foonathan::lexy - minitrace::minitrace - tinyxml2::tinyxml2 - minicoro::minicoro - flatbuffers::flatbuffers - $<$:cppzmq> - $<$:SQLite::SQLite3> + $ + $ + $ + $ + $ PUBLIC ${BTCPP_EXTRA_LIBRARIES} ) +if(BTCPP_GROOT_INTERFACE) + target_link_libraries(${BTCPP_LIBRARY} + PRIVATE + $ + ) +endif() + +if(BTCPP_SQLITE_LOGGING) + target_link_libraries(${BTCPP_LIBRARY} + PRIVATE + $ + ) +endif() + target_include_directories(${BTCPP_LIBRARY} PUBLIC $ From 042875f9cf14a67f8f914cf9bcda2a2c179a6a2c Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Thu, 23 Oct 2025 12:29:15 +0200 Subject: [PATCH 035/147] stop installing lexy --- 3rdparty/lexy/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/lexy/CMakeLists.txt b/3rdparty/lexy/CMakeLists.txt index a76693a9e..3a63b7798 100644 --- a/3rdparty/lexy/CMakeLists.txt +++ b/3rdparty/lexy/CMakeLists.txt @@ -9,7 +9,7 @@ option(LEXY_FORCE_CPP17 "Whether or not lexy should use C++17 even if compil add_subdirectory(src) -option(LEXY_ENABLE_INSTALL "whether or not to enable the install rule" ON) +option(LEXY_ENABLE_INSTALL "whether or not to enable the install rule" OFF) if(LEXY_ENABLE_INSTALL) include(CMakePackageConfigHelpers) include(GNUInstallDirs) From 2b4dc5bb9b0a5d0a464cfc172a831deffd29f0d1 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Thu, 23 Oct 2025 12:32:51 +0200 Subject: [PATCH 036/147] prepare release --- CHANGELOG.rst | 3 +++ CMakeLists.txt | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8010a2074..ba667964b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,9 @@ Changelog for package behaviortree_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- + 4.8.0 (2025-10-14) ------------------ * fix issue in destruction order diff --git a/CMakeLists.txt b/CMakeLists.txt index 117ff7daa..b254b3c2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16.3) # version on Ubuntu Focal -project(behaviortree_cpp VERSION 4.8.0 LANGUAGES C CXX) +project(behaviortree_cpp VERSION 4.8.1 LANGUAGES C CXX) # create compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON) From 36d13ed5f7f0a240d1d86668ac3442ec392585cd Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Thu, 23 Oct 2025 12:32:56 +0200 Subject: [PATCH 037/147] 4.8.1 --- CHANGELOG.rst | 4 ++-- package.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ba667964b..1141df81c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package behaviortree_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +4.8.1 (2025-10-23) +------------------ 4.8.0 (2025-10-14) ------------------ diff --git a/package.xml b/package.xml index c0b0ecff8..bb7266ea3 100644 --- a/package.xml +++ b/package.xml @@ -1,7 +1,7 @@ behaviortree_cpp - 4.8.0 + 4.8.1 This package provides the Behavior Trees core library. From b8a62dda0a32d0fe36a8e709fd97e597a5492ebc Mon Sep 17 00:00:00 2001 From: pleemann Date: Thu, 23 Oct 2025 18:04:38 +0200 Subject: [PATCH 038/147] added Tree::emitWakeUpSignal --- include/behaviortree_cpp/bt_factory.h | 8 +++++++- src/bt_factory.cpp | 5 +++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/behaviortree_cpp/bt_factory.h b/include/behaviortree_cpp/bt_factory.h index 020c9ea1d..c9d53c123 100644 --- a/include/behaviortree_cpp/bt_factory.h +++ b/include/behaviortree_cpp/bt_factory.h @@ -119,7 +119,8 @@ class Tree [[nodiscard]] TreeNode* rootNode() const; /** - * @brief Sleep for a certain amount of time. This sleep could be interrupted by the method TreeNode::emitWakeUpSignal() + * @brief Sleep for a certain amount of time. This sleep could be interrupted by the methods + * TreeNode::emitWakeUpSignal() or Tree::emitWakeUpSignal() * * @param timeout duration of the sleep * @return true if the timeout was NOT reached and the signal was received. @@ -127,6 +128,11 @@ class Tree * */ bool sleep(std::chrono::system_clock::duration timeout); + /** + * @brief Wake up the tree. This will interrupt the sleep() method. + */ + void emitWakeUpSignal(); + ~Tree(); /// Tick the root of the tree once, even if a node invoked diff --git a/src/bt_factory.cpp b/src/bt_factory.cpp index 01e01301a..d7dd1dbb0 100644 --- a/src/bt_factory.cpp +++ b/src/bt_factory.cpp @@ -519,6 +519,11 @@ bool Tree::sleep(std::chrono::system_clock::duration timeout) std::chrono::duration_cast(timeout)); } +void Tree::emitWakeUpSignal() +{ + wake_up_->emitSignal(); +} + Tree::~Tree() { haltTree(); From b451618cc47f3fcce28a20cbccec4d99d68b8117 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Thu, 30 Oct 2025 06:01:50 +0100 Subject: [PATCH 039/147] force tinyxml2_vendor in ROS2. See #1033 and #1028 --- CMakeLists.txt | 17 ++++++++++------- package.xml | 2 ++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b254b3c2f..b303fb325 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,6 @@ option(USE_VENDORED_FLATBUFFERS "Use the bundled version of flatbuffers" ON) option(USE_VENDORED_LEXY "Use the bundled version of lexy" ON) option(USE_VENDORED_MINICORO "Use the bundled version of minicoro" ON) option(USE_VENDORED_MINITRACE "Use the bundled version of minitrace" ON) -option(USE_VENDORED_TINYXML2 "Use the bundled version of tinyxml2" ON) set(BTCPP_LIB_DESTINATION lib) set(BTCPP_INCLUDE_DESTINATION include) @@ -93,11 +92,21 @@ if ( ament_cmake_FOUND ) message(STATUS "BehaviorTree is being built using AMENT.") message(STATUS "------------------------------------------") include(cmake/ament_build.cmake) + + find_package(tinyxml2_vendor REQUIRED) + find_package(TinyXML2 REQUIRED) else() message(STATUS "------------------------------------------") message(STATUS "BehaviorTree is being built without AMENT.") message(STATUS "------------------------------------------") include(cmake/conan_build.cmake) + + option(USE_VENDORED_TINYXML2 "Use the bundled version of tinyxml2" ON) + if(USE_VENDORED_TINYXML2) + add_subdirectory(3rdparty/tinyxml2) + else() + find_package(tinyxml2 REQUIRED) + endif() endif() ############################################################# @@ -141,12 +150,6 @@ else() find_package(minitrace REQUIRED) endif() -if(USE_VENDORED_TINYXML2) - add_subdirectory(3rdparty/tinyxml2) -else() - find_package(tinyxml2 REQUIRED) -endif() - list(APPEND BT_SOURCE src/action_node.cpp src/basic_types.cpp diff --git a/package.xml b/package.xml index bb7266ea3..59a7b62c8 100644 --- a/package.xml +++ b/package.xml @@ -22,6 +22,8 @@ libsqlite3-dev libzmq3-dev + tinyxml2 + tinyxml2_vendor ament_cmake_gtest From 5a586c8d19a96ee1f5cfae0a15b2ca64d3c8dd47 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Thu, 30 Oct 2025 06:11:33 +0100 Subject: [PATCH 040/147] fix issue #1034 --- include/behaviortree_cpp/bt_factory.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/behaviortree_cpp/bt_factory.h b/include/behaviortree_cpp/bt_factory.h index 020c9ea1d..d22fc0791 100644 --- a/include/behaviortree_cpp/bt_factory.h +++ b/include/behaviortree_cpp/bt_factory.h @@ -21,6 +21,7 @@ #include #include +#include "behaviortree_cpp/contrib/json.hpp" #include "behaviortree_cpp/contrib/magic_enum.hpp" #include "behaviortree_cpp/behavior_tree.h" From 425bd39d54012db46b61c2900f282c28c8cb0f5d Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Thu, 30 Oct 2025 06:32:13 +0100 Subject: [PATCH 041/147] prepare release --- CHANGELOG.rst | 14 ++++++++++++++ CMakeLists.txt | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1141df81c..2f75604cd 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,20 @@ Changelog for package behaviortree_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Forthcoming +----------- +* Merge pull request `#996 `_ from EnjoyRobotics/make-sequence-node-inheritable +* Merge branch 'master' of github.com:BehaviorTree/BehaviorTree.CPP +* fix issue `#1034 `_ +* Merge pull request `#1030 `_ from pleemann/tree_wake_up + Event-based tree ticking +* force tinyxml2_vendor in ROS2. See `#1033 `_ and `#1028 `_ +* added Tree::emitWakeUpSignal +* Lint +* Propagate node config to parent +* Make tick method protected +* Contributors: Davide Faconti, pleemann, redvinaa + 4.8.1 (2025-10-23) ------------------ diff --git a/CMakeLists.txt b/CMakeLists.txt index b303fb325..c9528bda6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16.3) # version on Ubuntu Focal -project(behaviortree_cpp VERSION 4.8.1 LANGUAGES C CXX) +project(behaviortree_cpp VERSION 4.8.2 LANGUAGES C CXX) # create compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON) From aa2426082c3767ee8ca751f55b99984e7f13711e Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Thu, 30 Oct 2025 06:38:47 +0100 Subject: [PATCH 042/147] 4.8.2 --- CHANGELOG.rst | 4 ++-- package.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2f75604cd..22080f36b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog for package behaviortree_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Forthcoming ------------ +4.8.2 (2025-10-30) +------------------ * Merge pull request `#996 `_ from EnjoyRobotics/make-sequence-node-inheritable * Merge branch 'master' of github.com:BehaviorTree/BehaviorTree.CPP * fix issue `#1034 `_ diff --git a/package.xml b/package.xml index 59a7b62c8..956dd1319 100644 --- a/package.xml +++ b/package.xml @@ -1,7 +1,7 @@ behaviortree_cpp - 4.8.1 + 4.8.2 This package provides the Behavior Trees core library. From 8d3dbb367be534a3204ef51099d5ffc0ca525ca5 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Thu, 30 Oct 2025 06:43:36 +0100 Subject: [PATCH 043/147] Update copyright year in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d80513ecf..13699b27f 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ only in the master branch. The MIT License (MIT) -Copyright (c) 2019-2023 Davide Faconti +Copyright (c) 2019-2025 Davide Faconti Copyright (c) 2018-2019 Davide Faconti, Eurecat From cdaca9c2c68f4b3aa187040f72b02b8dc8a1ed25 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Thu, 30 Oct 2025 11:59:58 +0100 Subject: [PATCH 044/147] Restore Star History and add Contributors section Reintroduced the Star History section and added Contributors section to the README. --- README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 13699b27f..e3637d351 100644 --- a/README.md +++ b/README.md @@ -94,10 +94,6 @@ example here: https://github.com/BehaviorTree/btcpp_sample . Are you using BT.CPP in your commercial product and do you need technical support / consulting? You can contact the primary author, **dfaconti@aurynrobotics.com**, to discuss your use case and needs. -# Star History - -[![Star History Chart](https://api.star-history.com/svg?repos=BehaviorTree/BehaviorTree.CPP&type=Date)](https://star-history.com/#BehaviorTree/BehaviorTree.CPP&Date) - ## Previous version Version 3.8 of the software can be found in the branch @@ -106,6 +102,16 @@ Version 3.8 of the software can be found in the branch That branch might receive bug fixes, but the new features will be implemented only in the master branch. +# Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=BehaviorTree/BehaviorTree.CPP&type=Date)](https://star-history.com/#BehaviorTree/BehaviorTree.CPP&Date) + +# Contributors + + + + + # License The MIT License (MIT) From 78b159dfa617d86e107f08f116750d190ae0ee3c Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Mon, 15 Dec 2025 18:31:41 +0100 Subject: [PATCH 045/147] Turn cppzmq dependency public Signed-off-by: Uilian Ries --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c9528bda6..d44131e03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -240,7 +240,7 @@ target_link_libraries(${BTCPP_LIBRARY} if(BTCPP_GROOT_INTERFACE) target_link_libraries(${BTCPP_LIBRARY} - PRIVATE + PUBLIC $ ) endif() From 15658a79834ed201a78e42b55f9d30d132d0ea16 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Mon, 29 Dec 2025 21:57:32 +0100 Subject: [PATCH 046/147] fix multiple issues with SimpleString --- .../behaviortree_cpp/utils/simple_string.hpp | 61 +++++++++++++------ 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/include/behaviortree_cpp/utils/simple_string.hpp b/include/behaviortree_cpp/utils/simple_string.hpp index 484780e46..1c6b49809 100644 --- a/include/behaviortree_cpp/utils/simple_string.hpp +++ b/include/behaviortree_cpp/utils/simple_string.hpp @@ -18,6 +18,12 @@ namespace SafeAny class SimpleString { public: + SimpleString() + { + _storage.soo.capacity_left = CAPACITY; + _storage.soo.data[0] = '\0'; + } + SimpleString(const std::string& str) : SimpleString(str.data(), str.size()) {} @@ -29,21 +35,28 @@ class SimpleString SimpleString& operator=(const SimpleString& other) { - this->~SimpleString(); - createImpl(other.data(), other.size()); + if(this != &other) + { + this->~SimpleString(); + createImpl(other.data(), other.size()); + } return *this; } - SimpleString(SimpleString&& other) : SimpleString(nullptr, 0) + SimpleString(SimpleString&& other) noexcept : SimpleString() { std::swap(_storage, other._storage); } - SimpleString& operator=(SimpleString&& other) + SimpleString& operator=(SimpleString&& other) noexcept { - this->~SimpleString(); - - std::swap(_storage, other._storage); + if(this != &other) + { + this->~SimpleString(); + // Ensure clean state before swap + _storage = {}; + std::swap(_storage, other._storage); + } return *this; } @@ -99,46 +112,58 @@ class SimpleString bool operator==(const SimpleString& other) const { - size_t N = size(); + const size_t N = size(); return other.size() == N && std::strncmp(data(), other.data(), N) == 0; } bool operator!=(const SimpleString& other) const { - size_t N = size(); + const size_t N = size(); return other.size() != N || std::strncmp(data(), other.data(), N) != 0; } bool operator<=(const SimpleString& other) const { - return std::strcmp(data(), other.data()) <= 0; + return !(*this > other); } bool operator>=(const SimpleString& other) const { - return std::strcmp(data(), other.data()) >= 0; + return !(*this < other); } bool operator<(const SimpleString& other) const { - return std::strcmp(data(), other.data()) < 0; + const size_t min_size = std::min(size(), other.size()); + int cmp = std::memcmp(data(), other.data(), min_size); + if(cmp != 0) + { + return cmp < 0; + } + return size() < other.size(); } bool operator>(const SimpleString& other) const { - return std::strcmp(data(), other.data()) > 0; + const size_t min_size = std::min(size(), other.size()); + int cmp = std::memcmp(data(), other.data(), min_size); + if(cmp != 0) + { + return cmp > 0; + } + return size() > other.size(); } bool isSOO() const { - return !(_storage.soo.capacity_left & IS_LONG_BIT); + return (_storage.soo.capacity_left & IS_LONG_BIT) == 0; } private: struct String { - char* data; - std::size_t size; + char* data = nullptr; + std::size_t size = 0; }; constexpr static std::size_t CAPACITY = 15; // sizeof(String) - 1); @@ -153,9 +178,9 @@ class SimpleString struct SOO { char data[CAPACITY]; - uint8_t capacity_left; + uint8_t capacity_left = CAPACITY; } soo; - } _storage; + } _storage = {}; private: void createImpl(const char* input_data, std::size_t size) From 01d504cd2f63b251fe5e0141e6fa9b751f6232da Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Mon, 29 Dec 2025 22:04:55 +0100 Subject: [PATCH 047/147] add unit test --- tests/CMakeLists.txt | 1 + tests/gtest_simple_string.cpp | 527 ++++++++++++++++++++++++++++++++++ 2 files changed, 528 insertions(+) create mode 100644 tests/gtest_simple_string.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b268235df..9e667babe 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -29,6 +29,7 @@ set(BT_TESTS gtest_updates.cpp gtest_wakeup.cpp gtest_interface.cpp + gtest_simple_string.cpp script_parser_test.cpp test_helper.hpp diff --git a/tests/gtest_simple_string.cpp b/tests/gtest_simple_string.cpp new file mode 100644 index 000000000..dd9a2a4e9 --- /dev/null +++ b/tests/gtest_simple_string.cpp @@ -0,0 +1,527 @@ +/* Copyright (C) 2018-2023 Davide Faconti, Eurecat - All Rights Reserved +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include "behaviortree_cpp/utils/simple_string.hpp" + +using namespace SafeAny; + +// Test default constructor +TEST(SimpleStringTest, DefaultConstructor) +{ + SimpleString s; + EXPECT_EQ(s.size(), 0); + EXPECT_STREQ(s.data(), ""); + EXPECT_TRUE(s.isSOO()); +} + +// Test construction from empty string +TEST(SimpleStringTest, EmptyString) +{ + SimpleString s(""); + EXPECT_EQ(s.size(), 0); + EXPECT_STREQ(s.data(), ""); + EXPECT_TRUE(s.isSOO()); +} + +// Test construction from const char* +TEST(SimpleStringTest, ConstructFromCString) +{ + SimpleString s("hello"); + EXPECT_EQ(s.size(), 5); + EXPECT_STREQ(s.data(), "hello"); + EXPECT_TRUE(s.isSOO()); +} + +// Test construction from const char* with explicit size +TEST(SimpleStringTest, ConstructFromCStringWithSize) +{ + const char* text = "hello world"; + SimpleString s(text, 5); + EXPECT_EQ(s.size(), 5); + EXPECT_STREQ(s.data(), "hello"); + EXPECT_TRUE(s.isSOO()); +} + +// Test construction from std::string +TEST(SimpleStringTest, ConstructFromStdString) +{ + std::string str = "testing"; + SimpleString s(str); + EXPECT_EQ(s.size(), 7); + EXPECT_STREQ(s.data(), "testing"); + EXPECT_TRUE(s.isSOO()); +} + +// Test construction from std::string_view +TEST(SimpleStringTest, ConstructFromStringView) +{ + std::string_view sv = "view test"; + SimpleString s(sv); + EXPECT_EQ(s.size(), 9); + EXPECT_STREQ(s.data(), "view test"); + EXPECT_TRUE(s.isSOO()); +} + +// Test SOO boundary - exactly 15 characters (max SOO capacity) +TEST(SimpleStringTest, SOOBoundaryExact) +{ + // Exactly 15 characters - should still use SOO + SimpleString s("123456789012345"); + EXPECT_EQ(s.size(), 15); + EXPECT_STREQ(s.data(), "123456789012345"); + EXPECT_TRUE(s.isSOO()); +} + +// Test SOO boundary - 16 characters (exceeds SOO capacity) +TEST(SimpleStringTest, SOOBoundaryExceeded) +{ + // 16 characters - should use heap allocation + SimpleString s("1234567890123456"); + EXPECT_EQ(s.size(), 16); + EXPECT_STREQ(s.data(), "1234567890123456"); + EXPECT_FALSE(s.isSOO()); +} + +// Test long string (non-SOO) +TEST(SimpleStringTest, LongString) +{ + std::string longStr(100, 'x'); + SimpleString s(longStr); + EXPECT_EQ(s.size(), 100); + EXPECT_EQ(s.toStdString(), longStr); + EXPECT_FALSE(s.isSOO()); +} + +// Test copy constructor with SOO string +TEST(SimpleStringTest, CopyConstructorSOO) +{ + SimpleString s1("hello"); + SimpleString s2(s1); + EXPECT_EQ(s1.size(), s2.size()); + EXPECT_STREQ(s1.data(), s2.data()); + EXPECT_TRUE(s1.isSOO()); + EXPECT_TRUE(s2.isSOO()); +} + +// Test copy constructor with non-SOO string +TEST(SimpleStringTest, CopyConstructorNonSOO) +{ + std::string longStr(50, 'a'); + SimpleString s1(longStr); + SimpleString s2(s1); + EXPECT_EQ(s1.size(), s2.size()); + EXPECT_STREQ(s1.data(), s2.data()); + EXPECT_FALSE(s1.isSOO()); + EXPECT_FALSE(s2.isSOO()); + // Ensure they have independent storage + EXPECT_NE(s1.data(), s2.data()); +} + +// Test copy assignment with SOO string +TEST(SimpleStringTest, CopyAssignmentSOO) +{ + SimpleString s1("hello"); + SimpleString s2("world"); + s2 = s1; + EXPECT_EQ(s1.size(), s2.size()); + EXPECT_STREQ(s1.data(), s2.data()); +} + +// Test copy assignment with default constructed target +TEST(SimpleStringTest, CopyAssignmentToDefault) +{ + SimpleString s1("hello"); + SimpleString s2; + s2 = s1; + EXPECT_EQ(s1.size(), s2.size()); + EXPECT_STREQ(s1.data(), s2.data()); +} + +// Test self copy assignment +TEST(SimpleStringTest, SelfCopyAssignment) +{ + SimpleString s("test"); + s = s; + EXPECT_EQ(s.size(), 4); + EXPECT_STREQ(s.data(), "test"); +} + +// Test copy assignment with non-SOO string +TEST(SimpleStringTest, CopyAssignmentNonSOO) +{ + std::string longStr(50, 'b'); + SimpleString s1(longStr); + SimpleString s2("temp"); + s2 = s1; + EXPECT_EQ(s1.size(), s2.size()); + EXPECT_STREQ(s1.data(), s2.data()); + EXPECT_NE(s1.data(), s2.data()); +} + +// Test move constructor +TEST(SimpleStringTest, MoveConstructor) +{ + SimpleString s1("hello"); + SimpleString s2(std::move(s1)); + EXPECT_EQ(s2.size(), 5); + EXPECT_STREQ(s2.data(), "hello"); +} + +// Test move constructor with non-SOO string +TEST(SimpleStringTest, MoveConstructorNonSOO) +{ + std::string longStr(50, 'c'); + SimpleString s1(longStr); + const char* originalData = s1.data(); + SimpleString s2(std::move(s1)); + EXPECT_EQ(s2.size(), 50); + EXPECT_EQ(s2.toStdString(), longStr); + // After move, s2 should have taken over the pointer + EXPECT_EQ(s2.data(), originalData); +} + +// Test move assignment +TEST(SimpleStringTest, MoveAssignment) +{ + SimpleString s1("hello"); + SimpleString s2("world"); + s2 = std::move(s1); + EXPECT_EQ(s2.size(), 5); + EXPECT_STREQ(s2.data(), "hello"); +} + +// Test move assignment to default constructed +TEST(SimpleStringTest, MoveAssignmentToDefault) +{ + SimpleString s1("hello"); + SimpleString s2; + s2 = std::move(s1); + EXPECT_EQ(s2.size(), 5); + EXPECT_STREQ(s2.data(), "hello"); +} + +// Test self move assignment +TEST(SimpleStringTest, SelfMoveAssignment) +{ + SimpleString s("test"); + s = std::move(s); + EXPECT_EQ(s.size(), 4); + EXPECT_STREQ(s.data(), "test"); +} + +// Test move assignment with non-SOO string +TEST(SimpleStringTest, MoveAssignmentNonSOO) +{ + std::string longStr(50, 'd'); + SimpleString s1(longStr); + const char* originalData = s1.data(); + SimpleString s2("temp"); + s2 = std::move(s1); + EXPECT_EQ(s2.size(), 50); + EXPECT_EQ(s2.toStdString(), longStr); + EXPECT_EQ(s2.data(), originalData); +} + +// Test toStdString() +TEST(SimpleStringTest, ToStdString) +{ + SimpleString s("convert me"); + std::string str = s.toStdString(); + EXPECT_EQ(str, "convert me"); +} + +// Test toStdString() with empty string +TEST(SimpleStringTest, ToStdStringEmpty) +{ + SimpleString s; + std::string str = s.toStdString(); + EXPECT_TRUE(str.empty()); +} + +// Test toStdStringView() +TEST(SimpleStringTest, ToStdStringView) +{ + SimpleString s("view me"); + std::string_view sv = s.toStdStringView(); + EXPECT_EQ(sv, "view me"); +} + +// Test toStdStringView() with empty string +TEST(SimpleStringTest, ToStdStringViewEmpty) +{ + SimpleString s; + std::string_view sv = s.toStdStringView(); + EXPECT_TRUE(sv.empty()); +} + +// Test equality operator +TEST(SimpleStringTest, EqualityOperator) +{ + SimpleString s1("hello"); + SimpleString s2("hello"); + SimpleString s3("world"); + SimpleString s4("hell"); + + EXPECT_TRUE(s1 == s2); + EXPECT_FALSE(s1 == s3); + EXPECT_FALSE(s1 == s4); +} + +// Test inequality operator +TEST(SimpleStringTest, InequalityOperator) +{ + SimpleString s1("hello"); + SimpleString s2("hello"); + SimpleString s3("world"); + + EXPECT_FALSE(s1 != s2); + EXPECT_TRUE(s1 != s3); +} + +// Test less than operator +TEST(SimpleStringTest, LessThanOperator) +{ + SimpleString s1("apple"); + SimpleString s2("banana"); + SimpleString s3("apple"); + SimpleString s4("app"); + + EXPECT_TRUE(s1 < s2); + EXPECT_FALSE(s2 < s1); + EXPECT_FALSE(s1 < s3); + EXPECT_FALSE(s1 < s4); // "apple" > "app" + EXPECT_TRUE(s4 < s1); // "app" < "apple" +} + +// Test greater than operator +TEST(SimpleStringTest, GreaterThanOperator) +{ + SimpleString s1("banana"); + SimpleString s2("apple"); + SimpleString s3("banana"); + SimpleString s4("ban"); + + EXPECT_TRUE(s1 > s2); + EXPECT_FALSE(s2 > s1); + EXPECT_FALSE(s1 > s3); + EXPECT_TRUE(s1 > s4); // "banana" > "ban" + EXPECT_FALSE(s4 > s1); // "ban" < "banana" +} + +// Test less than or equal operator +TEST(SimpleStringTest, LessEqualOperator) +{ + SimpleString s1("apple"); + SimpleString s2("banana"); + SimpleString s3("apple"); + + EXPECT_TRUE(s1 <= s2); + EXPECT_TRUE(s1 <= s3); + EXPECT_FALSE(s2 <= s1); +} + +// Test greater than or equal operator +TEST(SimpleStringTest, GreaterEqualOperator) +{ + SimpleString s1("banana"); + SimpleString s2("apple"); + SimpleString s3("banana"); + + EXPECT_TRUE(s1 >= s2); + EXPECT_TRUE(s1 >= s3); + EXPECT_FALSE(s2 >= s1); +} + +// Test comparison with non-SOO strings +TEST(SimpleStringTest, ComparisonNonSOO) +{ + std::string longStr1(50, 'a'); + std::string longStr2(50, 'b'); + std::string longStr3(50, 'a'); + + SimpleString s1(longStr1); + SimpleString s2(longStr2); + SimpleString s3(longStr3); + + EXPECT_TRUE(s1 == s3); + EXPECT_TRUE(s1 != s2); + EXPECT_TRUE(s1 < s2); + EXPECT_TRUE(s2 > s1); + EXPECT_TRUE(s1 <= s3); + EXPECT_TRUE(s1 >= s3); +} + +// Test empty string comparisons +TEST(SimpleStringTest, EmptyStringComparison) +{ + SimpleString empty1; + SimpleString empty2; + SimpleString nonEmpty("a"); + + EXPECT_TRUE(empty1 == empty2); + EXPECT_FALSE(empty1 != empty2); + EXPECT_TRUE(empty1 < nonEmpty); + EXPECT_TRUE(nonEmpty > empty1); + EXPECT_TRUE(empty1 <= nonEmpty); + EXPECT_TRUE(nonEmpty >= empty1); +} + +// Test that SimpleString size is as expected (16 bytes) +TEST(SimpleStringTest, SizeOfSimpleString) +{ + EXPECT_EQ(sizeof(SimpleString), 16); +} + +// Test assignment from SOO to non-SOO +TEST(SimpleStringTest, AssignmentSOOToNonSOO) +{ + SimpleString s1("short"); + std::string longStr(50, 'x'); + SimpleString s2(longStr); + + s2 = s1; + EXPECT_EQ(s2.size(), 5); + EXPECT_STREQ(s2.data(), "short"); + EXPECT_TRUE(s2.isSOO()); +} + +// Test assignment from non-SOO to SOO +TEST(SimpleStringTest, AssignmentNonSOOToSOO) +{ + std::string longStr(50, 'y'); + SimpleString s1(longStr); + SimpleString s2("tiny"); + + s2 = s1; + EXPECT_EQ(s2.size(), 50); + EXPECT_EQ(s2.toStdString(), longStr); + EXPECT_FALSE(s2.isSOO()); +} + +// Test very long string construction (non-SOO) +TEST(SimpleStringTest, VeryLongString) +{ + std::string veryLong(10000, 'z'); + SimpleString s(veryLong); + EXPECT_EQ(s.size(), 10000); + EXPECT_EQ(s.toStdString(), veryLong); + EXPECT_FALSE(s.isSOO()); +} + +// Test reassignment from SOO to non-SOO +TEST(SimpleStringTest, ReassignSOOToNonSOO) +{ + SimpleString s("first"); + EXPECT_TRUE(s.isSOO()); + + s = SimpleString("second value here"); + EXPECT_STREQ(s.data(), "second value here"); + EXPECT_FALSE(s.isSOO()); +} + +// Test reassignment from non-SOO to SOO +TEST(SimpleStringTest, ReassignNonSOOToSOO) +{ + SimpleString s("second value here"); + EXPECT_FALSE(s.isSOO()); + + s = SimpleString("third"); + EXPECT_STREQ(s.data(), "third"); + EXPECT_TRUE(s.isSOO()); +} + +// Test reassignment from non-SOO to non-SOO +TEST(SimpleStringTest, ReassignNonSOOToNonSOO) +{ + std::string longStr1(50, 'a'); + std::string longStr2(100, 'b'); + + SimpleString s(longStr1); + EXPECT_FALSE(s.isSOO()); + + s = SimpleString(longStr2); + EXPECT_EQ(s.toStdString(), longStr2); + EXPECT_FALSE(s.isSOO()); +} + +// Test construction from single character +TEST(SimpleStringTest, SingleCharacter) +{ + SimpleString s("a"); + EXPECT_EQ(s.size(), 1); + EXPECT_STREQ(s.data(), "a"); + EXPECT_TRUE(s.isSOO()); +} + +// Test construction from exactly CAPACITY-1 chars +TEST(SimpleStringTest, CapacityMinus1) +{ + // 14 characters + SimpleString s("12345678901234"); + EXPECT_EQ(s.size(), 14); + EXPECT_STREQ(s.data(), "12345678901234"); + EXPECT_TRUE(s.isSOO()); +} + +// Test construction from exactly CAPACITY+1 chars +TEST(SimpleStringTest, CapacityPlus1) +{ + // 16 characters + SimpleString s("1234567890123456"); + EXPECT_EQ(s.size(), 16); + EXPECT_STREQ(s.data(), "1234567890123456"); + EXPECT_FALSE(s.isSOO()); +} + +// Test that data() returns null-terminated string for SOO +TEST(SimpleStringTest, NullTerminatedSOO) +{ + SimpleString s("test"); + const char* d = s.data(); + EXPECT_EQ(d[4], '\0'); +} + +// Test that data() returns null-terminated string for non-SOO +TEST(SimpleStringTest, NullTerminatedNonSOO) +{ + std::string longStr(50, 'x'); + SimpleString s(longStr); + const char* d = s.data(); + EXPECT_EQ(d[50], '\0'); +} + +// Test copy of empty string +TEST(SimpleStringTest, CopyEmptyString) +{ + SimpleString s1; + SimpleString s2(s1); + EXPECT_EQ(s2.size(), 0); + EXPECT_STREQ(s2.data(), ""); +} + +// Test move of empty string +TEST(SimpleStringTest, MoveEmptyString) +{ + SimpleString s1; + SimpleString s2(std::move(s1)); + EXPECT_EQ(s2.size(), 0); + EXPECT_STREQ(s2.data(), ""); +} + +// Test exception on size too large +TEST(SimpleStringTest, SizeTooLarge) +{ + const char* data = "test"; + // MAX_SIZE is 100MB, attempting to create larger should throw + EXPECT_THROW(SimpleString(data, 200UL * 1024UL * 1024UL), std::invalid_argument); +} From 381612b3322a7ea7423a9509df18448fb2dfe6f8 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Mon, 29 Dec 2025 22:11:28 +0100 Subject: [PATCH 048/147] update copyright year --- include/behaviortree_cpp/action_node.h | 2 +- include/behaviortree_cpp/actions/always_failure_node.h | 2 +- include/behaviortree_cpp/actions/always_success_node.h | 2 +- include/behaviortree_cpp/actions/pop_from_queue.hpp | 2 +- include/behaviortree_cpp/actions/script_condition.h | 2 +- include/behaviortree_cpp/actions/script_node.h | 2 +- include/behaviortree_cpp/actions/set_blackboard_node.h | 2 +- include/behaviortree_cpp/actions/test_node.h | 2 +- include/behaviortree_cpp/actions/unset_blackboard_node.h | 2 +- include/behaviortree_cpp/actions/updated_action.h | 2 +- include/behaviortree_cpp/behavior_tree.h | 2 +- include/behaviortree_cpp/bt_factory.h | 2 +- include/behaviortree_cpp/bt_parser.h | 2 +- include/behaviortree_cpp/condition_node.h | 2 +- include/behaviortree_cpp/control_node.h | 2 +- include/behaviortree_cpp/controls/fallback_node.h | 2 +- include/behaviortree_cpp/controls/if_then_else_node.h | 2 +- include/behaviortree_cpp/controls/manual_node.h | 2 +- include/behaviortree_cpp/controls/parallel_all_node.h | 2 +- include/behaviortree_cpp/controls/parallel_node.h | 2 +- include/behaviortree_cpp/controls/reactive_fallback.h | 2 +- include/behaviortree_cpp/controls/reactive_sequence.h | 2 +- include/behaviortree_cpp/controls/sequence_node.h | 2 +- include/behaviortree_cpp/controls/sequence_with_memory_node.h | 2 +- include/behaviortree_cpp/controls/switch_node.h | 2 +- include/behaviortree_cpp/controls/while_do_else_node.h | 2 +- include/behaviortree_cpp/decorators/consume_queue.h | 2 +- include/behaviortree_cpp/decorators/delay_node.h | 2 +- include/behaviortree_cpp/decorators/force_failure_node.h | 2 +- include/behaviortree_cpp/decorators/force_success_node.h | 2 +- include/behaviortree_cpp/decorators/inverter_node.h | 2 +- .../decorators/keep_running_until_failure_node.h | 2 +- include/behaviortree_cpp/decorators/loop_node.h | 2 +- include/behaviortree_cpp/decorators/repeat_node.h | 2 +- include/behaviortree_cpp/decorators/retry_node.h | 2 +- include/behaviortree_cpp/decorators/run_once_node.h | 2 +- include/behaviortree_cpp/decorators/script_precondition.h | 2 +- include/behaviortree_cpp/decorators/timeout_node.h | 2 +- include/behaviortree_cpp/decorators/updated_decorator.h | 2 +- include/behaviortree_cpp/exceptions.h | 2 +- include/behaviortree_cpp/leaf_node.h | 2 +- include/behaviortree_cpp/scripting/any_types.hpp | 2 +- include/behaviortree_cpp/scripting/operators.hpp | 2 +- include/behaviortree_cpp/scripting/script_parser.hpp | 2 +- include/behaviortree_cpp/tree_node.h | 2 +- include/behaviortree_cpp/utils/convert_impl.hpp | 2 +- include/behaviortree_cpp/utils/safe_any.hpp | 2 +- src/action_node.cpp | 2 +- src/actions/updated_action.cpp | 2 +- src/behavior_tree.cpp | 2 +- src/bt_factory.cpp | 2 +- src/condition_node.cpp | 2 +- src/control_node.cpp | 2 +- src/controls/fallback_node.cpp | 2 +- src/controls/if_then_else_node.cpp | 2 +- src/controls/manual_node.cpp | 2 +- src/controls/parallel_all_node.cpp | 2 +- src/controls/parallel_node.cpp | 2 +- src/controls/reactive_fallback.cpp | 2 +- src/controls/reactive_sequence.cpp | 2 +- src/controls/sequence_node.cpp | 2 +- src/controls/sequence_with_memory_node.cpp | 2 +- src/controls/switch_node.cpp | 2 +- src/controls/while_do_else_node.cpp | 2 +- src/decorator_node.cpp | 2 +- src/decorators/inverter_node.cpp | 2 +- src/decorators/repeat_node.cpp | 2 +- src/decorators/retry_node.cpp | 2 +- src/decorators/timeout_node.cpp | 2 +- src/decorators/updated_decorator.cpp | 2 +- src/tree_node.cpp | 2 +- src/xml_parsing.cpp | 2 +- 72 files changed, 72 insertions(+), 72 deletions(-) diff --git a/include/behaviortree_cpp/action_node.h b/include/behaviortree_cpp/action_node.h index 409e57987..2b720ecc6 100644 --- a/include/behaviortree_cpp/action_node.h +++ b/include/behaviortree_cpp/action_node.h @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/actions/always_failure_node.h b/include/behaviortree_cpp/actions/always_failure_node.h index 9de60dbb2..f4ba08868 100644 --- a/include/behaviortree_cpp/actions/always_failure_node.h +++ b/include/behaviortree_cpp/actions/always_failure_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved +/* Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/actions/always_success_node.h b/include/behaviortree_cpp/actions/always_success_node.h index 777710521..ac0e8d687 100644 --- a/include/behaviortree_cpp/actions/always_success_node.h +++ b/include/behaviortree_cpp/actions/always_success_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2022 Davide Faconti, Eurecat - All Rights Reserved +/* Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/actions/pop_from_queue.hpp b/include/behaviortree_cpp/actions/pop_from_queue.hpp index 34b905fde..997346ffc 100644 --- a/include/behaviortree_cpp/actions/pop_from_queue.hpp +++ b/include/behaviortree_cpp/actions/pop_from_queue.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Davide Faconti - All Rights Reserved +/* Copyright (C) 2022-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/actions/script_condition.h b/include/behaviortree_cpp/actions/script_condition.h index a063c1eca..3d44033f2 100644 --- a/include/behaviortree_cpp/actions/script_condition.h +++ b/include/behaviortree_cpp/actions/script_condition.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Davide Faconti - All Rights Reserved +/* Copyright (C) 2023-2025 Davide Faconti - All Rights Reserved * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/include/behaviortree_cpp/actions/script_node.h b/include/behaviortree_cpp/actions/script_node.h index c54585c7d..fef8dc19f 100644 --- a/include/behaviortree_cpp/actions/script_node.h +++ b/include/behaviortree_cpp/actions/script_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Davide Faconti - All Rights Reserved +/* Copyright (C) 2022-2025 Davide Faconti - All Rights Reserved * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/include/behaviortree_cpp/actions/set_blackboard_node.h b/include/behaviortree_cpp/actions/set_blackboard_node.h index 05282c0c0..3d3746874 100644 --- a/include/behaviortree_cpp/actions/set_blackboard_node.h +++ b/include/behaviortree_cpp/actions/set_blackboard_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved +/* Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/actions/test_node.h b/include/behaviortree_cpp/actions/test_node.h index 9aaabb829..fd98e8d7d 100644 --- a/include/behaviortree_cpp/actions/test_node.h +++ b/include/behaviortree_cpp/actions/test_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Davide Faconti - All Rights Reserved +/* Copyright (C) 2022-2025 Davide Faconti - All Rights Reserved * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/include/behaviortree_cpp/actions/unset_blackboard_node.h b/include/behaviortree_cpp/actions/unset_blackboard_node.h index 875d1de7e..0a2836e59 100644 --- a/include/behaviortree_cpp/actions/unset_blackboard_node.h +++ b/include/behaviortree_cpp/actions/unset_blackboard_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Davide Faconti - All Rights Reserved +/* Copyright (C) 2023-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/actions/updated_action.h b/include/behaviortree_cpp/actions/updated_action.h index 80503ccf3..449d49be7 100644 --- a/include/behaviortree_cpp/actions/updated_action.h +++ b/include/behaviortree_cpp/actions/updated_action.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 Davide Faconti - All Rights Reserved +/* Copyright (C) 2024-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/behavior_tree.h b/include/behaviortree_cpp/behavior_tree.h index 42b860f99..0e6f44642 100644 --- a/include/behaviortree_cpp/behavior_tree.h +++ b/include/behaviortree_cpp/behavior_tree.h @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2023 Davide Faconti - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/bt_factory.h b/include/behaviortree_cpp/bt_factory.h index a1806a196..4cf7eac6e 100644 --- a/include/behaviortree_cpp/bt_factory.h +++ b/include/behaviortree_cpp/bt_factory.h @@ -1,5 +1,5 @@ /* Copyright (C) 2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2023 Davide Faconti - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/bt_parser.h b/include/behaviortree_cpp/bt_parser.h index 3b6bcb637..9fb8c364e 100644 --- a/include/behaviortree_cpp/bt_parser.h +++ b/include/behaviortree_cpp/bt_parser.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Davide Faconti - All Rights Reserved +/* Copyright (C) 2023-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/condition_node.h b/include/behaviortree_cpp/condition_node.h index 3c6299d2a..ffde2e7f8 100644 --- a/include/behaviortree_cpp/condition_node.h +++ b/include/behaviortree_cpp/condition_node.h @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/control_node.h b/include/behaviortree_cpp/control_node.h index 9062ca24e..8a058c3cb 100644 --- a/include/behaviortree_cpp/control_node.h +++ b/include/behaviortree_cpp/control_node.h @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/controls/fallback_node.h b/include/behaviortree_cpp/controls/fallback_node.h index 515e00e04..d54df4ce1 100644 --- a/include/behaviortree_cpp/controls/fallback_node.h +++ b/include/behaviortree_cpp/controls/fallback_node.h @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/controls/if_then_else_node.h b/include/behaviortree_cpp/controls/if_then_else_node.h index 40834a61e..817e5bc27 100644 --- a/include/behaviortree_cpp/controls/if_then_else_node.h +++ b/include/behaviortree_cpp/controls/if_then_else_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2022 Davide Faconti - All Rights Reserved +/* Copyright (C) 2020-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/controls/manual_node.h b/include/behaviortree_cpp/controls/manual_node.h index ef92dbc56..97b1619bd 100644 --- a/include/behaviortree_cpp/controls/manual_node.h +++ b/include/behaviortree_cpp/controls/manual_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2022 Davide Faconti - All Rights Reserved +/* Copyright (C) 2020-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/controls/parallel_all_node.h b/include/behaviortree_cpp/controls/parallel_all_node.h index fe807ef89..284b6a1ee 100644 --- a/include/behaviortree_cpp/controls/parallel_all_node.h +++ b/include/behaviortree_cpp/controls/parallel_all_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Davide Faconti - All Rights Reserved +/* Copyright (C) 2023-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/controls/parallel_node.h b/include/behaviortree_cpp/controls/parallel_node.h index ac2d7acd0..fb36b7344 100644 --- a/include/behaviortree_cpp/controls/parallel_node.h +++ b/include/behaviortree_cpp/controls/parallel_node.h @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2022 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/controls/reactive_fallback.h b/include/behaviortree_cpp/controls/reactive_fallback.h index bdd43d995..ceea54f9f 100644 --- a/include/behaviortree_cpp/controls/reactive_fallback.h +++ b/include/behaviortree_cpp/controls/reactive_fallback.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2022 Davide Faconti, Eurecat - All Rights Reserved +/* Copyright (C) 2020-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/controls/reactive_sequence.h b/include/behaviortree_cpp/controls/reactive_sequence.h index 030486d9a..eba0f41ff 100644 --- a/include/behaviortree_cpp/controls/reactive_sequence.h +++ b/include/behaviortree_cpp/controls/reactive_sequence.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2022 Davide Faconti, Eurecat - All Rights Reserved +/* Copyright (C) 2020-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/controls/sequence_node.h b/include/behaviortree_cpp/controls/sequence_node.h index cb6ab9bfc..4a5b6c711 100644 --- a/include/behaviortree_cpp/controls/sequence_node.h +++ b/include/behaviortree_cpp/controls/sequence_node.h @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/controls/sequence_with_memory_node.h b/include/behaviortree_cpp/controls/sequence_with_memory_node.h index 8bd351e72..ba4aa6970 100644 --- a/include/behaviortree_cpp/controls/sequence_with_memory_node.h +++ b/include/behaviortree_cpp/controls/sequence_with_memory_node.h @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/controls/switch_node.h b/include/behaviortree_cpp/controls/switch_node.h index 7ffb989a2..77a08ac70 100644 --- a/include/behaviortree_cpp/controls/switch_node.h +++ b/include/behaviortree_cpp/controls/switch_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020-2022 Davide Faconti - All Rights Reserved +/* Copyright (C) 2020-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/controls/while_do_else_node.h b/include/behaviortree_cpp/controls/while_do_else_node.h index 4a6243f6e..c5d76b907 100644 --- a/include/behaviortree_cpp/controls/while_do_else_node.h +++ b/include/behaviortree_cpp/controls/while_do_else_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Davide Faconti - All Rights Reserved +/* Copyright (C) 2020-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/decorators/consume_queue.h b/include/behaviortree_cpp/decorators/consume_queue.h index 07d8a97b2..2fa3a4f2c 100644 --- a/include/behaviortree_cpp/decorators/consume_queue.h +++ b/include/behaviortree_cpp/decorators/consume_queue.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Davide Faconti - All Rights Reserved +/* Copyright (C) 2022-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/decorators/delay_node.h b/include/behaviortree_cpp/decorators/delay_node.h index d2fd8fb7e..10678e9e3 100644 --- a/include/behaviortree_cpp/decorators/delay_node.h +++ b/include/behaviortree_cpp/decorators/delay_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2023 Davide Faconti - All Rights Reserved +/* Copyright (C) 2018-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/decorators/force_failure_node.h b/include/behaviortree_cpp/decorators/force_failure_node.h index 285bd7493..98a7128b4 100644 --- a/include/behaviortree_cpp/decorators/force_failure_node.h +++ b/include/behaviortree_cpp/decorators/force_failure_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved +/* Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/decorators/force_success_node.h b/include/behaviortree_cpp/decorators/force_success_node.h index e8b4ca0d5..a33301ce0 100644 --- a/include/behaviortree_cpp/decorators/force_success_node.h +++ b/include/behaviortree_cpp/decorators/force_success_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved +/* Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/decorators/inverter_node.h b/include/behaviortree_cpp/decorators/inverter_node.h index 66b163797..244844404 100644 --- a/include/behaviortree_cpp/decorators/inverter_node.h +++ b/include/behaviortree_cpp/decorators/inverter_node.h @@ -1,5 +1,5 @@ /* Copyright (C) 2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/decorators/keep_running_until_failure_node.h b/include/behaviortree_cpp/decorators/keep_running_until_failure_node.h index 46d397b24..7c7607ac9 100644 --- a/include/behaviortree_cpp/decorators/keep_running_until_failure_node.h +++ b/include/behaviortree_cpp/decorators/keep_running_until_failure_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved +/* Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * Copyright (C) 2020 Francisco Martin, Intelligent Robotics Lab (URJC) * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/include/behaviortree_cpp/decorators/loop_node.h b/include/behaviortree_cpp/decorators/loop_node.h index 18240c504..d3923201e 100644 --- a/include/behaviortree_cpp/decorators/loop_node.h +++ b/include/behaviortree_cpp/decorators/loop_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Davide Faconti - All Rights Reserved +/* Copyright (C) 2022-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/decorators/repeat_node.h b/include/behaviortree_cpp/decorators/repeat_node.h index 14887e9bf..13f79f105 100644 --- a/include/behaviortree_cpp/decorators/repeat_node.h +++ b/include/behaviortree_cpp/decorators/repeat_node.h @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2022 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/decorators/retry_node.h b/include/behaviortree_cpp/decorators/retry_node.h index 7a903ef39..81c3906d5 100644 --- a/include/behaviortree_cpp/decorators/retry_node.h +++ b/include/behaviortree_cpp/decorators/retry_node.h @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2022 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/decorators/run_once_node.h b/include/behaviortree_cpp/decorators/run_once_node.h index a3083d97e..6796fcd38 100644 --- a/include/behaviortree_cpp/decorators/run_once_node.h +++ b/include/behaviortree_cpp/decorators/run_once_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Davide Faconti - All Rights Reserved +/* Copyright (C) 2023-2025 Davide Faconti - All Rights Reserved * Copyright (C) 2022 Gaël Écorchard, Czech Institute of Informatics, Robotics, and Cybernetics (ciirc) * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/include/behaviortree_cpp/decorators/script_precondition.h b/include/behaviortree_cpp/decorators/script_precondition.h index e244c7a50..83f1b57d9 100644 --- a/include/behaviortree_cpp/decorators/script_precondition.h +++ b/include/behaviortree_cpp/decorators/script_precondition.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Davide Faconti - All Rights Reserved +/* Copyright (C) 2022-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/decorators/timeout_node.h b/include/behaviortree_cpp/decorators/timeout_node.h index b52cc1263..b821edd7f 100644 --- a/include/behaviortree_cpp/decorators/timeout_node.h +++ b/include/behaviortree_cpp/decorators/timeout_node.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2023 Davide Faconti - All Rights Reserved +/* Copyright (C) 2018-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/decorators/updated_decorator.h b/include/behaviortree_cpp/decorators/updated_decorator.h index d570aafb2..4a707b555 100644 --- a/include/behaviortree_cpp/decorators/updated_decorator.h +++ b/include/behaviortree_cpp/decorators/updated_decorator.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 Davide Faconti - All Rights Reserved +/* Copyright (C) 2024-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/exceptions.h b/include/behaviortree_cpp/exceptions.h index df778da01..3a8bf8fd1 100644 --- a/include/behaviortree_cpp/exceptions.h +++ b/include/behaviortree_cpp/exceptions.h @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/leaf_node.h b/include/behaviortree_cpp/leaf_node.h index 61d7a1ca1..8ad948ae6 100644 --- a/include/behaviortree_cpp/leaf_node.h +++ b/include/behaviortree_cpp/leaf_node.h @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/scripting/any_types.hpp b/include/behaviortree_cpp/scripting/any_types.hpp index 0db106288..41b943112 100644 --- a/include/behaviortree_cpp/scripting/any_types.hpp +++ b/include/behaviortree_cpp/scripting/any_types.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2022-24 Davide Faconti - All Rights Reserved +/* Copyright (C) 2022-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/scripting/operators.hpp b/include/behaviortree_cpp/scripting/operators.hpp index 4d41b6a88..d37ec6d56 100644 --- a/include/behaviortree_cpp/scripting/operators.hpp +++ b/include/behaviortree_cpp/scripting/operators.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Davide Faconti - All Rights Reserved +/* Copyright (C) 2022-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/scripting/script_parser.hpp b/include/behaviortree_cpp/scripting/script_parser.hpp index 6f82da312..070a1ae11 100644 --- a/include/behaviortree_cpp/scripting/script_parser.hpp +++ b/include/behaviortree_cpp/scripting/script_parser.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Davide Faconti - All Rights Reserved +/* Copyright (C) 2022-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/tree_node.h b/include/behaviortree_cpp/tree_node.h index 0087ce210..ec7b379e9 100644 --- a/include/behaviortree_cpp/tree_node.h +++ b/include/behaviortree_cpp/tree_node.h @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved -* Copyright (C) 2018-2023 Davide Faconti - All Rights Reserved +* Copyright (C) 2018-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/utils/convert_impl.hpp b/include/behaviortree_cpp/utils/convert_impl.hpp index 6baaa03fd..64baef967 100644 --- a/include/behaviortree_cpp/utils/convert_impl.hpp +++ b/include/behaviortree_cpp/utils/convert_impl.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Davide Faconti, Eurecat - All Rights Reserved +/* Copyright (C) 2022-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/include/behaviortree_cpp/utils/safe_any.hpp b/include/behaviortree_cpp/utils/safe_any.hpp index ce94b2eb7..4dc1c8718 100644 --- a/include/behaviortree_cpp/utils/safe_any.hpp +++ b/include/behaviortree_cpp/utils/safe_any.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Davide Faconti - All Rights Reserved +/* Copyright (C) 2022-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/action_node.cpp b/src/action_node.cpp index 2bf78f964..9493a2af9 100644 --- a/src/action_node.cpp +++ b/src/action_node.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2023 Davide Faconti - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/actions/updated_action.cpp b/src/actions/updated_action.cpp index 15be65600..e3b580b61 100644 --- a/src/actions/updated_action.cpp +++ b/src/actions/updated_action.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 Davide Faconti - All Rights Reserved +/* Copyright (C) 2024-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/behavior_tree.cpp b/src/behavior_tree.cpp index 1b07739c9..02c236129 100644 --- a/src/behavior_tree.cpp +++ b/src/behavior_tree.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved +/* Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/bt_factory.cpp b/src/bt_factory.cpp index d7dd1dbb0..3bb79763b 100644 --- a/src/bt_factory.cpp +++ b/src/bt_factory.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2022 Davide Faconti, Eurecat - All Rights Reserved +/* Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/condition_node.cpp b/src/condition_node.cpp index 0e2748e73..ad04b4613 100644 --- a/src/condition_node.cpp +++ b/src/condition_node.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/control_node.cpp b/src/control_node.cpp index b4a7d7d5c..28e96b3d9 100644 --- a/src/control_node.cpp +++ b/src/control_node.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/controls/fallback_node.cpp b/src/controls/fallback_node.cpp index 4b8fb3afb..86fed0fab 100644 --- a/src/controls/fallback_node.cpp +++ b/src/controls/fallback_node.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/controls/if_then_else_node.cpp b/src/controls/if_then_else_node.cpp index bb50a235b..2dce72fa0 100644 --- a/src/controls/if_then_else_node.cpp +++ b/src/controls/if_then_else_node.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Davide Faconti - All Rights Reserved +/* Copyright (C) 2020-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/controls/manual_node.cpp b/src/controls/manual_node.cpp index 82e7e443b..1ca31eda3 100644 --- a/src/controls/manual_node.cpp +++ b/src/controls/manual_node.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/controls/parallel_all_node.cpp b/src/controls/parallel_all_node.cpp index 4e5abc6a4..cf0ae9141 100644 --- a/src/controls/parallel_all_node.cpp +++ b/src/controls/parallel_all_node.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Davide Faconti - All Rights Reserved +/* Copyright (C) 2023-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/controls/parallel_node.cpp b/src/controls/parallel_node.cpp index 9e7627208..ec79b3326 100644 --- a/src/controls/parallel_node.cpp +++ b/src/controls/parallel_node.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/controls/reactive_fallback.cpp b/src/controls/reactive_fallback.cpp index 91f7e01b2..cafba1e21 100644 --- a/src/controls/reactive_fallback.cpp +++ b/src/controls/reactive_fallback.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Davide Faconti, Eurecat - All Rights Reserved +/* Copyright (C) 2020-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/controls/reactive_sequence.cpp b/src/controls/reactive_sequence.cpp index 2fc4110dd..ec16137a5 100644 --- a/src/controls/reactive_sequence.cpp +++ b/src/controls/reactive_sequence.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Davide Faconti, Eurecat - All Rights Reserved +/* Copyright (C) 2020-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/controls/sequence_node.cpp b/src/controls/sequence_node.cpp index a19c6b9ec..306fea022 100644 --- a/src/controls/sequence_node.cpp +++ b/src/controls/sequence_node.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/controls/sequence_with_memory_node.cpp b/src/controls/sequence_with_memory_node.cpp index 3031de9b2..1a5253476 100644 --- a/src/controls/sequence_with_memory_node.cpp +++ b/src/controls/sequence_with_memory_node.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/controls/switch_node.cpp b/src/controls/switch_node.cpp index 847d82277..121248edd 100644 --- a/src/controls/switch_node.cpp +++ b/src/controls/switch_node.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019-2022 Davide Faconti, Eurecat - All Rights Reserved +/* Copyright (C) 2019-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/controls/while_do_else_node.cpp b/src/controls/while_do_else_node.cpp index 943f559d3..480f00dcb 100644 --- a/src/controls/while_do_else_node.cpp +++ b/src/controls/while_do_else_node.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Davide Faconti - All Rights Reserved +/* Copyright (C) 2020-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/decorator_node.cpp b/src/decorator_node.cpp index 94294e34c..7e64ebda3 100644 --- a/src/decorator_node.cpp +++ b/src/decorator_node.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2017 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/decorators/inverter_node.cpp b/src/decorators/inverter_node.cpp index ae854dafd..05a7c72cc 100644 --- a/src/decorators/inverter_node.cpp +++ b/src/decorators/inverter_node.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/decorators/repeat_node.cpp b/src/decorators/repeat_node.cpp index 9ea023ee1..960ddd76c 100644 --- a/src/decorators/repeat_node.cpp +++ b/src/decorators/repeat_node.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/decorators/retry_node.cpp b/src/decorators/retry_node.cpp index d8c689c78..5b76dbadb 100644 --- a/src/decorators/retry_node.cpp +++ b/src/decorators/retry_node.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/decorators/timeout_node.cpp b/src/decorators/timeout_node.cpp index ae1018baa..345409b82 100644 --- a/src/decorators/timeout_node.cpp +++ b/src/decorators/timeout_node.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2023 Davide Faconti - All Rights Reserved +/* Copyright (C) 2018-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/decorators/updated_decorator.cpp b/src/decorators/updated_decorator.cpp index b4c9dd764..ff35222bd 100644 --- a/src/decorators/updated_decorator.cpp +++ b/src/decorators/updated_decorator.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 Davide Faconti - All Rights Reserved +/* Copyright (C) 2024-2025 Davide Faconti - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/tree_node.cpp b/src/tree_node.cpp index 946492318..fb5e4ebab 100644 --- a/src/tree_node.cpp +++ b/src/tree_node.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2018 Michele Colledanchise - All Rights Reserved - * Copyright (C) 2018-2022 Davide Faconti, Eurecat - All Rights Reserved + * Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, diff --git a/src/xml_parsing.cpp b/src/xml_parsing.cpp index 5426cc2e4..dbdac5ea3 100644 --- a/src/xml_parsing.cpp +++ b/src/xml_parsing.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2020 Davide Faconti, Eurecat - All Rights Reserved +/* Copyright (C) 2018-2025 Davide Faconti, Eurecat - All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, From 61592fe0cd3210df6b186b472f695f53ea061922 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Mon, 29 Dec 2025 22:12:46 +0100 Subject: [PATCH 049/147] move and clan up tests --- docs/PORT_CONNECTION_RULES.md | 337 ++++++++++ tests/CMakeLists.txt | 1 + tests/gtest_blackboard.cpp | 119 +--- tests/gtest_port_type_rules.cpp | 1034 +++++++++++++++++++++++++++++++ tests/gtest_ports.cpp | 152 +---- 5 files changed, 1376 insertions(+), 267 deletions(-) create mode 100644 docs/PORT_CONNECTION_RULES.md create mode 100644 tests/gtest_port_type_rules.cpp diff --git a/docs/PORT_CONNECTION_RULES.md b/docs/PORT_CONNECTION_RULES.md new file mode 100644 index 000000000..7eb353430 --- /dev/null +++ b/docs/PORT_CONNECTION_RULES.md @@ -0,0 +1,337 @@ +# Port Connection and Validation Rules + +This document describes the rules that govern how ports can be connected in BehaviorTree.CPP, including type checking, type conversion, and special cases. + +## Overview + +BehaviorTree.CPP uses a type system for ports that enforces type safety while providing flexibility through several special rules. Type checking occurs primarily at **tree creation time** (when parsing XML), not at runtime. + +## Port Types Classification + +### 1. Strongly Typed Ports + +A port is **strongly typed** when declared with a specific type: + +```cpp +InputPort("my_port") +OutputPort("result") +InputPort("goal") +``` + +### 2. Generic/Weakly Typed Ports (AnyTypeAllowed) + +A port is **generic** (not strongly typed) when: +- Declared without a type parameter: `InputPort<>("my_port")` +- Declared with `AnyTypeAllowed`: `InputPort("my_port")` +- Declared with `BT::Any`: `InputPort("my_port")` + +```cpp +// All of these create generic ports: +InputPort<>("value") // defaults to AnyTypeAllowed +InputPort("value") // explicit AnyTypeAllowed +InputPort("value") // BT::Any type +``` + +The `isStronglyTyped()` method returns `false` for these ports: + +```cpp +// From basic_types.h +bool isStronglyTyped() const +{ + return type_info_ != typeid(AnyTypeAllowed) && type_info_ != typeid(BT::Any); +} +``` + +## Port Connection Rules + +### Rule 1: Same Type - Always Compatible + +Ports of the **exact same type** can always be connected: + +```cpp +// Node A +OutputPort("value") // writes int + +// Node B +InputPort("value") // reads int + +// Connection: OK +``` + +**Test reference:** `gtest_port_type_rules.cpp` - `SameType_IntToInt`, `SameType_StringToString`, `SameType_CustomTypeToCustomType` tests + +### Rule 2: Generic Port - Compatible with Any Type + +A **generic port** (`AnyTypeAllowed` or `BT::Any`) can connect to any other port: + +```cpp +// Node A with generic output +OutputPort<>("output") // generic, can write anything + +// Node B with typed input +InputPort("input_int") // expects int + +// Connection: OK - generic port accepts any type +``` + +**Test reference:** `gtest_port_type_rules.cpp` - `GenericPort_AcceptsInt`, `GenericPort_AcceptsString`, `GenericOutput_ToTypedInput` tests + +### Rule 3: String is a "Universal Donor" (Generic Port) + +When a blackboard entry is created as `std::string`, it can be connected to ports of **any type** that has a `convertFromString()` specialization. This is the "string as generic port" rule. + +**Source:** `xml_parsing.cpp` +```cpp +// special case related to convertFromString +bool const string_input = (prev_info->type() == typeid(std::string)); + +if(port_type_mismatch && !string_input) +{ + // Error thrown only if NOT a string input + throw RuntimeError("The creation of the tree failed..."); +} +``` + +**Example:** +```xml + + + + + + + +``` + +**Also applies to:** +- Subtree port passing (string values passed to typed subtree ports) +- Script node assignments + +**Test reference:** `gtest_port_type_rules.cpp` - `StringToInt_ViaConvertFromString`, `StringToCustomType_ViaConvertFromString`, `SubtreeStringInput_ToTypedPort` tests + +### Rule 4: String Creation in Blackboard + +When using `Blackboard::set()`, the entry is created with `AnyTypeAllowed` type, not `std::string`: + +**Source:** `blackboard.h` +```cpp +// if a new generic port is created with a string, it's type should be AnyTypeAllowed +if constexpr(std::is_same_v) +{ + entry = createEntryImpl(key, PortInfo(PortDirection::INOUT)); // AnyTypeAllowed +} +``` + +This allows subsequent writes of different types to the same entry. + +**Test reference:** `gtest_port_type_rules.cpp` - `BlackboardSetString_CreatesGenericEntry`, `StringEntry_CanBecomeTyped` tests + +### Rule 5: Type Lock After First Strongly-Typed Write + +Once a blackboard entry receives a **strongly typed** value, its type is locked: + +**Source:** `blackboard.h` +```cpp +// special case: entry exists but it is not strongly typed... yet +if(!entry.info.isStronglyTyped()) +{ + // Use the new type to create a strongly typed entry + entry.info = TypeInfo::Create(); + // ... + return; +} +``` + +After this, writing a different type will fail (with exceptions noted below). + +**Test reference:** `gtest_port_type_rules.cpp` - `TypeLock_CannotChangeAfterTypedWrite`, `TypeLock_XMLTreeCreation_TypeMismatch`, `TypeLock_RuntimeTypeChange_Fails` tests + +### Rule 6: BT::Any Bypasses Type Checking + +When a blackboard entry is **created with type `BT::Any`**, it can store different types over time. This requires the entry to be explicitly created as `BT::Any` type. + +**Important:** Wrapping a value with `BT::Any()` does **not** bypass type checking - the wrapper is unwrapped and the inner type is used: + +```cpp +// This creates an entry of type int, NOT BT::Any +bb->set("key", BT::Any(42)); + +// This will FAIL - entry is int, not BT::Any +bb->set("key", BT::Any("hello")); // throws LogicError +``` + +To actually allow different types, create the entry as `BT::Any`: + +```cpp +// Create entry explicitly as BT::Any type +bb->createEntry("key", TypeInfo::Create()); + +// Now different types are allowed +bb->set("key", BT::Any(42)); // OK +bb->set("key", BT::Any("hello")); // OK +bb->set("key", BT::Any(3.14)); // OK +``` + +**Test reference:** `gtest_port_type_rules.cpp` - `BTAny_WrapperDoesNotBypassTypeCheck`, `BTAny_EntryType_AllowsDifferentTypes`, `BTAny_Port_AcceptsDifferentTypes` tests + +### Rule 7: Type Mismatch Between Strongly Typed Ports - Error + +If two **strongly typed** ports with **different types** try to use the same blackboard entry, an error is thrown at tree creation: + +```xml + + + + + +``` + +**Test reference:** `gtest_port_type_rules.cpp` - `TypeLock_XMLTreeCreation_TypeMismatch`, `TypeLock_IntToDouble_Fails`, `TypeLock_CustomTypeChange_Fails` tests + +## Type Conversion via convertFromString + +### Built-in Conversions + +The library provides `convertFromString()` for: +- `int`, `long`, `long long`, and unsigned variants +- `float`, `double` +- `bool` (accepts "true"/"false", "1"/"0") +- `std::string` +- `std::vector` (semicolon-separated values) +- Enums (when registered) + +### Custom Type Conversion + +To make a custom type compatible with string ports, specialize `convertFromString`: + +```cpp +namespace BT +{ +template <> +inline Position2D convertFromString(StringView str) +{ + auto parts = splitString(str, ';'); + if(parts.size() != 2) + throw RuntimeError("invalid input"); + + Position2D output; + output.x = convertFromString(parts[0]); + output.y = convertFromString(parts[1]); + return output; +} +} +``` + +**Test reference:** `gtest_ports.cpp`, `t03_generic_ports.cpp` + +### JSON Format Support + +Custom types can also use JSON format with "json:" prefix: + +```cpp +InputPort("pointE", R"(json:{"x":9,"y":10})", "description") +``` + +**Test reference:** `gtest_ports.cpp` + +## Validation Timeline + +### At Tree Creation (XML Parsing) + +1. **Port name validation** - Checks port exists in node manifest +2. **Literal value validation** - If port value is not a blackboard reference, validates conversion +3. **Blackboard entry type check** - If entry exists, checks type compatibility + +**Source:** `xml_parsing.cpp` +```cpp +if(!is_blackboard && port_model.converter() && port_model.isStronglyTyped()) +{ + try + { + port_model.converter()(port_value); // Validate conversion + } + catch(std::exception& ex) + { + throw LogicError("The port... can not be converted to " + port_model.typeName()); + } +} +``` + +### At Runtime (Blackboard::set) + +1. **Type match check** - Compares new type with entry's declared type +2. **String conversion attempt** - If mismatch, tries `parseString()` + +## Summary Table + +| Scenario | Compatible? | Notes | +|----------|-------------|-------| +| Same types | Yes | Always works | +| Generic port (either side) | Yes | `AnyTypeAllowed` or `BT::Any` | +| String → Typed port | Yes | Via `convertFromString()` | +| Typed → String port | No | Type mismatch error | +| int → double | No | Different strongly-typed | +| Point2D → std::string | No | Unless entry was string first | +| BT::Any entry to anything | Yes | Entry must be created as BT::Any type | + +## Reserved Port Names + +The following names **cannot** be used for ports: +- `name` - Reserved for node instance name +- `ID` - Reserved for node type ID +- Names starting with `_` - Reserved for internal use + +**Test reference:** `gtest_port_type_rules.cpp` - `ReservedPortName_ThrowsOnRegistration` test + +## Common Patterns + +### Pattern 1: Type-Safe Port Chain +```xml + + + + +``` + +### Pattern 2: String Literal to Typed Port +```xml + + + + +``` + +### Pattern 3: Generic Intermediate Storage +```xml + + + + + +``` + +## Error Messages + +Common type-related errors: + +1. **"The creation of the tree failed because the port [X] was initially created with type [A] and, later type [B] was used somewhere else."** + - Cause: Two nodes use same blackboard key with incompatible types + - Solution: Ensure consistent types or use string/generic ports + +2. **"Blackboard::set(X): once declared, the type of a port shall not change."** + - Cause: Runtime attempt to change entry type + - Solution: Use consistent types or BT::Any + +3. **"The port with name X and value Y can not be converted to Z"** + - Cause: Literal value cannot be parsed to port type + - Solution: Fix value format or add `convertFromString` specialization + +## References + +- Source: `include/behaviortree_cpp/basic_types.h` - Type system definitions +- Source: `include/behaviortree_cpp/blackboard.h` - Blackboard type checking +- Source: `src/xml_parsing.cpp` - Tree creation validation +- Tests: `tests/gtest_port_type_rules.cpp` - Comprehensive port type rule tests +- Tests: `tests/gtest_ports.cpp` - Port connection tests +- Tests: `tests/gtest_blackboard.cpp` - Blackboard tests +- Tutorial: `examples/t03_generic_ports.cpp` - Custom type example diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9e667babe..14a4fef8e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,6 +15,7 @@ set(BT_TESTS gtest_parallel.cpp gtest_preconditions.cpp gtest_ports.cpp + gtest_port_type_rules.cpp gtest_postconditions.cpp gtest_match.cpp gtest_json.cpp diff --git a/tests/gtest_blackboard.cpp b/tests/gtest_blackboard.cpp index 75c3db6d1..36b34b908 100644 --- a/tests/gtest_blackboard.cpp +++ b/tests/gtest_blackboard.cpp @@ -47,30 +47,6 @@ class BB_TestNode : public SyncActionNode } }; -class BB_TypedTestNode : public SyncActionNode -{ -public: - BB_TypedTestNode(const std::string& name, const NodeConfig& config) - : SyncActionNode(name, config) - {} - - NodeStatus tick() - { - return NodeStatus::SUCCESS; - } - - static PortsList providedPorts() - { - return { BT::InputPort("input"), - BT::InputPort("input_int"), - BT::InputPort("input_string"), - - BT::OutputPort("output"), - BT::OutputPort("output_int"), - BT::OutputPort("output_string") }; - } -}; - TEST(BlackboardTest, GetInputsFromBlackboard) { auto bb = Blackboard::create(); @@ -197,37 +173,7 @@ TEST(BlackboardTest, TypoInPortName) ASSERT_THROW(auto tree = factory.createTreeFromText(xml_text), RuntimeError); } -TEST(BlackboardTest, CheckPortType) -{ - BehaviorTreeFactory factory; - factory.registerNodeType("TypedNode"); - - //----------------------------- - std::string good_one = R"( - - - - - - - - )"; - - auto tree = factory.createTreeFromText(good_one); - ASSERT_NE(tree.rootNode(), nullptr); - //----------------------------- - std::string bad_one = R"( - - - - - - - - )"; - - ASSERT_THROW(auto tree = factory.createTreeFromText(bad_one), RuntimeError); -} +// NOTE: CheckPortType test moved to gtest_port_type_rules.cpp class RefCountClass { @@ -717,67 +663,8 @@ TEST(BlackboardTest, SetBlackboard_Upd_Ts_SeqId) ASSERT_GT(seq_id2, seq_id1); } -TEST(BlackboardTest, SetBlackboard_ChangeType1) -{ - BT::BehaviorTreeFactory factory; - - const std::string xml_text = R"( - - - - - - - - - )"; - - factory.registerBehaviorTreeFromText(xml_text); - auto tree = factory.createTree("MainTree"); - auto& blackboard = tree.subtrees.front()->blackboard; - - const Point point = { 2, 7 }; - blackboard->set("first_point", point); - blackboard->set("random_str", "Hello!"); - - // First tick should succeed - ASSERT_NO_THROW(tree.tickExactlyOnce()); - const auto entry_ptr = blackboard->getEntry("other_point"); - std::this_thread::sleep_for(std::chrono::milliseconds{ 5 }); - // Second tick should throw due to type mismatch - EXPECT_THROW({ tree.tickWhileRunning(); }, BT::LogicError); -} - -TEST(BlackboardTest, SetBlackboard_ChangeType2) -{ - BT::BehaviorTreeFactory factory; - - const std::string xml_text = R"( - - - - - - - - - )"; - - factory.registerBehaviorTreeFromText(xml_text); - auto tree = factory.createTree("MainTree"); - auto& blackboard = tree.subtrees.front()->blackboard; - - const Point point = { 2, 7 }; - blackboard->set("first_point", point); - blackboard->set("random_num", 57); - - // First tick should succeed - ASSERT_NO_THROW(tree.tickExactlyOnce()); - const auto entry_ptr = blackboard->getEntry("other_point"); - std::this_thread::sleep_for(std::chrono::milliseconds{ 5 }); - // Second tick should throw due to type mismatch - EXPECT_THROW({ tree.tickWhileRunning(); }, BT::LogicError); -} +// NOTE: SetBlackboard_ChangeType1 and SetBlackboard_ChangeType2 tests +// moved to gtest_port_type_rules.cpp // Simple Action that updates an instance of Point in the blackboard class UpdatePosition : public BT::SyncActionNode diff --git a/tests/gtest_port_type_rules.cpp b/tests/gtest_port_type_rules.cpp new file mode 100644 index 000000000..66541761c --- /dev/null +++ b/tests/gtest_port_type_rules.cpp @@ -0,0 +1,1034 @@ +/* Copyright (C) 2018-2024 Davide Faconti, Eurecat - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * @file gtest_port_type_rules.cpp + * @brief Comprehensive tests for port type connection and validation rules. + * + * This file tests the following rules documented in docs/PORT_CONNECTION_RULES.md: + * + * 1. Same type ports are always compatible + * 2. Generic ports (AnyTypeAllowed, BT::Any) are compatible with any type + * 3. String is a "universal donor" - can connect to any typed port via convertFromString + * 4. String creation in blackboard creates AnyTypeAllowed entry + * 5. Type locks after first strongly-typed write + * 6. Safe numeric casting between arithmetic types + * 7. BT::Any bypasses type checking + * 8. Type mismatch between strongly typed ports causes error + */ + +#include +#include "behaviortree_cpp/bt_factory.h" +#include "behaviortree_cpp/blackboard.h" + +using namespace BT; + +//------------------------------------------------------------------------------ +// Custom types for testing +//------------------------------------------------------------------------------ + +struct TestPoint +{ + double x = 0; + double y = 0; + bool operator==(const TestPoint& other) const + { + return x == other.x && y == other.y; + } + bool operator!=(const TestPoint& other) const + { + return !(*this == other); + } +}; + +// Custom type without string conversion (for testing type mismatch) +struct CustomTypeNoConversion +{ + int value = 0; +}; + +namespace BT +{ +// Provide string conversion for TestPoint (uses semicolon separator) +template <> +[[nodiscard]] inline TestPoint convertFromString(StringView str) +{ + auto parts = splitString(str, ';'); + if(parts.size() != 2) + { + throw RuntimeError("invalid TestPoint format, expected 'x;y'"); + } + TestPoint output; + output.x = convertFromString(parts[0]); + output.y = convertFromString(parts[1]); + return output; +} +} // namespace BT + +//------------------------------------------------------------------------------ +// Test node classes +//------------------------------------------------------------------------------ + +// Node with strongly typed int ports +class NodeWithIntPorts : public SyncActionNode +{ +public: + NodeWithIntPorts(const std::string& name, const NodeConfig& config) + : SyncActionNode(name, config) + {} + + NodeStatus tick() override + { + auto input = getInput("input"); + if(input) + { + setOutput("output", input.value() * 2); + return NodeStatus::SUCCESS; + } + return NodeStatus::FAILURE; + } + + static PortsList providedPorts() + { + return { InputPort("input"), OutputPort("output") }; + } +}; + +// Node with strongly typed string ports +class NodeWithStringPorts : public SyncActionNode +{ +public: + NodeWithStringPorts(const std::string& name, const NodeConfig& config) + : SyncActionNode(name, config) + {} + + NodeStatus tick() override + { + auto input = getInput("input"); + if(input) + { + setOutput("output", input.value()); + return NodeStatus::SUCCESS; + } + return NodeStatus::FAILURE; + } + + static PortsList providedPorts() + { + return { InputPort("input"), OutputPort("output") }; + } +}; + +// Node with strongly typed double ports +class NodeWithDoublePorts : public SyncActionNode +{ +public: + NodeWithDoublePorts(const std::string& name, const NodeConfig& config) + : SyncActionNode(name, config) + {} + + NodeStatus tick() override + { + auto input = getInput("input"); + if(input) + { + setOutput("output", input.value()); + return NodeStatus::SUCCESS; + } + return NodeStatus::FAILURE; + } + + static PortsList providedPorts() + { + return { InputPort("input"), OutputPort("output") }; + } +}; + +// Node with generic (AnyTypeAllowed) ports +class NodeWithGenericPorts : public SyncActionNode +{ +public: + NodeWithGenericPorts(const std::string& name, const NodeConfig& config) + : SyncActionNode(name, config) + {} + + NodeStatus tick() override + { + return NodeStatus::SUCCESS; + } + + static PortsList providedPorts() + { + // Ports without type parameter default to AnyTypeAllowed + return { InputPort<>("input"), OutputPort<>("output") }; + } +}; + +// Node with BT::Any ports +class NodeWithAnyPorts : public SyncActionNode +{ +public: + NodeWithAnyPorts(const std::string& name, const NodeConfig& config) + : SyncActionNode(name, config) + {} + + NodeStatus tick() override + { + // Can write different types to BT::Any port + setOutput("output", BT::Any(42)); + setOutput("output", BT::Any("hello")); + setOutput("output", BT::Any(3.14)); + return NodeStatus::SUCCESS; + } + + static PortsList providedPorts() + { + return { InputPort("input"), OutputPort("output") }; + } +}; + +// Node with TestPoint custom type ports +class NodeWithTestPointPorts : public SyncActionNode +{ +public: + NodeWithTestPointPorts(const std::string& name, const NodeConfig& config) + : SyncActionNode(name, config) + {} + + NodeStatus tick() override + { + auto input = getInput("input"); + if(input) + { + setOutput("output", input.value()); + return NodeStatus::SUCCESS; + } + return NodeStatus::FAILURE; + } + + static PortsList providedPorts() + { + return { InputPort("input"), OutputPort("output") }; + } +}; + +// Node with vector ports (for testing string to container conversion) +class NodeWithVectorPorts : public SyncActionNode +{ +public: + NodeWithVectorPorts(const std::string& name, const NodeConfig& config, + std::vector* result) + : SyncActionNode(name, config), result_(result) + {} + + NodeStatus tick() override + { + auto input = getInput>("input"); + if(input && result_) + { + *result_ = input.value(); + return NodeStatus::SUCCESS; + } + return NodeStatus::FAILURE; + } + + static PortsList providedPorts() + { + return { InputPort>("input") }; + } + +private: + std::vector* result_; +}; + +//============================================================================== +// TEST SECTION 1: Same Type Ports (Rule 1) +//============================================================================== + +TEST(PortTypeRules, SameType_IntToInt) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithIntPorts"); + + std::string xml = R"( + + + + + + + + + )"; + + auto tree = factory.createTreeFromText(xml); + auto status = tree.tickWhileRunning(); + + ASSERT_EQ(status, NodeStatus::SUCCESS); + ASSERT_EQ(tree.rootBlackboard()->get("result"), 84); // 21 * 2 * 2 +} + +TEST(PortTypeRules, SameType_StringToString) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithStringPorts"); + + std::string xml = R"( + + + + + + + + + )"; + + auto tree = factory.createTreeFromText(xml); + auto status = tree.tickWhileRunning(); + + ASSERT_EQ(status, NodeStatus::SUCCESS); + ASSERT_EQ(tree.rootBlackboard()->get("result"), "hello"); +} + +TEST(PortTypeRules, SameType_CustomTypeToCustomType) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithTestPointPorts"); + + std::string xml = R"( + + + + + + + + + )"; + + auto tree = factory.createTreeFromText(xml); + auto status = tree.tickWhileRunning(); + + ASSERT_EQ(status, NodeStatus::SUCCESS); + auto result = tree.rootBlackboard()->get("result"); + ASSERT_EQ(result.x, 1.5); + ASSERT_EQ(result.y, 2.5); +} + +//============================================================================== +// TEST SECTION 2: Generic Ports (Rule 2) +//============================================================================== + +TEST(PortTypeRules, GenericPort_AcceptsInt) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithIntPorts"); + factory.registerNodeType("NodeWithGenericPorts"); + + std::string xml = R"( + + + + + + + + + )"; + + ASSERT_NO_THROW(auto tree = factory.createTreeFromText(xml)); +} + +TEST(PortTypeRules, GenericPort_AcceptsString) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithStringPorts"); + factory.registerNodeType("NodeWithGenericPorts"); + + std::string xml = R"( + + + + + + + + + )"; + + ASSERT_NO_THROW(auto tree = factory.createTreeFromText(xml)); +} + +TEST(PortTypeRules, GenericOutput_ToTypedInput) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithIntPorts"); + factory.registerNodeType("NodeWithGenericPorts"); + + // Generic output connected to typed input via blackboard + std::string xml = R"( + + + + + + + + + )"; + + // This should create the tree without error (types resolved at runtime) + ASSERT_NO_THROW(auto tree = factory.createTreeFromText(xml)); +} + +//============================================================================== +// TEST SECTION 3: String as Universal Donor (Rule 3) +//============================================================================== + +TEST(PortTypeRules, StringToInt_ViaConvertFromString) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithIntPorts"); + + // SetBlackboard creates a string entry, but NodeWithIntPorts expects int + std::string xml = R"( + + + + + + + + + )"; + + auto tree = factory.createTreeFromText(xml); + auto status = tree.tickWhileRunning(); + + ASSERT_EQ(status, NodeStatus::SUCCESS); + ASSERT_EQ(tree.rootBlackboard()->get("result"), 84); // 42 * 2 +} + +TEST(PortTypeRules, StringToDouble_ViaConvertFromString) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithDoublePorts"); + + std::string xml = R"( + + + + + + + + + )"; + + auto tree = factory.createTreeFromText(xml); + auto status = tree.tickWhileRunning(); + + ASSERT_EQ(status, NodeStatus::SUCCESS); + ASSERT_DOUBLE_EQ(tree.rootBlackboard()->get("result"), 3.14); +} + +TEST(PortTypeRules, StringToCustomType_ViaConvertFromString) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithTestPointPorts"); + + // String "1.0;2.0" should convert to TestPoint via convertFromString + std::string xml = R"( + + + + + + + + + )"; + + auto tree = factory.createTreeFromText(xml); + auto status = tree.tickWhileRunning(); + + ASSERT_EQ(status, NodeStatus::SUCCESS); + auto result = tree.rootBlackboard()->get("result"); + ASSERT_EQ(result.x, 1.0); + ASSERT_EQ(result.y, 2.0); +} + +TEST(PortTypeRules, StringToVector_ViaConvertFromString) +{ + BehaviorTreeFactory factory; + std::vector result; + factory.registerNodeType("NodeWithVectorPorts", &result); + + // Semicolon-separated string converts to vector + std::string xml = R"( + + + + + + )"; + + auto tree = factory.createTreeFromText(xml); + auto status = tree.tickWhileRunning(); + + ASSERT_EQ(status, NodeStatus::SUCCESS); + ASSERT_EQ(result.size(), 3u); + ASSERT_EQ(result[0], 1.0); + ASSERT_EQ(result[1], 2.0); + ASSERT_EQ(result[2], 3.0); +} + +TEST(PortTypeRules, SubtreeStringInput_ToTypedPort) +{ + BehaviorTreeFactory factory; + std::vector result; + factory.registerNodeType("NodeWithVectorPorts", &result); + + // String passed to subtree, then used by typed port + std::string xml = R"( + + + + + + + + + )"; + + factory.registerBehaviorTreeFromText(xml); + auto tree = factory.createTree("Main"); + auto status = tree.tickWhileRunning(); + + ASSERT_EQ(status, NodeStatus::SUCCESS); + ASSERT_EQ(result.size(), 2u); + ASSERT_EQ(result[0], 3.0); + ASSERT_EQ(result[1], 7.0); +} + +//============================================================================== +// TEST SECTION 4: String Creates AnyTypeAllowed Entry (Rule 4) +//============================================================================== + +TEST(PortTypeRules, BlackboardSetString_CreatesGenericEntry) +{ + auto bb = Blackboard::create(); + + // Setting a string should create an AnyTypeAllowed entry + bb->set("key", std::string("hello")); + + auto info = bb->entryInfo("key"); + ASSERT_NE(info, nullptr); + + // Entry should NOT be strongly typed (isStronglyTyped() == false) + ASSERT_FALSE(info->isStronglyTyped()); +} + +TEST(PortTypeRules, BlackboardSetInt_CreatesStronglyTypedEntry) +{ + auto bb = Blackboard::create(); + + bb->set("key", 42); + + auto info = bb->entryInfo("key"); + ASSERT_NE(info, nullptr); + ASSERT_TRUE(info->isStronglyTyped()); + ASSERT_EQ(info->type(), typeid(int)); +} + +TEST(PortTypeRules, StringEntry_CanBecomeTyped) +{ + auto bb = Blackboard::create(); + + // First set as string (creates AnyTypeAllowed) + bb->set("key", std::string("42")); + ASSERT_FALSE(bb->entryInfo("key")->isStronglyTyped()); + + // Now set as int - should lock the type + bb->set("key", 42); + ASSERT_TRUE(bb->entryInfo("key")->isStronglyTyped()); + ASSERT_EQ(bb->entryInfo("key")->type(), typeid(int)); +} + +//============================================================================== +// TEST SECTION 5: Type Locks After First Strongly-Typed Write (Rule 5) +//============================================================================== + +TEST(PortTypeRules, TypeLock_CannotChangeAfterTypedWrite) +{ + auto bb = Blackboard::create(); + + // First set as int (strongly typed) + bb->set("key", 42); + ASSERT_TRUE(bb->entryInfo("key")->isStronglyTyped()); + + // Cannot change to different type - throws RuntimeError for string (tries to convert) + // or LogicError for incompatible types + EXPECT_ANY_THROW(bb->set("key", std::string("hello"))); + EXPECT_ANY_THROW(bb->set("key", 3.14)); +} + +TEST(PortTypeRules, TypeLock_XMLTreeCreation_TypeMismatch) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithIntPorts"); + factory.registerNodeType("NodeWithStringPorts"); + + // First node creates int entry, second expects string - should fail + std::string xml = R"( + + + + + + + + + )"; + + EXPECT_THROW(auto tree = factory.createTreeFromText(xml), RuntimeError); +} + +TEST(PortTypeRules, TypeLock_IntToDouble_Fails) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithIntPorts"); + factory.registerNodeType("NodeWithDoublePorts"); + + // int output to double input - type mismatch + std::string xml = R"( + + + + + + + + + )"; + + EXPECT_THROW(auto tree = factory.createTreeFromText(xml), RuntimeError); +} + +TEST(PortTypeRules, TypeLock_CustomTypeChange_Fails) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithTestPointPorts"); + factory.registerNodeType("NodeWithIntPorts"); + + // TestPoint output to int input - should fail at tree creation + std::string xml = R"( + + + + + + + + + )"; + + // Throws either RuntimeError or LogicError depending on validation stage + EXPECT_ANY_THROW(auto tree = factory.createTreeFromText(xml)); +} + +TEST(PortTypeRules, TypeLock_RuntimeTypeChange_Fails) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithTestPointPorts"); + factory.registerNodeType("NodeWithStringPorts"); + + std::string xml = R"( + + + + + + + + + + )"; + + auto tree = factory.createTreeFromText(xml); + auto& bb = tree.subtrees.front()->blackboard; + + TestPoint point = { 2, 7 }; + bb->set("point_value", point); + bb->set("string_value", std::string("Hello!")); + + // First tick succeeds (creates entry as TestPoint) + ASSERT_NO_THROW(tree.tickExactlyOnce()); + + std::this_thread::sleep_for(std::chrono::milliseconds{ 5 }); + + // Second tick fails (tries to change TestPoint to string) + EXPECT_THROW(tree.tickWhileRunning(), LogicError); +} + +//============================================================================== +// TEST SECTION 6: Safe Numeric Casting (Rule 6) +//============================================================================== + +TEST(PortTypeRules, SafeCast_IntToUint8_InRange) +{ + auto bb = Blackboard::create(); + + // Create entry as uint8_t + bb->set("key", 100); + ASSERT_TRUE(bb->entryInfo("key")->isStronglyTyped()); + + // int(50) fits in uint8_t, should succeed + ASSERT_NO_THROW(bb->set("key", int(50))); + ASSERT_EQ(bb->get("key"), 50); +} + +TEST(PortTypeRules, SafeCast_IntToUint8_Overflow) +{ + auto bb = Blackboard::create(); + + // Create entry as uint8_t + bb->set("key", 100); + + // int(300) > 255, should fail + EXPECT_THROW(bb->set("key", int(300)), LogicError); +} + +TEST(PortTypeRules, SafeCast_IntToUint8_Negative) +{ + auto bb = Blackboard::create(); + + // Create entry as uint8_t + bb->set("key", 100); + + // Negative value cannot fit in unsigned type + EXPECT_THROW(bb->set("key", int(-1)), LogicError); +} + +TEST(PortTypeRules, SafeCast_DifferentIntTypes_NotAllowed) +{ + auto bb = Blackboard::create(); + + // Create entry as int64_t + bb->set("key", 100); + + // Even though int values fit in int64_t, different types are NOT allowed + // Safe casting only works within the SAME conceptual type (e.g., int to uint8_t) + EXPECT_THROW(bb->set("key", int(-1000000)), LogicError); + + // Setting same type works + ASSERT_NO_THROW(bb->set("key", int64_t(1000000))); +} + +//============================================================================== +// TEST SECTION 7: BT::Any Bypasses Type Checking (Rule 7) +//============================================================================== + +TEST(PortTypeRules, BTAny_WrapperDoesNotBypassTypeCheck) +{ + auto bb = Blackboard::create(); + + // Note: BT::Any(42) creates an entry of type int, NOT type BT::Any + // The BT::Any wrapper is unwrapped when stored + bb->set("key", BT::Any(42)); + + // Cannot change to different type even with BT::Any wrapper + // because the entry was created as int + EXPECT_THROW(bb->set("key", BT::Any("hello")), LogicError); +} + +TEST(PortTypeRules, BTAny_EntryType_AllowsDifferentTypes) +{ + auto bb = Blackboard::create(); + + // Create entry explicitly as BT::Any type + bb->createEntry("key", TypeInfo::Create()); + + // Now we can set different types because the entry type is BT::Any + ASSERT_NO_THROW(bb->set("key", BT::Any(42))); + ASSERT_NO_THROW(bb->set("key", BT::Any("hello"))); + ASSERT_NO_THROW(bb->set("key", BT::Any(3.14))); +} + +TEST(PortTypeRules, BTAny_Port_AcceptsDifferentTypes) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithAnyPorts"); + factory.registerNodeType("NodeWithIntPorts"); + + std::string xml = R"( + + + + + + + + + )"; + + // BT::Any output can connect to typed input + ASSERT_NO_THROW(auto tree = factory.createTreeFromText(xml)); +} + +TEST(PortTypeRules, BTAny_InputPort_ReadsAsString) +{ + BehaviorTreeFactory factory; + + // Create a node that reads BT::Any as string + class GetAnyAsString : public SyncActionNode + { + public: + GetAnyAsString(const std::string& name, const NodeConfig& config, std::string* result) + : SyncActionNode(name, config), result_(result) + {} + + NodeStatus tick() override + { + auto res = getInput("input"); + if(res) + { + *result_ = res.value(); + return NodeStatus::SUCCESS; + } + return NodeStatus::FAILURE; + } + + static PortsList providedPorts() + { + return { InputPort("input") }; + } + + private: + std::string* result_; + }; + + std::string result; + factory.registerNodeType("GetAnyAsString", &result); + factory.registerNodeType("NodeWithIntPorts"); + + std::string xml = R"( + + + + + + + + + )"; + + auto tree = factory.createTreeFromText(xml); + auto status = tree.tickWhileRunning(); + + ASSERT_EQ(status, NodeStatus::SUCCESS); + ASSERT_EQ(result, "42"); // 21 * 2 = 42, converted to string +} + +//============================================================================== +// TEST SECTION 8: isStronglyTyped() Behavior +//============================================================================== + +TEST(PortTypeRules, IsStronglyTyped_TypeInfo) +{ + // AnyTypeAllowed is NOT strongly typed + TypeInfo anyType; + ASSERT_FALSE(anyType.isStronglyTyped()); + + // Specific types ARE strongly typed + TypeInfo intType = TypeInfo::Create(); + ASSERT_TRUE(intType.isStronglyTyped()); + + TypeInfo stringType = TypeInfo::Create(); + ASSERT_TRUE(stringType.isStronglyTyped()); + + // BT::Any is NOT strongly typed + TypeInfo btAnyType = TypeInfo::Create(); + ASSERT_FALSE(btAnyType.isStronglyTyped()); +} + +TEST(PortTypeRules, GenericPortDeclaration_DefaultsToAnyTypeAllowed) +{ + // Port<>() without type should be AnyTypeAllowed + auto [name, portInfo] = InputPort<>("test_port"); + + ASSERT_FALSE(portInfo.isStronglyTyped()); + ASSERT_EQ(portInfo.type(), typeid(AnyTypeAllowed)); +} + +//============================================================================== +// TEST SECTION 9: Edge Cases and Complex Scenarios +//============================================================================== + +TEST(PortTypeRules, GenericToTyped_ChainThroughBlackboard) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithGenericPorts"); + factory.registerNodeType("NodeWithIntPorts"); + + // Generic port writes, then two typed ports use it + std::string xml = R"( + + + + + + + + + + )"; + + auto tree = factory.createTreeFromText(xml); + auto status = tree.tickWhileRunning(); + + ASSERT_EQ(status, NodeStatus::SUCCESS); + ASSERT_EQ(tree.rootBlackboard()->get("result"), 40); // 10 * 2 * 2 +} + +TEST(PortTypeRules, MixedTypesWithGenericIntermediate) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithIntPorts"); + factory.registerNodeType("NodeWithGenericPorts"); + + std::string xml = R"( + + + + + + + + + + )"; + + // This tests the pattern: typed -> generic -> typed should work + ASSERT_NO_THROW(auto tree = factory.createTreeFromText(xml)); +} + +TEST(PortTypeRules, StringLiteralValidation_InvalidFormat) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithIntPorts"); + + // "not_a_number" cannot be converted to int + std::string xml = R"( + + + + + + )"; + + EXPECT_THROW(auto tree = factory.createTreeFromText(xml), LogicError); +} + +TEST(PortTypeRules, StringLiteralValidation_ValidFormat) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithIntPorts"); + + // "42" can be converted to int + std::string xml = R"( + + + + + + )"; + + ASSERT_NO_THROW(auto tree = factory.createTreeFromText(xml)); +} + +TEST(PortTypeRules, CustomTypeStringLiteral_ValidFormat) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithTestPointPorts"); + + std::string xml = R"( + + + + + + )"; + + auto tree = factory.createTreeFromText(xml); + auto status = tree.tickWhileRunning(); + + ASSERT_EQ(status, NodeStatus::SUCCESS); +} + +TEST(PortTypeRules, CustomTypeStringLiteral_InvalidFormat) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithTestPointPorts"); + + // Missing second coordinate + std::string xml = R"( + + + + + + )"; + + EXPECT_THROW(auto tree = factory.createTreeFromText(xml), LogicError); +} + +//============================================================================== +// TEST SECTION 10: Reserved Port Names +//============================================================================== + +class IllegalPortNameNode : public SyncActionNode +{ +public: + IllegalPortNameNode(const std::string& name, const NodeConfig& config) + : SyncActionNode(name, config) + {} + + NodeStatus tick() override + { + return NodeStatus::SUCCESS; + } + + static PortsList providedPorts() + { + // "name" is reserved and should not be allowed + return { InputPort("name") }; + } +}; + +TEST(PortTypeRules, ReservedPortName_ThrowsOnRegistration) +{ + BehaviorTreeFactory factory; + + // Should throw because "name" is a reserved port name + EXPECT_THROW(factory.registerNodeType("IllegalPortNameNode"), + RuntimeError); +} diff --git a/tests/gtest_ports.cpp b/tests/gtest_ports.cpp index 2a33178d3..4e8b4a2ae 100644 --- a/tests/gtest_ports.cpp +++ b/tests/gtest_ports.cpp @@ -224,82 +224,6 @@ TEST(PortTest, EmptyPort) ASSERT_EQ(status, NodeStatus::FAILURE); } -class IllegalPorts : public SyncActionNode -{ -public: - IllegalPorts(const std::string& name, const NodeConfig& config) - : SyncActionNode(name, config) - {} - - NodeStatus tick() override - { - return NodeStatus::SUCCESS; - } - - static PortsList providedPorts() - { - return { BT::InputPort("name") }; - } -}; - -TEST(PortTest, IllegalPorts) -{ - BehaviorTreeFactory factory; - ASSERT_ANY_THROW(factory.registerNodeType("nope")); -} - -class ActionVectorDoubleIn : public SyncActionNode -{ -public: - ActionVectorDoubleIn(const std::string& name, const NodeConfig& config, - std::vector* states) - : SyncActionNode(name, config), states_(states) - {} - - NodeStatus tick() override - { - getInput("states", *states_); - return NodeStatus::SUCCESS; - } - - static PortsList providedPorts() - { - return { BT::InputPort>("states") }; - } - -private: - std::vector* states_; -}; - -TEST(PortTest, SubtreeStringInput_Issue489) -{ - std::string xml_txt = R"( - - - - - - - - - )"; - - std::vector states; - - BehaviorTreeFactory factory; - factory.registerNodeType("ActionVectorDoubleIn", &states); - - factory.registerBehaviorTreeFromText(xml_txt); - auto tree = factory.createTree("Main"); - - NodeStatus status = tree.tickWhileRunning(); - - ASSERT_EQ(status, NodeStatus::SUCCESS); - ASSERT_EQ(2, states.size()); - ASSERT_EQ(3, states[0]); - ASSERT_EQ(7, states[1]); -} - class ActionVectorStringIn : public SyncActionNode { public: @@ -447,81 +371,7 @@ TEST(PortTest, DefaultInput) ASSERT_EQ(status, NodeStatus::SUCCESS); } -class GetAny : public SyncActionNode -{ -public: - GetAny(const std::string& name, const NodeConfig& config) : SyncActionNode(name, config) - {} - - NodeStatus tick() override - { - // case 1: the port is Any, but we can cast directly to string - auto res_str = getInput("val_str"); - // case 2: the port is Any, and we retrieve an Any (to be casted later) - auto res_int = getInput("val_int"); - - // case 3: port is double and we get a double - auto res_real_A = getInput("val_real"); - // case 4: port is double and we get an Any - auto res_real_B = getInput("val_real"); - - bool expected = res_str.value() == "hello" && res_int->cast() == 42 && - res_real_A.value() == 3.14 && res_real_B->cast() == 3.14; - - return expected ? NodeStatus::SUCCESS : NodeStatus::FAILURE; - } - - static PortsList providedPorts() - { - return { BT::InputPort("val_str"), BT::InputPort("val_int"), - BT::InputPort("val_real") }; - } -}; - -class SetAny : public SyncActionNode -{ -public: - SetAny(const std::string& name, const NodeConfig& config) : SyncActionNode(name, config) - {} - - NodeStatus tick() override - { - // check that the port can contain different types - setOutput("val_str", BT::Any(1.0)); - setOutput("val_str", BT::Any(1)); - setOutput("val_str", BT::Any("hello")); - - setOutput("val_int", 42); - setOutput("val_real", 3.14); - return NodeStatus::SUCCESS; - } - - static PortsList providedPorts() - { - return { BT::OutputPort("val_str"), BT::OutputPort("val_int"), - BT::OutputPort("val_real") }; - } -}; - -TEST(PortTest, AnyPort) -{ - std::string xml_txt = R"( - - - - - - - - )"; - - BehaviorTreeFactory factory; - factory.registerNodeType("SetAny"); - factory.registerNodeType("GetAny"); - auto tree = factory.createTreeFromText(xml_txt); - auto status = tree.tickOnce(); - ASSERT_EQ(status, NodeStatus::SUCCESS); -} +// NOTE: GetAny, SetAny classes and AnyPort test moved to gtest_port_type_rules.cpp class NodeWithDefaultPoints : public SyncActionNode { From fe02e7942da25b80876de8951a4525696fba95d4 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Mon, 29 Dec 2025 23:41:01 +0100 Subject: [PATCH 050/147] Add Clang tidy and many fixes (#1047) --- .clang-tidy | 48 ++++++ .github/dependabot.yml | 6 + .github/workflows/pre-commit.yaml | 27 +++- .gitignore | 6 +- .pre-commit-config.yaml | 9 ++ CMakeLists.txt | 15 ++ include/behaviortree_cpp/action_node.h | 22 ++- .../actions/pop_from_queue.hpp | 23 +-- include/behaviortree_cpp/actions/sleep_node.h | 7 +- .../behaviortree_cpp/actions/updated_action.h | 5 + include/behaviortree_cpp/basic_types.h | 5 +- include/behaviortree_cpp/blackboard.h | 11 +- include/behaviortree_cpp/bt_factory.h | 2 + include/behaviortree_cpp/condition_node.h | 12 +- include/behaviortree_cpp/control_node.h | 7 +- .../behaviortree_cpp/controls/fallback_node.h | 7 +- .../controls/if_then_else_node.h | 7 +- .../behaviortree_cpp/controls/manual_node.h | 7 +- .../controls/parallel_all_node.h | 5 + .../behaviortree_cpp/controls/parallel_node.h | 5 + .../behaviortree_cpp/controls/sequence_node.h | 7 +- .../controls/sequence_with_memory_node.h | 7 +- .../behaviortree_cpp/controls/switch_node.h | 11 +- .../controls/while_do_else_node.h | 7 +- include/behaviortree_cpp/decorator_node.h | 12 +- .../behaviortree_cpp/decorators/delay_node.h | 5 + .../decorators/inverter_node.h | 7 +- .../behaviortree_cpp/decorators/repeat_node.h | 7 +- .../behaviortree_cpp/decorators/retry_node.h | 14 +- .../decorators/script_precondition.h | 9 +- .../decorators/subtree_node.h | 7 +- .../decorators/timeout_node.h | 5 + .../decorators/updated_decorator.h | 5 + include/behaviortree_cpp/json_export.h | 14 +- include/behaviortree_cpp/leaf_node.h | 8 +- .../loggers/abstract_logger.h | 7 +- .../behaviortree_cpp/loggers/bt_cout_logger.h | 5 + .../loggers/bt_minitrace_logger.h | 7 +- .../behaviortree_cpp/loggers/bt_observer.h | 5 + .../loggers/bt_sqlite_logger.h | 7 +- .../loggers/groot2_protocol.h | 11 +- include/behaviortree_cpp/tree_node.h | 4 +- .../behaviortree_cpp/utils/convert_impl.hpp | 18 ++- .../behaviortree_cpp/utils/demangle_util.h | 10 +- .../utils/locked_reference.hpp | 11 +- include/behaviortree_cpp/utils/safe_any.hpp | 17 +- .../behaviortree_cpp/utils/shared_library.h | 8 +- .../behaviortree_cpp/utils/simple_string.hpp | 5 +- include/behaviortree_cpp/utils/timer_queue.h | 26 +-- include/behaviortree_cpp/utils/wildcards.hpp | 7 +- run_clang_tidy.sh | 77 +++++++++ src/action_node.cpp | 31 ++-- src/actions/sleep_node.cpp | 2 +- src/actions/updated_action.cpp | 2 +- src/basic_types.cpp | 93 ++++++----- src/behavior_tree.cpp | 10 +- src/blackboard.cpp | 28 ++-- src/bt_factory.cpp | 63 ++++---- src/control_node.cpp | 4 +- src/controls/fallback_node.cpp | 4 + src/controls/if_then_else_node.cpp | 15 +- src/controls/manual_node.cpp | 22 ++- src/controls/reactive_fallback.cpp | 4 +- src/controls/reactive_sequence.cpp | 2 +- src/controls/sequence_node.cpp | 4 + src/controls/switch_node.cpp | 6 +- src/controls/while_do_else_node.cpp | 9 +- src/decorator_node.cpp | 8 +- src/decorators/delay_node.cpp | 25 +-- src/decorators/repeat_node.cpp | 4 +- src/decorators/retry_node.cpp | 6 +- src/decorators/subtree_node.cpp | 2 +- src/decorators/timeout_node.cpp | 21 ++- src/decorators/updated_decorator.cpp | 2 +- src/example.cpp | 77 --------- src/json_export.cpp | 3 +- src/loggers/bt_cout_logger.cpp | 5 +- src/loggers/bt_file_logger_v2.cpp | 23 +-- src/loggers/bt_minitrace_logger.cpp | 3 + src/loggers/bt_sqlite_logger.cpp | 32 ++-- src/loggers/groot2_publisher.cpp | 85 +++++----- src/script_parser.cpp | 17 +- src/shared_library.cpp | 7 +- src/shared_library_UNIX.cpp | 22 ++- src/tree_node.cpp | 41 +++-- src/xml_parsing.cpp | 150 ++++++++---------- 86 files changed, 862 insertions(+), 556 deletions(-) create mode 100644 .clang-tidy create mode 100644 .github/dependabot.yml create mode 100755 run_clang_tidy.sh delete mode 100644 src/example.cpp diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 000000000..e34185555 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,48 @@ +Checks: [ + "-*", + "bugprone-*", + "cert-*", + "clang-analyzer-*", + "concurrency-*", + "cppcoreguidelines-*", + "misc-*", + "modernize-*", + "performance-*", + "portability-*", + "readability-*", + "-bugprone-easily-swappable-parameters", + "-bugprone-narrowing-conversions", + "-cert-err58-cpp", + "-cppcoreguidelines-avoid-c-arrays", + "-cppcoreguidelines-avoid-magic-numbers", + "-cppcoreguidelines-avoid-non-const-global-variables", + "-cppcoreguidelines-non-private-member-variables-in-classes", + "-cppcoreguidelines-pro-bounds-array-to-pointer-decay", + "-cppcoreguidelines-pro-bounds-constant-array-index", + "-cppcoreguidelines-pro-bounds-pointer-arithmetic", + "-cppcoreguidelines-pro-type-const-cast", + "-cppcoreguidelines-pro-type-union-access", + "-cppcoreguidelines-pro-type-vararg", + "-misc-no-recursion", + "-misc-non-private-member-variables-in-classes" +] + + +WarningsAsErrors: '-*,bugprone-*,cert-*,clang-analyzer-*,concurrency-*,cppcoreguidelines-*,misc-*,portability-*,readability-implicit-bool-conversion,-concurrency-mt-unsafe,-readability-function-cognitive-complexity' + +CheckOptions: + # ignore macros when computing the cyclomatic complexity. problem caused by RCLCPP LOG macros + - key: readability-function-cognitive-complexity.IgnoreMacros + value: 'true' + + # This change makes it compatible with MISRA:2023 rule 4.14.1 + - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: 'true' + + # Making a copy of a shared_ptr has a non-zero cost, but this cost is small. + # Unfortunately the ROS API (subscriber callbacks) oblige the user to use callbacks functions that will trigger this warning + # This is the reason wht the warning is silenced here + - key: performance-unnecessary-value-param.AllowedTypes + value: 'std::shared_ptr' + + # Reference: https://clang.llvm.org/extra/clang-tidy/checks/readability/identifier-naming.html diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..5ace4600a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index ee7fa9229..5414862c3 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -11,6 +11,29 @@ jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 - uses: pre-commit/action@v3.0.1 + + clang-tidy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install LLVM 21 + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 21 + sudo apt-get install -y clangd-21 clang-tidy-21 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y libzmq3-dev libsqlite3-dev + + - name: Configure CMake + run: cmake -B build -DBUILD_TESTING=OFF + + - name: Run clang-tidy + run: ./run_clang_tidy.sh diff --git a/.gitignore b/.gitignore index 9d5bd4326..0b25bb78b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,9 @@ site/* /.vscode/ .vs/ -# clangd cache +# clangd cache and config (generated by CMake) /.cache/* +/.clangd CMakeSettings.json # OSX junk @@ -18,3 +19,6 @@ CMakeSettings.json CMakeUserPresets.json tags +/clang_tidy_output.log +/.clang-tidy-venv/* +/llvm.sh diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3f81aaaee..3a2b22f2a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,6 +43,15 @@ repos: - id: clang-format args: ['-fallback-style=none', '-i'] + # C++ static analysis (installs clang-tidy automatically via pip) + # - repo: https://github.com/mxmlnrdr/clang_tidy_hook + # rev: v0.3.1 + # hooks: + # - id: clang-tidy + # args: + # - --config-file=.clang-tidy + # - -p=build + # Spell check - repo: https://github.com/codespell-project/codespell rev: v2.4.1 diff --git a/CMakeLists.txt b/CMakeLists.txt index d44131e03..888d56c56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -306,6 +306,21 @@ if(BTCPP_EXAMPLES) add_subdirectory(examples) endif() +###################################################### +# Generate .clangd configuration file for standalone header checking +file(WRITE ${CMAKE_SOURCE_DIR}/.clangd +"CompileFlags: + Add: + - -xc++ + - -std=c++17 + - -I${CMAKE_SOURCE_DIR}/include + - -I${CMAKE_SOURCE_DIR}/3rdparty + - -I${CMAKE_SOURCE_DIR}/3rdparty/minitrace + - -I${CMAKE_SOURCE_DIR}/3rdparty/tinyxml2 + - -I${CMAKE_SOURCE_DIR}/3rdparty/minicoro + - -I${CMAKE_SOURCE_DIR}/3rdparty/lexy/include +") + ###################################################### # INSTALL diff --git a/include/behaviortree_cpp/action_node.h b/include/behaviortree_cpp/action_node.h index 2b720ecc6..3e25957fb 100644 --- a/include/behaviortree_cpp/action_node.h +++ b/include/behaviortree_cpp/action_node.h @@ -38,6 +38,11 @@ class ActionNodeBase : public LeafNode ActionNodeBase(const std::string& name, const NodeConfig& config); ~ActionNodeBase() override = default; + ActionNodeBase(const ActionNodeBase&) = delete; + ActionNodeBase& operator=(const ActionNodeBase&) = delete; + ActionNodeBase(ActionNodeBase&&) = delete; + ActionNodeBase& operator=(ActionNodeBase&&) = delete; + virtual NodeType type() const override final { return NodeType::ACTION; @@ -55,6 +60,11 @@ class SyncActionNode : public ActionNodeBase SyncActionNode(const std::string& name, const NodeConfig& config); ~SyncActionNode() override = default; + SyncActionNode(const SyncActionNode&) = delete; + SyncActionNode& operator=(const SyncActionNode&) = delete; + SyncActionNode(SyncActionNode&&) = delete; + SyncActionNode& operator=(SyncActionNode&&) = delete; + /// throws if the derived class return RUNNING. virtual NodeStatus executeTick() override; @@ -87,6 +97,11 @@ class SimpleActionNode : public SyncActionNode ~SimpleActionNode() override = default; + SimpleActionNode(const SimpleActionNode&) = delete; + SimpleActionNode& operator=(const SimpleActionNode&) = delete; + SimpleActionNode(SimpleActionNode&&) = delete; + SimpleActionNode& operator=(SimpleActionNode&&) = delete; + protected: virtual NodeStatus tick() override final; @@ -197,7 +212,12 @@ class CoroActionNode : public ActionNodeBase { public: CoroActionNode(const std::string& name, const NodeConfig& config); - virtual ~CoroActionNode() override; + ~CoroActionNode() override; + + CoroActionNode(const CoroActionNode&) = delete; + CoroActionNode& operator=(const CoroActionNode&) = delete; + CoroActionNode(CoroActionNode&&) = delete; + CoroActionNode& operator=(CoroActionNode&&) = delete; /// Use this method to return RUNNING and temporary "pause" the Action. void setStatusRunningAndYield(); diff --git a/include/behaviortree_cpp/actions/pop_from_queue.hpp b/include/behaviortree_cpp/actions/pop_from_queue.hpp index 997346ffc..6fced9e97 100644 --- a/include/behaviortree_cpp/actions/pop_from_queue.hpp +++ b/include/behaviortree_cpp/actions/pop_from_queue.hpp @@ -71,18 +71,12 @@ class PopFromQueue : public SyncActionNode { return NodeStatus::FAILURE; } - else - { - T val = items.front(); - items.pop_front(); - setOutput("popped_item", val); - return NodeStatus::SUCCESS; - } - } - else - { - return NodeStatus::FAILURE; + T val = items.front(); + items.pop_front(); + setOutput("popped_item", val); + return NodeStatus::SUCCESS; } + return NodeStatus::FAILURE; } static PortsList providedPorts() @@ -125,11 +119,8 @@ class QueueSize : public SyncActionNode { return NodeStatus::FAILURE; } - else - { - setOutput("size", int(items.size())); - return NodeStatus::SUCCESS; - } + setOutput("size", int(items.size())); + return NodeStatus::SUCCESS; } return NodeStatus::FAILURE; } diff --git a/include/behaviortree_cpp/actions/sleep_node.h b/include/behaviortree_cpp/actions/sleep_node.h index 5a14f3eb5..8737ba6b2 100644 --- a/include/behaviortree_cpp/actions/sleep_node.h +++ b/include/behaviortree_cpp/actions/sleep_node.h @@ -22,6 +22,11 @@ class SleepNode : public StatefulActionNode halt(); } + SleepNode(const SleepNode&) = delete; + SleepNode& operator=(const SleepNode&) = delete; + SleepNode(SleepNode&&) = delete; + SleepNode& operator=(SleepNode&&) = delete; + NodeStatus onStart() override; NodeStatus onRunning() override; @@ -35,7 +40,7 @@ class SleepNode : public StatefulActionNode private: TimerQueue<> timer_; - uint64_t timer_id_; + uint64_t timer_id_ = 0; std::atomic_bool timer_waiting_ = false; std::mutex delay_mutex_; diff --git a/include/behaviortree_cpp/actions/updated_action.h b/include/behaviortree_cpp/actions/updated_action.h index 449d49be7..993c4b5e1 100644 --- a/include/behaviortree_cpp/actions/updated_action.h +++ b/include/behaviortree_cpp/actions/updated_action.h @@ -30,6 +30,11 @@ class EntryUpdatedAction : public SyncActionNode ~EntryUpdatedAction() override = default; + EntryUpdatedAction(const EntryUpdatedAction&) = delete; + EntryUpdatedAction& operator=(const EntryUpdatedAction&) = delete; + EntryUpdatedAction(EntryUpdatedAction&&) = delete; + EntryUpdatedAction& operator=(EntryUpdatedAction&&) = delete; + static PortsList providedPorts() { return { InputPort("entry", "Entry to check") }; diff --git a/include/behaviortree_cpp/basic_types.h b/include/behaviortree_cpp/basic_types.h index b61d49c16..f8547d95e 100644 --- a/include/behaviortree_cpp/basic_types.h +++ b/include/behaviortree_cpp/basic_types.h @@ -418,8 +418,11 @@ class PortInfo : public TypeInfo { default_value_str_ = BT::toStr(default_value); } + // NOLINTNEXTLINE(bugprone-empty-catch) catch(LogicError&) - {} + { + // conversion to string not available for this type, ignore + } } [[nodiscard]] const std::string& description() const; diff --git a/include/behaviortree_cpp/blackboard.h b/include/behaviortree_cpp/blackboard.h index 412360bf8..1287b0d67 100644 --- a/include/behaviortree_cpp/blackboard.h +++ b/include/behaviortree_cpp/blackboard.h @@ -40,6 +40,11 @@ class Blackboard {} public: + Blackboard(const Blackboard&) = delete; + Blackboard& operator=(const Blackboard&) = delete; + Blackboard(Blackboard&&) = delete; + Blackboard& operator=(Blackboard&&) = delete; + struct Entry { Any value; @@ -54,7 +59,11 @@ class Blackboard Entry(const TypeInfo& _info) : info(_info) {} - Entry& operator=(const Entry& other); + ~Entry() = default; + Entry(const Entry&) = delete; + Entry& operator=(const Entry&) = delete; + Entry(Entry&&) = delete; + Entry& operator=(Entry&&) = delete; }; /** Use this static method to create an instance of the BlackBoard diff --git a/include/behaviortree_cpp/bt_factory.h b/include/behaviortree_cpp/bt_factory.h index 4cf7eac6e..1cd4a3b12 100644 --- a/include/behaviortree_cpp/bt_factory.h +++ b/include/behaviortree_cpp/bt_factory.h @@ -78,8 +78,10 @@ inline TreeNodeManifest CreateManifest(const std::string& ID, * See examples in sample_nodes directory. */ +// NOLINTBEGIN(cppcoreguidelines-macro-usage,bugprone-macro-parentheses) #define BT_REGISTER_NODES(factory) \ BTCPP_EXPORT void BT_RegisterNodesFromPlugin(BT::BehaviorTreeFactory& factory) +// NOLINTEND(cppcoreguidelines-macro-usage,bugprone-macro-parentheses) constexpr const char* PLUGIN_SYMBOL = "BT_RegisterNodesFromPlugin"; diff --git a/include/behaviortree_cpp/condition_node.h b/include/behaviortree_cpp/condition_node.h index ffde2e7f8..091905dbe 100644 --- a/include/behaviortree_cpp/condition_node.h +++ b/include/behaviortree_cpp/condition_node.h @@ -23,7 +23,12 @@ class ConditionNode : public LeafNode public: ConditionNode(const std::string& name, const NodeConfig& config); - virtual ~ConditionNode() override = default; + ~ConditionNode() override = default; + + ConditionNode(const ConditionNode&) = delete; + ConditionNode& operator=(const ConditionNode&) = delete; + ConditionNode(ConditionNode&&) = delete; + ConditionNode& operator=(ConditionNode&&) = delete; //Do nothing virtual void halt() override final @@ -58,6 +63,11 @@ class SimpleConditionNode : public ConditionNode ~SimpleConditionNode() override = default; + SimpleConditionNode(const SimpleConditionNode&) = delete; + SimpleConditionNode& operator=(const SimpleConditionNode&) = delete; + SimpleConditionNode(SimpleConditionNode&&) = delete; + SimpleConditionNode& operator=(SimpleConditionNode&&) = delete; + protected: virtual NodeStatus tick() override; diff --git a/include/behaviortree_cpp/control_node.h b/include/behaviortree_cpp/control_node.h index 8a058c3cb..85b4f796c 100644 --- a/include/behaviortree_cpp/control_node.h +++ b/include/behaviortree_cpp/control_node.h @@ -26,7 +26,12 @@ class ControlNode : public TreeNode public: ControlNode(const std::string& name, const NodeConfig& config); - virtual ~ControlNode() override = default; + ~ControlNode() override = default; + + ControlNode(const ControlNode&) = delete; + ControlNode& operator=(const ControlNode&) = delete; + ControlNode(ControlNode&&) = delete; + ControlNode& operator=(ControlNode&&) = delete; /// The method used to add nodes to the children vector void addChild(TreeNode* child); diff --git a/include/behaviortree_cpp/controls/fallback_node.h b/include/behaviortree_cpp/controls/fallback_node.h index d54df4ce1..cf1235730 100644 --- a/include/behaviortree_cpp/controls/fallback_node.h +++ b/include/behaviortree_cpp/controls/fallback_node.h @@ -34,7 +34,12 @@ class FallbackNode : public ControlNode public: FallbackNode(const std::string& name, bool make_asynch = false); - virtual ~FallbackNode() override = default; + ~FallbackNode() override = default; + + FallbackNode(const FallbackNode&) = delete; + FallbackNode& operator=(const FallbackNode&) = delete; + FallbackNode(FallbackNode&&) = delete; + FallbackNode& operator=(FallbackNode&&) = delete; virtual void halt() override; diff --git a/include/behaviortree_cpp/controls/if_then_else_node.h b/include/behaviortree_cpp/controls/if_then_else_node.h index 817e5bc27..560d835c4 100644 --- a/include/behaviortree_cpp/controls/if_then_else_node.h +++ b/include/behaviortree_cpp/controls/if_then_else_node.h @@ -36,7 +36,12 @@ class IfThenElseNode : public ControlNode public: IfThenElseNode(const std::string& name); - virtual ~IfThenElseNode() override = default; + ~IfThenElseNode() override = default; + + IfThenElseNode(const IfThenElseNode&) = delete; + IfThenElseNode& operator=(const IfThenElseNode&) = delete; + IfThenElseNode(IfThenElseNode&&) = delete; + IfThenElseNode& operator=(IfThenElseNode&&) = delete; virtual void halt() override; diff --git a/include/behaviortree_cpp/controls/manual_node.h b/include/behaviortree_cpp/controls/manual_node.h index 97b1619bd..7f977363d 100644 --- a/include/behaviortree_cpp/controls/manual_node.h +++ b/include/behaviortree_cpp/controls/manual_node.h @@ -24,7 +24,12 @@ class ManualSelectorNode : public ControlNode public: ManualSelectorNode(const std::string& name, const NodeConfig& config); - virtual ~ManualSelectorNode() override = default; + ~ManualSelectorNode() override = default; + + ManualSelectorNode(const ManualSelectorNode&) = delete; + ManualSelectorNode& operator=(const ManualSelectorNode&) = delete; + ManualSelectorNode(ManualSelectorNode&&) = delete; + ManualSelectorNode& operator=(ManualSelectorNode&&) = delete; virtual void halt() override; diff --git a/include/behaviortree_cpp/controls/parallel_all_node.h b/include/behaviortree_cpp/controls/parallel_all_node.h index 284b6a1ee..dafeece1c 100644 --- a/include/behaviortree_cpp/controls/parallel_all_node.h +++ b/include/behaviortree_cpp/controls/parallel_all_node.h @@ -45,6 +45,11 @@ class ParallelAllNode : public ControlNode ~ParallelAllNode() override = default; + ParallelAllNode(const ParallelAllNode&) = delete; + ParallelAllNode& operator=(const ParallelAllNode&) = delete; + ParallelAllNode(ParallelAllNode&&) = delete; + ParallelAllNode& operator=(ParallelAllNode&&) = delete; + virtual void halt() override; size_t failureThreshold() const; diff --git a/include/behaviortree_cpp/controls/parallel_node.h b/include/behaviortree_cpp/controls/parallel_node.h index fb36b7344..77e56fc13 100644 --- a/include/behaviortree_cpp/controls/parallel_node.h +++ b/include/behaviortree_cpp/controls/parallel_node.h @@ -56,6 +56,11 @@ class ParallelNode : public ControlNode ~ParallelNode() override = default; + ParallelNode(const ParallelNode&) = delete; + ParallelNode& operator=(const ParallelNode&) = delete; + ParallelNode(ParallelNode&&) = delete; + ParallelNode& operator=(ParallelNode&&) = delete; + virtual void halt() override; size_t successThreshold() const; diff --git a/include/behaviortree_cpp/controls/sequence_node.h b/include/behaviortree_cpp/controls/sequence_node.h index 4a5b6c711..f49190586 100644 --- a/include/behaviortree_cpp/controls/sequence_node.h +++ b/include/behaviortree_cpp/controls/sequence_node.h @@ -37,7 +37,12 @@ class SequenceNode : public ControlNode SequenceNode(const std::string& name, bool make_async = false, const NodeConfiguration& conf = NodeConfiguration()); - virtual ~SequenceNode() override = default; + ~SequenceNode() override = default; + + SequenceNode(const SequenceNode&) = delete; + SequenceNode& operator=(const SequenceNode&) = delete; + SequenceNode(SequenceNode&&) = delete; + SequenceNode& operator=(SequenceNode&&) = delete; virtual void halt() override; diff --git a/include/behaviortree_cpp/controls/sequence_with_memory_node.h b/include/behaviortree_cpp/controls/sequence_with_memory_node.h index ba4aa6970..9c9a0544e 100644 --- a/include/behaviortree_cpp/controls/sequence_with_memory_node.h +++ b/include/behaviortree_cpp/controls/sequence_with_memory_node.h @@ -36,7 +36,12 @@ class SequenceWithMemory : public ControlNode public: SequenceWithMemory(const std::string& name); - virtual ~SequenceWithMemory() override = default; + ~SequenceWithMemory() override = default; + + SequenceWithMemory(const SequenceWithMemory&) = delete; + SequenceWithMemory& operator=(const SequenceWithMemory&) = delete; + SequenceWithMemory(SequenceWithMemory&&) = delete; + SequenceWithMemory& operator=(SequenceWithMemory&&) = delete; virtual void halt() override; diff --git a/include/behaviortree_cpp/controls/switch_node.h b/include/behaviortree_cpp/controls/switch_node.h index 77a08ac70..2056f0b3c 100644 --- a/include/behaviortree_cpp/controls/switch_node.h +++ b/include/behaviortree_cpp/controls/switch_node.h @@ -52,14 +52,19 @@ class SwitchNode : public ControlNode public: SwitchNode(const std::string& name, const BT::NodeConfig& config); - virtual ~SwitchNode() override = default; + ~SwitchNode() override = default; + + SwitchNode(const SwitchNode&) = delete; + SwitchNode& operator=(const SwitchNode&) = delete; + SwitchNode(SwitchNode&&) = delete; + SwitchNode& operator=(SwitchNode&&) = delete; void halt() override; static PortsList providedPorts(); private: - int running_child_; + int running_child_ = -1; std::vector case_keys_; virtual BT::NodeStatus tick() override; }; @@ -70,7 +75,7 @@ class SwitchNode : public ControlNode template inline SwitchNode::SwitchNode(const std::string& name, const NodeConfig& config) - : ControlNode::ControlNode(name, config), running_child_(-1) + : ControlNode::ControlNode(name, config) { setRegistrationID("Switch"); for(unsigned i = 1; i <= NUM_CASES; i++) diff --git a/include/behaviortree_cpp/controls/while_do_else_node.h b/include/behaviortree_cpp/controls/while_do_else_node.h index c5d76b907..844608181 100644 --- a/include/behaviortree_cpp/controls/while_do_else_node.h +++ b/include/behaviortree_cpp/controls/while_do_else_node.h @@ -35,7 +35,12 @@ class WhileDoElseNode : public ControlNode public: WhileDoElseNode(const std::string& name); - virtual ~WhileDoElseNode() override = default; + ~WhileDoElseNode() override = default; + + WhileDoElseNode(const WhileDoElseNode&) = delete; + WhileDoElseNode& operator=(const WhileDoElseNode&) = delete; + WhileDoElseNode(WhileDoElseNode&&) = delete; + WhileDoElseNode& operator=(WhileDoElseNode&&) = delete; virtual void halt() override; diff --git a/include/behaviortree_cpp/decorator_node.h b/include/behaviortree_cpp/decorator_node.h index 4d186c593..018c4c332 100644 --- a/include/behaviortree_cpp/decorator_node.h +++ b/include/behaviortree_cpp/decorator_node.h @@ -13,7 +13,12 @@ class DecoratorNode : public TreeNode public: DecoratorNode(const std::string& name, const NodeConfig& config); - virtual ~DecoratorNode() override = default; + ~DecoratorNode() override = default; + + DecoratorNode(const DecoratorNode&) = delete; + DecoratorNode& operator=(const DecoratorNode&) = delete; + DecoratorNode(DecoratorNode&&) = delete; + DecoratorNode& operator=(DecoratorNode&&) = delete; void setChild(TreeNode* child); @@ -60,6 +65,11 @@ class SimpleDecoratorNode : public DecoratorNode ~SimpleDecoratorNode() override = default; + SimpleDecoratorNode(const SimpleDecoratorNode&) = delete; + SimpleDecoratorNode& operator=(const SimpleDecoratorNode&) = delete; + SimpleDecoratorNode(SimpleDecoratorNode&&) = delete; + SimpleDecoratorNode& operator=(SimpleDecoratorNode&&) = delete; + protected: virtual NodeStatus tick() override; diff --git a/include/behaviortree_cpp/decorators/delay_node.h b/include/behaviortree_cpp/decorators/delay_node.h index 10678e9e3..02dd01a44 100644 --- a/include/behaviortree_cpp/decorators/delay_node.h +++ b/include/behaviortree_cpp/decorators/delay_node.h @@ -43,6 +43,11 @@ class DelayNode : public DecoratorNode halt(); } + DelayNode(const DelayNode&) = delete; + DelayNode& operator=(const DelayNode&) = delete; + DelayNode(DelayNode&&) = delete; + DelayNode& operator=(DelayNode&&) = delete; + static PortsList providedPorts() { return { InputPort("delay_msec", "Tick the child after a few " diff --git a/include/behaviortree_cpp/decorators/inverter_node.h b/include/behaviortree_cpp/decorators/inverter_node.h index 244844404..0565f6d28 100644 --- a/include/behaviortree_cpp/decorators/inverter_node.h +++ b/include/behaviortree_cpp/decorators/inverter_node.h @@ -27,7 +27,12 @@ class InverterNode : public DecoratorNode public: InverterNode(const std::string& name); - virtual ~InverterNode() override = default; + ~InverterNode() override = default; + + InverterNode(const InverterNode&) = delete; + InverterNode& operator=(const InverterNode&) = delete; + InverterNode(InverterNode&&) = delete; + InverterNode& operator=(InverterNode&&) = delete; private: virtual BT::NodeStatus tick() override; diff --git a/include/behaviortree_cpp/decorators/repeat_node.h b/include/behaviortree_cpp/decorators/repeat_node.h index 13f79f105..6581a268f 100644 --- a/include/behaviortree_cpp/decorators/repeat_node.h +++ b/include/behaviortree_cpp/decorators/repeat_node.h @@ -39,7 +39,12 @@ class RepeatNode : public DecoratorNode RepeatNode(const std::string& name, const NodeConfig& config); - virtual ~RepeatNode() override = default; + ~RepeatNode() override = default; + + RepeatNode(const RepeatNode&) = delete; + RepeatNode& operator=(const RepeatNode&) = delete; + RepeatNode(RepeatNode&&) = delete; + RepeatNode& operator=(RepeatNode&&) = delete; static PortsList providedPorts() { diff --git a/include/behaviortree_cpp/decorators/retry_node.h b/include/behaviortree_cpp/decorators/retry_node.h index 81c3906d5..acfdd7dca 100644 --- a/include/behaviortree_cpp/decorators/retry_node.h +++ b/include/behaviortree_cpp/decorators/retry_node.h @@ -43,7 +43,12 @@ class RetryNode : public DecoratorNode RetryNode(const std::string& name, const NodeConfig& config); - virtual ~RetryNode() override = default; + ~RetryNode() override = default; + + RetryNode(const RetryNode&) = delete; + RetryNode& operator=(const RetryNode&) = delete; + RetryNode(RetryNode&&) = delete; + RetryNode& operator=(RetryNode&&) = delete; static PortsList providedPorts() { @@ -73,7 +78,12 @@ class [[deprecated("RetryUntilSuccesful was a typo and deprecated, use " RetryNodeTypo(const std::string& name, const NodeConfig& config) : RetryNode(name, config){}; - virtual ~RetryNodeTypo() override = default; + ~RetryNodeTypo() override = default; + + RetryNodeTypo(const RetryNodeTypo&) = delete; + RetryNodeTypo& operator=(const RetryNodeTypo&) = delete; + RetryNodeTypo(RetryNodeTypo&&) = delete; + RetryNodeTypo& operator=(RetryNodeTypo&&) = delete; }; } // namespace BT diff --git a/include/behaviortree_cpp/decorators/script_precondition.h b/include/behaviortree_cpp/decorators/script_precondition.h index 83f1b57d9..b9a5d55ba 100644 --- a/include/behaviortree_cpp/decorators/script_precondition.h +++ b/include/behaviortree_cpp/decorators/script_precondition.h @@ -27,7 +27,12 @@ class PreconditionNode : public DecoratorNode loadExecutor(); } - virtual ~PreconditionNode() override = default; + ~PreconditionNode() override = default; + + PreconditionNode(const PreconditionNode&) = delete; + PreconditionNode& operator=(const PreconditionNode&) = delete; + PreconditionNode(PreconditionNode&&) = delete; + PreconditionNode& operator=(PreconditionNode&&) = delete; static PortsList providedPorts() { @@ -42,7 +47,7 @@ class PreconditionNode : public DecoratorNode { loadExecutor(); - BT::NodeStatus else_return; + BT::NodeStatus else_return = NodeStatus::FAILURE; if(!getInput("else", else_return)) { throw RuntimeError("Missing parameter [else] in Precondition"); diff --git a/include/behaviortree_cpp/decorators/subtree_node.h b/include/behaviortree_cpp/decorators/subtree_node.h index 4e2287947..07a5ef1d4 100644 --- a/include/behaviortree_cpp/decorators/subtree_node.h +++ b/include/behaviortree_cpp/decorators/subtree_node.h @@ -54,7 +54,12 @@ class SubTreeNode : public DecoratorNode public: SubTreeNode(const std::string& name, const NodeConfig& config); - virtual ~SubTreeNode() override = default; + ~SubTreeNode() override = default; + + SubTreeNode(const SubTreeNode&) = delete; + SubTreeNode& operator=(const SubTreeNode&) = delete; + SubTreeNode(SubTreeNode&&) = delete; + SubTreeNode& operator=(SubTreeNode&&) = delete; static PortsList providedPorts(); diff --git a/include/behaviortree_cpp/decorators/timeout_node.h b/include/behaviortree_cpp/decorators/timeout_node.h index b821edd7f..fd895e96b 100644 --- a/include/behaviortree_cpp/decorators/timeout_node.h +++ b/include/behaviortree_cpp/decorators/timeout_node.h @@ -60,6 +60,11 @@ class TimeoutNode : public DecoratorNode timer_.cancelAll(); } + TimeoutNode(const TimeoutNode&) = delete; + TimeoutNode& operator=(const TimeoutNode&) = delete; + TimeoutNode(TimeoutNode&&) = delete; + TimeoutNode& operator=(TimeoutNode&&) = delete; + static PortsList providedPorts() { return { InputPort("msec", "After a certain amount of time, " diff --git a/include/behaviortree_cpp/decorators/updated_decorator.h b/include/behaviortree_cpp/decorators/updated_decorator.h index 4a707b555..39b7f10d7 100644 --- a/include/behaviortree_cpp/decorators/updated_decorator.h +++ b/include/behaviortree_cpp/decorators/updated_decorator.h @@ -31,6 +31,11 @@ class EntryUpdatedDecorator : public DecoratorNode ~EntryUpdatedDecorator() override = default; + EntryUpdatedDecorator(const EntryUpdatedDecorator&) = delete; + EntryUpdatedDecorator& operator=(const EntryUpdatedDecorator&) = delete; + EntryUpdatedDecorator(EntryUpdatedDecorator&&) = delete; + EntryUpdatedDecorator& operator=(EntryUpdatedDecorator&&) = delete; + static PortsList providedPorts() { return { InputPort("entry", "Entry to check") }; diff --git a/include/behaviortree_cpp/json_export.h b/include/behaviortree_cpp/json_export.h index dfe9b0fee..fcfc78b47 100644 --- a/include/behaviortree_cpp/json_export.h +++ b/include/behaviortree_cpp/json_export.h @@ -51,10 +51,17 @@ class JsonExporter public: static JsonExporter& get(); - // Delete copy constructors as can only be this one global instance. + ~JsonExporter() = default; + + JsonExporter(const JsonExporter&) = delete; + JsonExporter& operator=(const JsonExporter&) = delete; + JsonExporter(JsonExporter&&) = delete; JsonExporter& operator=(JsonExporter&&) = delete; - JsonExporter& operator=(JsonExporter&) = delete; +private: + JsonExporter() = default; + +public: /** * @brief toJson adds the content of "any" to the JSON "destination". * @@ -246,7 +253,7 @@ inline void RegisterJsonDefinition() //------------------------------------------------ // Macro to implement to_json() and from_json() - +// NOLINTBEGIN(bugprone-macro-parentheses) #define BT_JSON_CONVERTER(Type, value) \ template \ void _JsonTypeDefinition(Type&, AddField&); \ @@ -266,5 +273,6 @@ inline void RegisterJsonDefinition() \ template \ inline void _JsonTypeDefinition(Type& value, AddField& add_field) +// NOLINTEND(bugprone-macro-parentheses) //end of file diff --git a/include/behaviortree_cpp/leaf_node.h b/include/behaviortree_cpp/leaf_node.h index 8ad948ae6..eac254be3 100644 --- a/include/behaviortree_cpp/leaf_node.h +++ b/include/behaviortree_cpp/leaf_node.h @@ -20,12 +20,16 @@ namespace BT { class LeafNode : public TreeNode { -protected: public: LeafNode(const std::string& name, const NodeConfig& config) : TreeNode(name, config) {} - virtual ~LeafNode() override = default; + ~LeafNode() override = default; + + LeafNode(const LeafNode&) = delete; + LeafNode& operator=(const LeafNode&) = delete; + LeafNode(LeafNode&&) = delete; + LeafNode& operator=(LeafNode&&) = delete; }; } // namespace BT diff --git a/include/behaviortree_cpp/loggers/abstract_logger.h b/include/behaviortree_cpp/loggers/abstract_logger.h index be994944c..1dee2321b 100644 --- a/include/behaviortree_cpp/loggers/abstract_logger.h +++ b/include/behaviortree_cpp/loggers/abstract_logger.h @@ -56,10 +56,10 @@ class StatusChangeLogger } private: - bool enabled_; - bool show_transition_to_idle_; + bool enabled_ = true; + bool show_transition_to_idle_ = true; std::vector subscribers_; - TimestampType type_; + TimestampType type_ = TimestampType::absolute; BT::TimePoint first_timestamp_ = {}; std::mutex callback_mutex_; }; @@ -67,7 +67,6 @@ class StatusChangeLogger //-------------------------------------------- inline StatusChangeLogger::StatusChangeLogger(TreeNode* root_node) - : enabled_(true), show_transition_to_idle_(true), type_(TimestampType::absolute) { first_timestamp_ = std::chrono::high_resolution_clock::now(); diff --git a/include/behaviortree_cpp/loggers/bt_cout_logger.h b/include/behaviortree_cpp/loggers/bt_cout_logger.h index 0d7cf4700..1ffe4cb6b 100644 --- a/include/behaviortree_cpp/loggers/bt_cout_logger.h +++ b/include/behaviortree_cpp/loggers/bt_cout_logger.h @@ -17,6 +17,11 @@ class StdCoutLogger : public StatusChangeLogger StdCoutLogger(const BT::Tree& tree); ~StdCoutLogger() override; + StdCoutLogger(const StdCoutLogger&) = delete; + StdCoutLogger& operator=(const StdCoutLogger&) = delete; + StdCoutLogger(StdCoutLogger&&) = delete; + StdCoutLogger& operator=(StdCoutLogger&&) = delete; + virtual void flush() override; private: diff --git a/include/behaviortree_cpp/loggers/bt_minitrace_logger.h b/include/behaviortree_cpp/loggers/bt_minitrace_logger.h index 38efa9140..185b55f01 100644 --- a/include/behaviortree_cpp/loggers/bt_minitrace_logger.h +++ b/include/behaviortree_cpp/loggers/bt_minitrace_logger.h @@ -9,7 +9,12 @@ class MinitraceLogger : public StatusChangeLogger public: MinitraceLogger(const BT::Tree& tree, const char* filename_json); - virtual ~MinitraceLogger() override; + ~MinitraceLogger() override; + + MinitraceLogger(const MinitraceLogger&) = delete; + MinitraceLogger& operator=(const MinitraceLogger&) = delete; + MinitraceLogger(MinitraceLogger&&) = delete; + MinitraceLogger& operator=(MinitraceLogger&&) = delete; virtual void callback(Duration timestamp, const TreeNode& node, NodeStatus prev_status, NodeStatus status) override; diff --git a/include/behaviortree_cpp/loggers/bt_observer.h b/include/behaviortree_cpp/loggers/bt_observer.h index e863270d2..943691d90 100644 --- a/include/behaviortree_cpp/loggers/bt_observer.h +++ b/include/behaviortree_cpp/loggers/bt_observer.h @@ -20,6 +20,11 @@ class TreeObserver : public StatusChangeLogger TreeObserver(const BT::Tree& tree); ~TreeObserver() override; + TreeObserver(const TreeObserver&) = delete; + TreeObserver& operator=(const TreeObserver&) = delete; + TreeObserver(TreeObserver&&) = delete; + TreeObserver& operator=(TreeObserver&&) = delete; + virtual void flush() override {} diff --git a/include/behaviortree_cpp/loggers/bt_sqlite_logger.h b/include/behaviortree_cpp/loggers/bt_sqlite_logger.h index 82f625ded..d85ec8ffb 100644 --- a/include/behaviortree_cpp/loggers/bt_sqlite_logger.h +++ b/include/behaviortree_cpp/loggers/bt_sqlite_logger.h @@ -55,7 +55,12 @@ class SqliteLogger : public StatusChangeLogger */ SqliteLogger(const Tree& tree, std::filesystem::path const& file, bool append = false); - virtual ~SqliteLogger() override; + ~SqliteLogger() override; + + SqliteLogger(const SqliteLogger&) = delete; + SqliteLogger& operator=(const SqliteLogger&) = delete; + SqliteLogger(SqliteLogger&&) = delete; + SqliteLogger& operator=(SqliteLogger&&) = delete; // You can inject a function that add a string to the Transitions table, // in the column "extra_data". diff --git a/include/behaviortree_cpp/loggers/groot2_protocol.h b/include/behaviortree_cpp/loggers/groot2_protocol.h index 5c27b5cb5..845fd870b 100644 --- a/include/behaviortree_cpp/loggers/groot2_protocol.h +++ b/include/behaviortree_cpp/loggers/groot2_protocol.h @@ -132,17 +132,12 @@ struct RequestHeader struct ReplyHeader { RequestHeader request; - TreeUniqueUUID tree_id; + TreeUniqueUUID tree_id = {}; static size_t size() { return RequestHeader::size() + 16; } - - ReplyHeader() - { - tree_id.fill(0); - } }; template @@ -155,7 +150,7 @@ inline unsigned Serialize(char* buffer, unsigned offset, T value) template inline unsigned Deserialize(const char* buffer, unsigned offset, T& value) { - memcpy(reinterpret_cast(&value), buffer + offset, sizeof(T)); + memcpy(&value, buffer + offset, sizeof(T)); return sizeof(T); } @@ -186,7 +181,7 @@ inline RequestHeader DeserializeRequestHeader(const std::string& buffer) RequestHeader header; unsigned offset = 0; offset += Deserialize(buffer.data(), offset, header.protocol); - uint8_t type; + uint8_t type = 0; offset += Deserialize(buffer.data(), offset, type); header.type = static_cast(type); offset += Deserialize(buffer.data(), offset, header.unique_id); diff --git a/include/behaviortree_cpp/tree_node.h b/include/behaviortree_cpp/tree_node.h index ec7b379e9..890257f37 100644 --- a/include/behaviortree_cpp/tree_node.h +++ b/include/behaviortree_cpp/tree_node.h @@ -370,9 +370,9 @@ class TreeNode } else if constexpr(hasNodeNameCtor()) { - auto node_ptr = new DerivedT(name, args...); + auto node_ptr = std::make_unique(name, args...); node_ptr->config() = config; - return std::unique_ptr(node_ptr); + return node_ptr; } } diff --git a/include/behaviortree_cpp/utils/convert_impl.hpp b/include/behaviortree_cpp/utils/convert_impl.hpp index 64baef967..690c1970e 100644 --- a/include/behaviortree_cpp/utils/convert_impl.hpp +++ b/include/behaviortree_cpp/utils/convert_impl.hpp @@ -154,12 +154,19 @@ void convertNumber(const SRC& source, DST& target) throw std::runtime_error("Value is negative and can't be converted to unsigned"); } } - // these conversions are always safe: + // these conversions are always safe (no check needed): // - same type // - float -> double - if constexpr(is_same() || (is_same() && is_same())) + // - floating point to bool (C-style: any non-zero is true) + if constexpr(is_same() || (is_same() && is_same()) || + (std::is_floating_point::value && is_same())) { - // No check needed + target = static_cast(source); + } + // integer to bool: only 0 and 1 are valid + else if constexpr(is_integer() && is_same()) + { + checkLowerLimit(source); target = static_cast(source); } else if constexpr(both_integers) @@ -179,11 +186,6 @@ void convertNumber(const SRC& source, DST& target) } target = static_cast(source); } - // special case: bool accept truncation - else if constexpr(is_convertible_to_bool() && is_same()) - { - target = static_cast(source); - } // casting to/from floating points might cause truncation. else if constexpr(std::is_floating_point::value || std::is_floating_point::value) diff --git a/include/behaviortree_cpp/utils/demangle_util.h b/include/behaviortree_cpp/utils/demangle_util.h index 7f741e876..6c7b27af4 100644 --- a/include/behaviortree_cpp/utils/demangle_util.h +++ b/include/behaviortree_cpp/utils/demangle_util.h @@ -45,6 +45,8 @@ class scoped_demangled_name scoped_demangled_name(scoped_demangled_name const&) = delete; scoped_demangled_name& operator=(scoped_demangled_name const&) = delete; + scoped_demangled_name(scoped_demangled_name&&) = delete; + scoped_demangled_name& operator=(scoped_demangled_name&&) = delete; }; #if defined(HAS_CXXABI_H) @@ -58,6 +60,7 @@ inline char const* demangle_alloc(char const* name) noexcept inline void demangle_free(char const* name) noexcept { + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc,cppcoreguidelines-owning-memory) std::free(const_cast(name)); } @@ -103,14 +106,11 @@ inline std::string demangle(const std::type_index& index) scoped_demangled_name demangled_name(index.name()); char const* const p = demangled_name.get(); - if(p) + if(p != nullptr) { return p; } - else - { - return index.name(); - } + return index.name(); } inline std::string demangle(const std::type_info& info) diff --git a/include/behaviortree_cpp/utils/locked_reference.hpp b/include/behaviortree_cpp/utils/locked_reference.hpp index f65bd68c9..43131e7d4 100644 --- a/include/behaviortree_cpp/utils/locked_reference.hpp +++ b/include/behaviortree_cpp/utils/locked_reference.hpp @@ -25,7 +25,7 @@ class LockedPtr ~LockedPtr() { - if(mutex_) + if(mutex_ != nullptr) { mutex_->unlock(); } @@ -34,16 +34,17 @@ class LockedPtr LockedPtr(LockedPtr const&) = delete; LockedPtr& operator=(LockedPtr const&) = delete; - LockedPtr(LockedPtr&& other) + LockedPtr(LockedPtr&& other) noexcept { std::swap(ref_, other.ref_); std::swap(mutex_, other.mutex_); } - LockedPtr& operator=(LockedPtr&& other) + LockedPtr& operator=(LockedPtr&& other) noexcept { std::swap(ref_, other.ref_); std::swap(mutex_, other.mutex_); + return *this; } operator bool() const @@ -53,7 +54,7 @@ class LockedPtr void lock() { - if(mutex_) + if(mutex_ != nullptr) { mutex_->lock(); } @@ -61,7 +62,7 @@ class LockedPtr void unlock() { - if(mutex_) + if(mutex_ != nullptr) { mutex_->unlock(); } diff --git a/include/behaviortree_cpp/utils/safe_any.hpp b/include/behaviortree_cpp/utils/safe_any.hpp index 4dc1c8718..086ec1825 100644 --- a/include/behaviortree_cpp/utils/safe_any.hpp +++ b/include/behaviortree_cpp/utils/safe_any.hpp @@ -70,7 +70,8 @@ class Any Any(const Any& other) : _any(other._any), _original_type(other._original_type) {} - Any(Any&& other) : _any(std::move(other._any)), _original_type(other._original_type) + Any(Any&& other) noexcept + : _any(std::move(other._any)), _original_type(other._original_type) {} explicit Any(const double& value) : _any(value), _original_type(typeid(double)) @@ -117,6 +118,8 @@ class Any Any& operator=(const Any& other); + Any& operator=(Any&& other) noexcept; + [[nodiscard]] bool isNumber() const; [[nodiscard]] bool isIntegral() const; @@ -305,7 +308,17 @@ inline bool isCastingSafe(const std::type_index& type, const T& val) inline Any& Any::operator=(const Any& other) { - this->_any = other._any; + if(this != &other) + { + this->_any = other._any; + this->_original_type = other._original_type; + } + return *this; +} + +inline Any& Any::operator=(Any&& other) noexcept +{ + this->_any = std::move(other._any); this->_original_type = other._original_type; return *this; } diff --git a/include/behaviortree_cpp/utils/shared_library.h b/include/behaviortree_cpp/utils/shared_library.h index 778c23164..2708ba49e 100644 --- a/include/behaviortree_cpp/utils/shared_library.h +++ b/include/behaviortree_cpp/utils/shared_library.h @@ -127,10 +127,12 @@ class SharedLibrary /// with prefix() and suffix() static std::string getOSName(const std::string& name); -private: - SharedLibrary(const SharedLibrary&); - SharedLibrary& operator=(const SharedLibrary&); + SharedLibrary(const SharedLibrary&) = delete; + SharedLibrary& operator=(const SharedLibrary&) = delete; + SharedLibrary(SharedLibrary&&) = delete; + SharedLibrary& operator=(SharedLibrary&&) = delete; +private: void* findSymbol(const std::string& name); std::string _path; diff --git a/include/behaviortree_cpp/utils/simple_string.hpp b/include/behaviortree_cpp/utils/simple_string.hpp index 1c6b49809..f12724ca6 100644 --- a/include/behaviortree_cpp/utils/simple_string.hpp +++ b/include/behaviortree_cpp/utils/simple_string.hpp @@ -52,9 +52,6 @@ class SimpleString { if(this != &other) { - this->~SimpleString(); - // Ensure clean state before swap - _storage = {}; std::swap(_storage, other._storage); } return *this; @@ -194,7 +191,7 @@ class SimpleString { _storage.str.size = size; _storage.soo.capacity_left = IS_LONG_BIT; - _storage.str.data = new char[size + 1]; + _storage.str.data = new char[size + 1]; // NOLINT(cppcoreguidelines-owning-memory) std::memcpy(_storage.str.data, input_data, size); _storage.str.data[size] = '\0'; } diff --git a/include/behaviortree_cpp/utils/timer_queue.h b/include/behaviortree_cpp/utils/timer_queue.h index 2a68ba872..d1a841c5a 100644 --- a/include/behaviortree_cpp/utils/timer_queue.h +++ b/include/behaviortree_cpp/utils/timer_queue.h @@ -71,8 +71,8 @@ class Semaphore // - Handlers are ALWAYS executed in the Timer Queue worker thread. // - Handlers execution order is NOT guaranteed // -template +template class TimerQueue { public: @@ -99,7 +99,7 @@ class TimerQueue uint64_t add(std::chrono::milliseconds milliseconds, std::function handler) { WorkItem item; - item.end = _Clock::now() + milliseconds; + item.end = ClockT::now() + milliseconds; item.handler = std::move(handler); std::unique_lock lk(m_mtx); @@ -132,7 +132,7 @@ class TimerQueue { WorkItem newItem; // Zero time, so it stays at the top for immediate execution - newItem.end = std::chrono::time_point<_Clock, _Duration>(); + newItem.end = std::chrono::time_point(); newItem.id = 0; // Means it is a canceled item // Move the handler from item to newItem. // Also, we need to manually set the handler to nullptr, since @@ -164,7 +164,7 @@ class TimerQueue { if(item.id) { - item.end = std::chrono::time_point<_Clock, _Duration>(); + item.end = std::chrono::time_point(); item.id = 0; } } @@ -175,10 +175,12 @@ class TimerQueue return ret; } -private: TimerQueue(const TimerQueue&) = delete; TimerQueue& operator=(const TimerQueue&) = delete; + TimerQueue(TimerQueue&&) = delete; + TimerQueue& operator=(TimerQueue&&) = delete; +private: void run() { while(!m_finish.load()) @@ -193,7 +195,7 @@ class TimerQueue else { // No timers exist, so wait an arbitrary amount of time - m_checkWork.waitUntil(_Clock::now() + std::chrono::milliseconds(10)); + m_checkWork.waitUntil(ClockT::now() + std::chrono::milliseconds(10)); } // Check and execute as much work as possible, such as, all expired @@ -206,7 +208,7 @@ class TimerQueue assert(m_items.size() == 0); } - std::pair> calcWaitTime() + std::pair> calcWaitTime() { std::lock_guard lk(m_mtx); while(m_items.size()) @@ -225,13 +227,13 @@ class TimerQueue // No items found, so return no wait time (causes the thread to wait // indefinitely) - return std::make_pair(false, std::chrono::time_point<_Clock, _Duration>()); + return std::make_pair(false, std::chrono::time_point()); } void checkWork() { std::unique_lock lk(m_mtx); - while(m_items.size() && m_items.top().end <= _Clock::now()) + while(m_items.size() && m_items.top().end <= ClockT::now()) { WorkItem item(std::move(m_items.top())); m_items.pop(); @@ -252,8 +254,8 @@ class TimerQueue struct WorkItem { - std::chrono::time_point<_Clock, _Duration> end; - uint64_t id; // id==0 means it was cancelled + std::chrono::time_point end; + uint64_t id = 0; // id==0 means it was cancelled std::function handler; bool operator>(const WorkItem& other) const { diff --git a/include/behaviortree_cpp/utils/wildcards.hpp b/include/behaviortree_cpp/utils/wildcards.hpp index 68e2029f4..a46e697e0 100644 --- a/include/behaviortree_cpp/utils/wildcards.hpp +++ b/include/behaviortree_cpp/utils/wildcards.hpp @@ -1,5 +1,10 @@ #pragma once +#include +#include +#include +#include + /** * @file wildcards.hpp * @brief Simple wildcard matching function supporting '*' and '?'. @@ -35,7 +40,7 @@ inline bool wildcards_match(std::string_view str, std::string_view pattern) return cached == 1; } - bool result; + bool result = false; if(pattern[j] == '*') { result = match_ref(match_ref, i, j + 1); diff --git a/run_clang_tidy.sh b/run_clang_tidy.sh new file mode 100755 index 000000000..183825f8b --- /dev/null +++ b/run_clang_tidy.sh @@ -0,0 +1,77 @@ +#!/bin/bash -eu + +script_dir=${0%/*} +ws_dir=$(realpath "$script_dir") + +# Check if clangd-21 is available +if ! command -v clangd-21 &> /dev/null; then + echo "Error: clangd-21 is not installed or not in PATH." + echo "" + echo "To install clangd-21 on Ubuntu/Debian, visit:" + echo " https://apt.llvm.org/" + echo "" + echo "Quick install instructions:" + echo " wget https://apt.llvm.org/llvm.sh" + echo " chmod +x llvm.sh" + echo " sudo ./llvm.sh 21" + echo " sudo apt install clangd-21 clang-tidy-21" + exit 1 +fi + +# Display help message if --help is passed as an argument +if [[ "${1:-}" == "--help" ]]; then + echo "Usage: $(basename "$0") [source_path] [build_path]" + echo "Run clang-tidy on the specified paths." + echo + echo "Arguments:" + echo " build_path Path to build directory containing compile_commands.json (default: build)" + exit 0 +fi + + +clang_tidy_paths="$ws_dir/src $ws_dir/include" +cmake_build_path="$ws_dir/${1:-build}" + +skip_list=( + "$ws_dir/3rdparty" + "$ws_dir/include/behaviortree_cpp/contrib" + "$ws_dir/include/behaviortree_cpp/scripting" + "$ws_dir/include/behaviortree_cpp/flatbuffers" +) + +skip_paths=() +for path in "${skip_list[@]}"; do + skip_paths+=(-not -path "$path/*") +done + +## check that the file compile_commands.json exists +if [ ! -f "$cmake_build_path/compile_commands.json" ]; then + echo "Error: compile_commands.json not found in $cmake_build_path" + echo "Please build the project first with CMake to generate compile_commands.json" + exit 1 +fi + +echo "-----------------------------------------------------------" +echo "Running clang-tidy on $clang_tidy_paths" +echo " Skipping paths:" +for path in "${skip_list[@]}"; do + echo " $path" +done +echo "-----------------------------------------------------------" + +find "$ws_dir/src" "$ws_dir/include" \( -name '*.cpp' -o -name '*.hpp' -o -name '*.h' \) -not -name '*_WIN.cpp' "${skip_paths[@]}" -print0 \ + | xargs -0 -n 1 -P $(nproc) bash -c ' + set -o pipefail + echo "$@" + cd "'"$ws_dir"'" && clangd-21 \ + --log=error \ + --clang-tidy \ + --compile-commands-dir="'"$cmake_build_path"'" \ + --check-locations=false \ + --check="$@" \ + 2>&1 | sed "s/^/${1//\//\\/}: /" + ' _ + +echo "-----------------------------------------------------------" +echo "Clang-tidy complete." +echo "-----------------------------------------------------------" diff --git a/src/action_node.cpp b/src/action_node.cpp index 9493a2af9..c25673ee3 100644 --- a/src/action_node.cpp +++ b/src/action_node.cpp @@ -14,6 +14,7 @@ #define MINICORO_IMPL #include "minicoro.h" #include "behaviortree_cpp/action_node.h" +#include using namespace BT; @@ -39,7 +40,7 @@ NodeStatus SimpleActionNode::tick() prev_status = NodeStatus::RUNNING; } - NodeStatus status = tick_functor_(*this); + const NodeStatus status = tick_functor_(*this); if(status != prev_status) { setStatus(status); @@ -68,13 +69,16 @@ NodeStatus SyncActionNode::executeTick() struct CoroActionNode::Pimpl { mco_coro* coro = nullptr; - mco_desc desc; + mco_desc desc = {}; }; +namespace +{ void CoroEntry(mco_coro* co) { static_cast(co->user_data)->tickImpl(); } +} // namespace CoroActionNode::CoroActionNode(const std::string& name, const NodeConfig& config) : ActionNodeBase(name, config), _p(new Pimpl) @@ -82,7 +86,14 @@ CoroActionNode::CoroActionNode(const std::string& name, const NodeConfig& config CoroActionNode::~CoroActionNode() { - destroyCoroutine(); + try + { + destroyCoroutine(); + } + catch(const std::exception& ex) + { + std::cerr << "Exception in ~CoroActionNode(): " << ex.what() << std::endl; + } } void CoroActionNode::setStatusRunningAndYield() @@ -100,7 +111,7 @@ NodeStatus CoroActionNode::executeTick() _p->desc = mco_desc_init(CoroEntry, 0); _p->desc.user_data = this; - mco_result res = mco_create(&_p->coro, &_p->desc); + const mco_result res = mco_create(&_p->coro, &_p->desc); if(res != MCO_SUCCESS) { throw RuntimeError("Can't create coroutine"); @@ -134,9 +145,9 @@ void CoroActionNode::halt() void CoroActionNode::destroyCoroutine() { - if(_p->coro) + if(_p->coro != nullptr) { - mco_result res = mco_destroy(_p->coro); + const mco_result res = mco_destroy(_p->coro); if(res != MCO_SUCCESS) { throw RuntimeError("Can't destroy coroutine"); @@ -156,7 +167,7 @@ NodeStatus StatefulActionNode::tick() if(prev_status == NodeStatus::IDLE) { - NodeStatus new_status = onStart(); + const NodeStatus new_status = onStart(); if(new_status == NodeStatus::IDLE) { throw LogicError("StatefulActionNode::onStart() must not return IDLE"); @@ -166,7 +177,7 @@ NodeStatus StatefulActionNode::tick() //------------------------------------------ if(prev_status == NodeStatus::RUNNING) { - NodeStatus new_status = onRunning(); + const NodeStatus new_status = onRunning(); if(new_status == NodeStatus::IDLE) { throw LogicError("StatefulActionNode::onRunning() must not return IDLE"); @@ -210,7 +221,7 @@ NodeStatus BT::ThreadedAction::executeTick() << name() << "]\n" << std::endl; // Set the exception pointer and the status atomically. - lock_type l(mutex_); + const lock_type l(mutex_); exptr_ = std::current_exception(); setStatus(BT::NodeStatus::IDLE); } @@ -218,7 +229,7 @@ NodeStatus BT::ThreadedAction::executeTick() }); } - lock_type l(mutex_); + const lock_type l(mutex_); if(exptr_) { // The official interface of std::exception_ptr does not define any move diff --git a/src/actions/sleep_node.cpp b/src/actions/sleep_node.cpp index 3fbccf10a..108518010 100644 --- a/src/actions/sleep_node.cpp +++ b/src/actions/sleep_node.cpp @@ -25,7 +25,7 @@ NodeStatus SleepNode::onStart() timer_waiting_ = true; timer_id_ = timer_.add(std::chrono::milliseconds(msec), [this](bool aborted) { - std::unique_lock lk(delay_mutex_); + const std::unique_lock lk(delay_mutex_); if(!aborted) { emitWakeUpSignal(); diff --git a/src/actions/updated_action.cpp b/src/actions/updated_action.cpp index e3b580b61..1bc0f0713 100644 --- a/src/actions/updated_action.cpp +++ b/src/actions/updated_action.cpp @@ -40,7 +40,7 @@ NodeStatus EntryUpdatedAction::tick() { if(auto entry = config().blackboard->getEntry(entry_key_)) { - std::unique_lock lk(entry->entry_mutex); + const std::unique_lock lk(entry->entry_mutex); const uint64_t current_id = entry->sequence_id; const uint64_t previous_id = sequence_id_; sequence_id_ = current_id; diff --git a/src/basic_types.cpp b/src/basic_types.cpp index ab5e016bb..285349d6e 100644 --- a/src/basic_types.cpp +++ b/src/basic_types.cpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace BT { @@ -46,31 +47,28 @@ std::string toStr(NodeStatus status, bool colored) { return toStr(status); } - else + switch(status) { - switch(status) - { - case NodeStatus::SUCCESS: - return "\x1b[32m" - "SUCCESS" - "\x1b[0m"; // RED - case NodeStatus::FAILURE: - return "\x1b[31m" - "FAILURE" - "\x1b[0m"; // GREEN - case NodeStatus::RUNNING: - return "\x1b[33m" - "RUNNING" - "\x1b[0m"; // YELLOW - case NodeStatus::SKIPPED: - return "\x1b[34m" - "SKIPPED" - "\x1b[0m"; // BLUE - case NodeStatus::IDLE: - return "\x1b[36m" - "IDLE" - "\x1b[0m"; // CYAN - } + case NodeStatus::SUCCESS: + return "\x1b[32m" + "SUCCESS" + "\x1b[0m"; // GREEN + case NodeStatus::FAILURE: + return "\x1b[31m" + "FAILURE" + "\x1b[0m"; // RED + case NodeStatus::RUNNING: + return "\x1b[33m" + "RUNNING" + "\x1b[0m"; // YELLOW + case NodeStatus::SKIPPED: + return "\x1b[34m" + "SKIPPED" + "\x1b[0m"; // BLUE + case NodeStatus::IDLE: + return "\x1b[36m" + "IDLE" + "\x1b[0m"; // CYAN } return "Undefined"; } @@ -120,7 +118,8 @@ template <> int64_t convertFromString(StringView str) { long result = 0; - auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), result); + const auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), result); + std::ignore = ptr; if(ec != std::errc()) { throw RuntimeError(StrCat("Can't convert string [", str, "] to integer")); @@ -132,7 +131,8 @@ template <> uint64_t convertFromString(StringView str) { unsigned long result = 0; - auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), result); + const auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), result); + std::ignore = ptr; if(ec != std::errc()) { throw RuntimeError(StrCat("Can't convert string [", str, "] to integer")); @@ -140,6 +140,8 @@ uint64_t convertFromString(StringView str) return result; } +namespace +{ template T ConvertWithBoundCheck(StringView str) { @@ -151,6 +153,7 @@ T ConvertWithBoundCheck(StringView str) } return res; } +} // namespace template <> int8_t convertFromString(StringView str) @@ -194,21 +197,23 @@ double convertFromString(StringView str) // see issue #120 // http://quick-bench.com/DWaXRWnxtxvwIMvZy2DxVPEKJnE - std::string old_locale = setlocale(LC_NUMERIC, nullptr); - setlocale(LC_NUMERIC, "C"); - double val = std::stod(str.data()); - setlocale(LC_NUMERIC, old_locale.c_str()); + const std::string old_locale = setlocale(LC_NUMERIC, nullptr); + std::ignore = setlocale(LC_NUMERIC, "C"); + const std::string str_copy(str.data(), str.size()); + const double val = std::stod(str_copy); + std::ignore = setlocale(LC_NUMERIC, old_locale.c_str()); return val; } template <> float convertFromString(StringView str) { - std::string old_locale = setlocale(LC_NUMERIC, nullptr); - setlocale(LC_NUMERIC, "C"); - float val = std::stof(str.data()); - setlocale(LC_NUMERIC, old_locale.c_str()); - return val; + const std::string old_locale = setlocale(LC_NUMERIC, nullptr); + std::ignore = setlocale(LC_NUMERIC, "C"); + const std::string str_copy(str.data(), str.size()); + const double val = std::stod(str_copy); + std::ignore = setlocale(LC_NUMERIC, old_locale.c_str()); + return static_cast(val); } template <> @@ -439,7 +444,7 @@ bool IsAllowedPortName(StringView str) return false; } const char first_char = str.data()[0]; - if(!std::isalpha(first_char)) + if(std::isalpha(static_cast(first_char)) == 0) { return false; } @@ -467,7 +472,7 @@ bool IsReservedAttribute(StringView str) Any convertFromJSON(StringView json_text, std::type_index type) { - nlohmann::json json = nlohmann::json::parse(json_text); + const nlohmann::json json = nlohmann::json::parse(json_text); auto res = JsonExporter::get().fromJson(json, type); if(!res) { @@ -488,8 +493,18 @@ Expected toJsonString(const Any& value) bool StartWith(StringView str, StringView prefix) { - return str.size() >= prefix.size() && - strncmp(str.data(), prefix.data(), prefix.size()) == 0; + if(str.size() < prefix.size()) + { + return false; + } + for(size_t i = 0; i < prefix.size(); ++i) + { + if(str[i] != prefix[i]) + { + return false; + } + } + return true; } bool StartWith(StringView str, char prefix) diff --git a/src/behavior_tree.cpp b/src/behavior_tree.cpp index 02c236129..ed610ddca 100644 --- a/src/behavior_tree.cpp +++ b/src/behavior_tree.cpp @@ -18,7 +18,7 @@ namespace BT void applyRecursiveVisitor(const TreeNode* node, const std::function& visitor) { - if(!node) + if(node == nullptr) { throw LogicError("One of the children of a DecoratorNode or ControlNode is nullptr"); } @@ -40,7 +40,7 @@ void applyRecursiveVisitor(const TreeNode* node, void applyRecursiveVisitor(TreeNode* node, const std::function& visitor) { - if(!node) + if(node == nullptr) { throw LogicError("One of the children of a DecoratorNode or ControlNode is nullptr"); } @@ -56,7 +56,7 @@ void applyRecursiveVisitor(TreeNode* node, const std::function& } else if(auto decorator = dynamic_cast(node)) { - if(decorator->child()) + if(decorator->child() != nullptr) { applyRecursiveVisitor(decorator->child(), visitor); } @@ -72,7 +72,7 @@ void printTreeRecursively(const TreeNode* root_node, std::ostream& stream) { stream << " "; } - if(!node) + if(node == nullptr) { stream << "!nullptr!" << std::endl; return; @@ -98,7 +98,7 @@ void printTreeRecursively(const TreeNode* root_node, std::ostream& stream) stream << "----------------" << std::endl; } -void buildSerializedStatusSnapshot(TreeNode* root_node, +void buildSerializedStatusSnapshot(const TreeNode* root_node, SerializedTreeStatus& serialized_buffer) { serialized_buffer.clear(); diff --git a/src/blackboard.cpp b/src/blackboard.cpp index 462e7b41e..8a989e974 100644 --- a/src/blackboard.cpp +++ b/src/blackboard.cpp @@ -1,14 +1,18 @@ #include "behaviortree_cpp/blackboard.h" +#include #include #include "behaviortree_cpp/json_export.h" namespace BT { +namespace +{ bool IsPrivateKey(StringView str) { return str.size() >= 1 && str.data()[0] == '_'; } +} // namespace void Blackboard::enableAutoRemapping(bool remapping) { @@ -40,6 +44,7 @@ const Any* Blackboard::getAny(const std::string& key) const Any* Blackboard::getAny(const std::string& key) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) return const_cast(getAnyLocked(key).get()); } @@ -53,7 +58,7 @@ Blackboard::getEntry(const std::string& key) const } { - std::unique_lock storage_lock(storage_mutex_); + const std::unique_lock storage_lock(storage_mutex_); auto it = storage_.find(key); if(it != storage_.end()) { @@ -111,7 +116,6 @@ void Blackboard::debugMessage() const { std::cout << "[" << from << "] remapped to port of parent tree [" << to << "]" << std::endl; - continue; } } @@ -132,7 +136,7 @@ std::vector Blackboard::getKeys() const void Blackboard::clear() { - std::unique_lock storage_lock(storage_mutex_); + const std::unique_lock storage_lock(storage_mutex_); storage_.clear(); } @@ -167,8 +171,9 @@ void Blackboard::cloneInto(Blackboard& dst) const // keys that are not updated must be removed. std::unordered_set keys_to_remove; auto& dst_storage = dst.storage_; - for(const auto& [key, _] : dst_storage) + for(const auto& [key, entry] : dst_storage) { + std::ignore = entry; // unused in this loop keys_to_remove.insert(key); } @@ -216,7 +221,7 @@ Blackboard::Ptr Blackboard::parent() std::shared_ptr Blackboard::createEntryImpl(const std::string& key, const TypeInfo& info) { - std::unique_lock storage_lock(storage_mutex_); + const std::unique_lock storage_lock(storage_mutex_); // This function might be called recursively, when we do remapping, because we move // to the top scope to find already existing entries @@ -273,7 +278,7 @@ nlohmann::json ExportBlackboardToJSON(const Blackboard& blackboard) nlohmann::json dest; for(auto entry_name : blackboard.getKeys()) { - std::string name(entry_name); + const std::string name(entry_name); if(auto any_ref = blackboard.getAnyLocked(name)) { if(auto any_ptr = any_ref.get()) @@ -302,19 +307,10 @@ void ImportBlackboardFromJSON(const nlohmann::json& json, Blackboard& blackboard } } -Blackboard::Entry& Blackboard::Entry::operator=(const Entry& other) -{ - value = other.value; - info = other.info; - string_converter = other.string_converter; - sequence_id = other.sequence_id; - stamp = other.stamp; - return *this; -} - Blackboard* BT::Blackboard::rootBlackboard() { auto bb = static_cast(*this).rootBlackboard(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) return const_cast(bb); } diff --git a/src/bt_factory.cpp b/src/bt_factory.cpp index 3bb79763b..487ea6034 100644 --- a/src/bt_factory.cpp +++ b/src/bt_factory.cpp @@ -102,12 +102,11 @@ BehaviorTreeFactory::BehaviorTreeFactory() : _p(new PImpl) _p->scripting_enums = std::make_shared>(); } -BehaviorTreeFactory::~BehaviorTreeFactory() -{} +BehaviorTreeFactory::~BehaviorTreeFactory() = default; bool BehaviorTreeFactory::unregisterBuilder(const std::string& ID) { - if(builtinNodes().count(ID)) + if(builtinNodes().count(ID) != 0) { throw LogicError("You can not remove the builtin registration ID [", ID, "]"); } @@ -138,12 +137,12 @@ void BehaviorTreeFactory::registerSimpleCondition( const std::string& ID, const SimpleConditionNode::TickFunctor& tick_functor, PortsList ports) { - NodeBuilder builder = [tick_functor, ID](const std::string& name, - const NodeConfig& config) { + const NodeBuilder builder = [tick_functor, ID](const std::string& name, + const NodeConfig& config) { return std::make_unique(name, tick_functor, config); }; - TreeNodeManifest manifest = { NodeType::CONDITION, ID, std::move(ports), {} }; + const TreeNodeManifest manifest = { NodeType::CONDITION, ID, std::move(ports), {} }; registerBuilder(manifest, builder); } @@ -151,12 +150,12 @@ void BehaviorTreeFactory::registerSimpleAction( const std::string& ID, const SimpleActionNode::TickFunctor& tick_functor, PortsList ports) { - NodeBuilder builder = [tick_functor, ID](const std::string& name, - const NodeConfig& config) { + const NodeBuilder builder = [tick_functor, ID](const std::string& name, + const NodeConfig& config) { return std::make_unique(name, tick_functor, config); }; - TreeNodeManifest manifest = { NodeType::ACTION, ID, std::move(ports), {} }; + const TreeNodeManifest manifest = { NodeType::ACTION, ID, std::move(ports), {} }; registerBuilder(manifest, builder); } @@ -164,12 +163,12 @@ void BehaviorTreeFactory::registerSimpleDecorator( const std::string& ID, const SimpleDecoratorNode::TickFunctor& tick_functor, PortsList ports) { - NodeBuilder builder = [tick_functor, ID](const std::string& name, - const NodeConfig& config) { + const NodeBuilder builder = [tick_functor, ID](const std::string& name, + const NodeConfig& config) { return std::make_unique(name, tick_functor, config); }; - TreeNodeManifest manifest = { NodeType::DECORATOR, ID, std::move(ports), {} }; + const TreeNodeManifest manifest = { NodeType::DECORATOR, ID, std::move(ports), {} }; registerBuilder(manifest, builder); } @@ -177,11 +176,12 @@ void BehaviorTreeFactory::registerFromPlugin(const std::string& file_path) { BT::SharedLibrary loader; loader.load(file_path); - typedef void (*Func)(BehaviorTreeFactory&); + using Func = void (*)(BehaviorTreeFactory&); if(loader.hasSymbol(PLUGIN_SYMBOL)) { - Func func = (Func)loader.getSymbol(PLUGIN_SYMBOL); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto* func = reinterpret_cast(loader.getSymbol(PLUGIN_SYMBOL)); func(*this); } else @@ -191,6 +191,7 @@ void BehaviorTreeFactory::registerFromPlugin(const std::string& file_path) } } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) void BehaviorTreeFactory::registerFromROSPlugins() { throw RuntimeError("Using attribute [ros_pkg] in , but this library was " @@ -219,6 +220,7 @@ void BehaviorTreeFactory::clearRegisteredBehaviorTrees() _p->parser->clearInternalState(); } +// NOLINTNEXTLINE(readability-function-cognitive-complexity) std::unique_ptr BehaviorTreeFactory::instantiateTreeNode( const std::string& name, const std::string& ID, const NodeConfig& config) const { @@ -246,7 +248,7 @@ std::unique_ptr BehaviorTreeFactory::instantiateTreeNode( { // first case: the rule is simply a string with the name of the // node to create instead - if(const auto substituted_ID = std::get_if(&rule)) + if(const auto* const substituted_ID = std::get_if(&rule)) { auto it_builder = _p->builders.find(*substituted_ID); if(it_builder != _p->builders.end()) @@ -261,24 +263,23 @@ std::unique_ptr BehaviorTreeFactory::instantiateTreeNode( substituted = true; break; } - else if(const auto test_config = std::get_if(&rule)) + + if(const auto* const test_config = std::get_if(&rule)) { node = std::make_unique(name, config, std::make_shared(*test_config)); substituted = true; break; } - else if(const auto test_config = - std::get_if>(&rule)) + + if(const auto* const test_config = + std::get_if>(&rule)) { node = std::make_unique(name, config, *test_config); substituted = true; break; } - else - { - throw LogicError("Substitution rule is not a string or a TestNodeConfig"); - } + throw LogicError("Substitution rule is not a string or a TestNodeConfig"); } } @@ -414,7 +415,7 @@ void BehaviorTreeFactory::clearSubstitutionRules() void BehaviorTreeFactory::addSubstitutionRule(StringView filter, SubstitutionRule rule) { - _p->substitution_rules[std::string(filter)] = rule; + _p->substitution_rules[std::string(filter)] = std::move(rule); } void BehaviorTreeFactory::loadSubstitutionRuleFromJSON(const std::string& json_text) @@ -471,8 +472,7 @@ BehaviorTreeFactory::substitutionRules() const return _p->substitution_rules; } -Tree::Tree() -{} +Tree::Tree() = default; void Tree::initialize() { @@ -486,9 +486,10 @@ void Tree::initialize() } } +// NOLINTNEXTLINE(readability-make-member-function-const) void Tree::haltTree() { - if(!rootNode()) + if(rootNode() == nullptr) { return; } @@ -546,7 +547,7 @@ NodeStatus Tree::tickWhileRunning(std::chrono::milliseconds sleep_time) Blackboard::Ptr Tree::rootBlackboard() { - if(subtrees.size() > 0) + if(!subtrees.empty()) { return subtrees.front()->blackboard; } @@ -558,9 +559,10 @@ void Tree::applyVisitor(const std::function& visitor) con BT::applyRecursiveVisitor(static_cast(rootNode()), visitor); } +// NOLINTNEXTLINE(readability-make-member-function-const) void Tree::applyVisitor(const std::function& visitor) { - BT::applyRecursiveVisitor(static_cast(rootNode()), visitor); + BT::applyRecursiveVisitor(rootNode(), visitor); } uint16_t Tree::getUID() @@ -578,7 +580,7 @@ NodeStatus Tree::tickRoot(TickOption opt, std::chrono::milliseconds sleep_time) initialize(); } - if(!rootNode()) + if(rootNode() == nullptr) { throw RuntimeError("Empty Tree"); } @@ -635,7 +637,6 @@ nlohmann::json ExportTreeToJSON(const Tree& tree) nlohmann::json out; for(const auto& subtree : tree.subtrees) { - nlohmann::json json_sub; auto sub_name = subtree->instance_name; if(sub_name.empty()) { @@ -654,7 +655,7 @@ void ImportTreeFromJSON(const nlohmann::json& json, Tree& tree) } size_t index = 0; - for(auto& [key, array] : json.items()) + for(const auto& [key, array] : json.items()) { auto& subtree = tree.subtrees.at(index++); ImportBlackboardFromJSON(array, *subtree->blackboard); diff --git a/src/control_node.cpp b/src/control_node.cpp index 28e96b3d9..535165376 100644 --- a/src/control_node.cpp +++ b/src/control_node.cpp @@ -37,7 +37,7 @@ void ControlNode::halt() void ControlNode::resetChildren() { - for(auto child : children_nodes_) + for(auto* child : children_nodes_) { if(child->status() == NodeStatus::RUNNING) { @@ -54,7 +54,7 @@ const std::vector& ControlNode::children() const void ControlNode::haltChild(size_t i) { - auto child = children_nodes_[i]; + auto* child = children_nodes_[i]; if(child->status() == NodeStatus::RUNNING) { child->haltNode(); diff --git a/src/controls/fallback_node.cpp b/src/controls/fallback_node.cpp index 86fed0fab..333e90db0 100644 --- a/src/controls/fallback_node.cpp +++ b/src/controls/fallback_node.cpp @@ -19,9 +19,13 @@ FallbackNode::FallbackNode(const std::string& name, bool make_asynch) : ControlNode::ControlNode(name, {}), current_child_idx_(0), asynch_(make_asynch) { if(asynch_) + { setRegistrationID("AsyncFallback"); + } else + { setRegistrationID("Fallback"); + } } NodeStatus FallbackNode::tick() diff --git a/src/controls/if_then_else_node.cpp b/src/controls/if_then_else_node.cpp index 2dce72fa0..7d33099c6 100644 --- a/src/controls/if_then_else_node.cpp +++ b/src/controls/if_then_else_node.cpp @@ -39,13 +39,13 @@ NodeStatus IfThenElseNode::tick() if(child_idx_ == 0) { - NodeStatus condition_status = children_nodes_[0]->executeTick(); + const NodeStatus condition_status = children_nodes_[0]->executeTick(); if(condition_status == NodeStatus::RUNNING) { return condition_status; } - else if(condition_status == NodeStatus::SUCCESS) + if(condition_status == NodeStatus::SUCCESS) { child_idx_ = 1; } @@ -64,17 +64,14 @@ NodeStatus IfThenElseNode::tick() // not an else if(child_idx_ > 0) { - NodeStatus status = children_nodes_[child_idx_]->executeTick(); + const NodeStatus status = children_nodes_[child_idx_]->executeTick(); if(status == NodeStatus::RUNNING) { return NodeStatus::RUNNING; } - else - { - resetChildren(); - child_idx_ = 0; - return status; - } + resetChildren(); + child_idx_ = 0; + return status; } throw std::logic_error("Something unexpected happened in IfThenElseNode"); diff --git a/src/controls/manual_node.cpp b/src/controls/manual_node.cpp index 1ca31eda3..d8461fd4a 100644 --- a/src/controls/manual_node.cpp +++ b/src/controls/manual_node.cpp @@ -73,7 +73,7 @@ NodeStatus ManualSelectorNode::tick() } } - NodeStatus ret = children_nodes_[idx]->executeTick(); + const NodeStatus ret = children_nodes_[idx]->executeTick(); if(ret == NodeStatus::RUNNING) { running_child_idx_ = idx; @@ -83,12 +83,10 @@ NodeStatus ManualSelectorNode::tick() NodeStatus ManualSelectorNode::selectStatus() const { - WINDOW* win; + WINDOW* win = nullptr; initscr(); cbreak(); - win = newwin(6, 70, 1, 1); // create a new window - mvwprintw(win, 0, 0, "No children."); mvwprintw(win, 1, 0, "Press: S to return SUCCESSFUL,"); mvwprintw(win, 2, 0, " F to return FAILURE, or"); @@ -100,20 +98,20 @@ NodeStatus ManualSelectorNode::selectStatus() const curs_set(0); // hide the default screen cursor. int ch = 0; - NodeStatus ret; - while(1) + NodeStatus ret = NodeStatus::RUNNING; + while(true) { if(ch == 's' || ch == 'S') { ret = NodeStatus::SUCCESS; break; } - else if(ch == 'f' || ch == 'F') + if(ch == 'f' || ch == 'F') { ret = NodeStatus::FAILURE; break; } - else if(ch == 'r' || ch == 'R') + if(ch == 'r' || ch == 'R') { ret = NodeStatus::RUNNING; break; @@ -144,11 +142,11 @@ uint8_t ManualSelectorNode::selectChild() const width = std::max(width, str.size() + 2); } - WINDOW* win; + WINDOW* win = nullptr; initscr(); cbreak(); - win = newwin(children_count + 6, 70, 1, 1); // create a new window + win = newwin(static_cast(children_count) + 6, 70, 1, 1); // create a new window mvwprintw(win, 0, 0, "Use UP/DOWN arrow to select the child, Enter to confirm."); mvwprintw(win, 1, 0, "Press: S to skip and return SUCCESSFUL,"); @@ -158,7 +156,7 @@ uint8_t ManualSelectorNode::selectChild() const // now print all the menu items and highlight the first one for(size_t i = 0; i < list.size(); i++) { - mvwprintw(win, i + 5, 0, "%2ld. %s", i + 1, list[i].c_str()); + mvwprintw(win, static_cast(i) + 5, 0, "%2zu. %s", i + 1, list[i].c_str()); } wrefresh(win); // update the terminal screen @@ -168,7 +166,7 @@ uint8_t ManualSelectorNode::selectChild() const uint8_t row = 0; int ch = 0; - while(1) + while(true) { // right pad with spaces to make the items appear with even width. wattroff(win, A_STANDOUT); diff --git a/src/controls/reactive_fallback.cpp b/src/controls/reactive_fallback.cpp index cafba1e21..54399f0a6 100644 --- a/src/controls/reactive_fallback.cpp +++ b/src/controls/reactive_fallback.cpp @@ -53,9 +53,9 @@ NodeStatus ReactiveFallback::tick() } if(running_child_ == -1) { - running_child_ = int(index); + running_child_ = static_cast(index); } - else if(throw_if_multiple_running && running_child_ != int(index)) + else if(throw_if_multiple_running && running_child_ != static_cast(index)) { throw LogicError("[ReactiveFallback]: only a single child can return RUNNING.\n" "This throw can be disabled with " diff --git a/src/controls/reactive_sequence.cpp b/src/controls/reactive_sequence.cpp index ec16137a5..0a6f7ae6c 100644 --- a/src/controls/reactive_sequence.cpp +++ b/src/controls/reactive_sequence.cpp @@ -55,7 +55,7 @@ NodeStatus ReactiveSequence::tick() { running_child_ = int(index); } - else if(throw_if_multiple_running && running_child_ != int(index)) + else if(throw_if_multiple_running && running_child_ != static_cast(index)) { throw LogicError("[ReactiveSequence]: only a single child can return RUNNING.\n" "This throw can be disabled with " diff --git a/src/controls/sequence_node.cpp b/src/controls/sequence_node.cpp index 306fea022..46afdab88 100644 --- a/src/controls/sequence_node.cpp +++ b/src/controls/sequence_node.cpp @@ -20,9 +20,13 @@ SequenceNode::SequenceNode(const std::string& name, bool make_async, : ControlNode::ControlNode(name, conf), current_child_idx_(0), asynch_(make_async) { if(asynch_) + { setRegistrationID("AsyncSequence"); + } else + { setRegistrationID("Sequence"); + } } void SequenceNode::halt() diff --git a/src/controls/switch_node.cpp b/src/controls/switch_node.cpp index 121248edd..e43e70a4e 100644 --- a/src/controls/switch_node.cpp +++ b/src/controls/switch_node.cpp @@ -79,11 +79,7 @@ bool CheckStringEquality(const std::string& v1, const std::string& v2, double v1_real = 0; double v2_real = 0; constexpr auto eps = double(std::numeric_limits::epsilon()); - if(ToReal(v1, v1_real) && ToReal(v2, v2_real) && std::abs(v1_real - v2_real) <= eps) - { - return true; - } - return false; + return ToReal(v1, v1_real) && ToReal(v2, v2_real) && std::abs(v1_real - v2_real) <= eps; } } // namespace BT::details diff --git a/src/controls/while_do_else_node.cpp b/src/controls/while_do_else_node.cpp index 480f00dcb..40c58086e 100644 --- a/src/controls/while_do_else_node.cpp +++ b/src/controls/while_do_else_node.cpp @@ -36,7 +36,7 @@ NodeStatus WhileDoElseNode::tick() setStatus(NodeStatus::RUNNING); - NodeStatus condition_status = children_nodes_[0]->executeTick(); + const NodeStatus condition_status = children_nodes_[0]->executeTick(); if(condition_status == NodeStatus::RUNNING) { @@ -70,11 +70,8 @@ NodeStatus WhileDoElseNode::tick() { return NodeStatus::RUNNING; } - else - { - resetChildren(); - return status; - } + resetChildren(); + return status; } } // namespace BT diff --git a/src/decorator_node.cpp b/src/decorator_node.cpp index 7e64ebda3..ab41215ed 100644 --- a/src/decorator_node.cpp +++ b/src/decorator_node.cpp @@ -21,7 +21,7 @@ DecoratorNode::DecoratorNode(const std::string& name, const NodeConfig& config) void DecoratorNode::setChild(TreeNode* child) { - if(child_node_) + if(child_node_ != nullptr) { throw BehaviorTreeException("Decorator [", name(), "] has already a child assigned"); } @@ -52,7 +52,7 @@ void DecoratorNode::haltChild() void DecoratorNode::resetChild() { - if(!child_node_) + if(child_node_ == nullptr) { return; } @@ -76,8 +76,8 @@ NodeStatus SimpleDecoratorNode::tick() NodeStatus DecoratorNode::executeTick() { - NodeStatus status = TreeNode::executeTick(); - NodeStatus child_status = child()->status(); + const NodeStatus status = TreeNode::executeTick(); + const NodeStatus child_status = child()->status(); if(child_status == NodeStatus::SUCCESS || child_status == NodeStatus::FAILURE) { child()->resetStatus(); diff --git a/src/decorators/delay_node.cpp b/src/decorators/delay_node.cpp index d942e2f6b..ea6f82ff2 100644 --- a/src/decorators/delay_node.cpp +++ b/src/decorators/delay_node.cpp @@ -1,26 +1,18 @@ /* Contributed by Indraneel on 26/04/2020 -*/ + */ #include "behaviortree_cpp/decorators/delay_node.h" namespace BT { DelayNode::DelayNode(const std::string& name, unsigned milliseconds) - : DecoratorNode(name, {}) - , delay_started_(false) - , delay_aborted_(false) - , msec_(milliseconds) - , read_parameter_from_ports_(false) + : DecoratorNode(name, {}), timer_id_(0), msec_(milliseconds) { setRegistrationID("Delay"); } DelayNode::DelayNode(const std::string& name, const NodeConfig& config) - : DecoratorNode(name, config) - , delay_started_(false) - , delay_aborted_(false) - , msec_(0) - , read_parameter_from_ports_(true) + : DecoratorNode(name, config), timer_id_(0), msec_(0) {} void DelayNode::halt() @@ -48,7 +40,7 @@ NodeStatus DelayNode::tick() setStatus(NodeStatus::RUNNING); timer_id_ = timer_.add(std::chrono::milliseconds(msec_), [this](bool aborted) { - std::unique_lock lk(delay_mutex_); + const std::unique_lock lk(delay_mutex_); delay_complete_ = (!aborted); if(!aborted) { @@ -57,7 +49,7 @@ NodeStatus DelayNode::tick() }); } - std::unique_lock lk(delay_mutex_); + const std::unique_lock lk(delay_mutex_); if(delay_aborted_) { @@ -65,7 +57,7 @@ NodeStatus DelayNode::tick() delay_started_ = false; return NodeStatus::FAILURE; } - else if(delay_complete_) + if(delay_complete_) { const NodeStatus child_status = child()->executeTick(); if(isStatusCompleted(child_status)) @@ -76,10 +68,7 @@ NodeStatus DelayNode::tick() } return child_status; } - else - { - return NodeStatus::RUNNING; - } + return NodeStatus::RUNNING; } } // namespace BT diff --git a/src/decorators/repeat_node.cpp b/src/decorators/repeat_node.cpp index 960ddd76c..d502397c3 100644 --- a/src/decorators/repeat_node.cpp +++ b/src/decorators/repeat_node.cpp @@ -47,8 +47,8 @@ NodeStatus RepeatNode::tick() while(do_loop) { - NodeStatus const prev_status = child_node_->status(); - NodeStatus child_status = child_node_->executeTick(); + const NodeStatus prev_status = child_node_->status(); + const NodeStatus child_status = child_node_->executeTick(); switch(child_status) { diff --git a/src/decorators/retry_node.cpp b/src/decorators/retry_node.cpp index 5b76dbadb..c6a11f1b5 100644 --- a/src/decorators/retry_node.cpp +++ b/src/decorators/retry_node.cpp @@ -15,8 +15,6 @@ namespace BT { -constexpr const char* RetryNode::NUM_ATTEMPTS; - RetryNode::RetryNode(const std::string& name, int NTries) : DecoratorNode(name, {}) , max_attempts_(NTries) @@ -54,8 +52,8 @@ NodeStatus RetryNode::tick() while(do_loop) { - NodeStatus prev_status = child_node_->status(); - NodeStatus child_status = child_node_->executeTick(); + const NodeStatus prev_status = child_node_->status(); + const NodeStatus child_status = child_node_->executeTick(); switch(child_status) { diff --git a/src/decorators/subtree_node.cpp b/src/decorators/subtree_node.cpp index fa695993a..212b0886f 100644 --- a/src/decorators/subtree_node.cpp +++ b/src/decorators/subtree_node.cpp @@ -19,7 +19,7 @@ BT::PortsList BT::SubTreeNode::providedPorts() BT::NodeStatus BT::SubTreeNode::tick() { - NodeStatus prev_status = status(); + const NodeStatus prev_status = status(); if(prev_status == NodeStatus::IDLE) { setStatus(NodeStatus::RUNNING); diff --git a/src/decorators/timeout_node.cpp b/src/decorators/timeout_node.cpp index 345409b82..5fc254954 100644 --- a/src/decorators/timeout_node.cpp +++ b/src/decorators/timeout_node.cpp @@ -41,7 +41,7 @@ NodeStatus TimeoutNode::tick() { return; } - std::unique_lock lk(timeout_mutex_); + const std::unique_lock lk(timeout_mutex_); if(child()->status() == NodeStatus::RUNNING) { child_halted_ = true; @@ -59,19 +59,16 @@ NodeStatus TimeoutNode::tick() timeout_started_ = false; return NodeStatus::FAILURE; } - else + const NodeStatus child_status = child()->executeTick(); + if(isStatusCompleted(child_status)) { - const NodeStatus child_status = child()->executeTick(); - if(isStatusCompleted(child_status)) - { - timeout_started_ = false; - timeout_mutex_.unlock(); - timer_.cancel(timer_id_); - timeout_mutex_.lock(); - resetChild(); - } - return child_status; + timeout_started_ = false; + lk.unlock(); + timer_.cancel(timer_id_); + lk.lock(); + resetChild(); } + return child_status; } void TimeoutNode::halt() diff --git a/src/decorators/updated_decorator.cpp b/src/decorators/updated_decorator.cpp index ff35222bd..8ddc8d8af 100644 --- a/src/decorators/updated_decorator.cpp +++ b/src/decorators/updated_decorator.cpp @@ -50,7 +50,7 @@ NodeStatus EntryUpdatedDecorator::tick() if(auto entry = config().blackboard->getEntry(entry_key_)) { - std::unique_lock lk(entry->entry_mutex); + const std::unique_lock lk(entry->entry_mutex); const uint64_t current_id = entry->sequence_id; const uint64_t previous_id = sequence_id_; sequence_id_ = current_id; diff --git a/src/example.cpp b/src/example.cpp deleted file mode 100644 index 252c91bdb..000000000 --- a/src/example.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include -#include - -class MyCondition : public BT::ConditionNode -{ -public: - MyCondition(const std::string& name); - ~MyCondition(); - BT::ReturnStatus Tick(); -}; - -MyCondition::MyCondition(const std::string& name) : BT::ConditionNode::ConditionNode(name) -{} - -BT::ReturnStatus MyCondition::Tick() -{ - std::cout << "The Condition is true" << std::endl; - - return NodeStatus::SUCCESS; -} - -class MyAction : public BT::ActionNode -{ -public: - MyAction(const std::string& name); - ~MyAction(); - BT::ReturnStatus Tick(); - void Halt(); -}; - -MyAction::MyAction(const std::string& name) : ActionNode::ActionNode(name) -{} - -BT::ReturnStatus MyAction::Tick() -{ - std::cout << "The Action is doing some operations" << std::endl; - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - if(is_halted()) - { - return NodeStatus::IDLE; - } - - std::cout << "The Action is doing some others operations" << std::endl; - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - if(is_halted()) - { - return NodeStatus::IDLE; - } - - std::cout << "The Action is doing more operations" << std::endl; - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - if(is_halted()) - { - return NodeStatus::IDLE; - } - - std::cout << "The Action has succeeded" << std::endl; - return NodeStatus::SUCCESS; -} - -void MyAction::Halt() -{} - -int main(int argc, char* argv[]) -{ - BT::SequenceNode* seq = new BT::SequenceNode("Sequence"); - MyCondition* my_con_1 = new MyCondition("Condition"); - MyAction* my_act_1 = new MyAction("Action"); - int tick_time_milliseconds = 1000; - - seq->AddChild(my_con_1); - seq->AddChild(my_act_1); - - Execute(seq, tick_time_milliseconds); - - return 0; -} diff --git a/src/json_export.cpp b/src/json_export.cpp index b716a94ea..f8f6d3ec1 100644 --- a/src/json_export.cpp +++ b/src/json_export.cpp @@ -11,7 +11,6 @@ JsonExporter& JsonExporter::get() bool JsonExporter::toJson(const Any& any, nlohmann::json& dst) const { - nlohmann::json json; auto const& type = any.castedType(); if(any.isString()) @@ -89,7 +88,7 @@ JsonExporter::ExpectedEntry JsonExporter::fromJson(const nlohmann::json& source) // basic vectors if(source.is_array() && source.size() > 0 && !source.contains("__type")) { - auto first_element = source[0]; + const auto first_element = source[0]; if(first_element.is_string()) { return Entry{ BT::Any(source.get>()), diff --git a/src/loggers/bt_cout_logger.cpp b/src/loggers/bt_cout_logger.cpp index 121bc60f3..c08ba7475 100644 --- a/src/loggers/bt_cout_logger.cpp +++ b/src/loggers/bt_cout_logger.cpp @@ -5,8 +5,7 @@ namespace BT StdCoutLogger::StdCoutLogger(const BT::Tree& tree) : StatusChangeLogger(tree.rootNode()) {} -StdCoutLogger::~StdCoutLogger() -{} +StdCoutLogger::~StdCoutLogger() = default; void StdCoutLogger::callback(Duration timestamp, const TreeNode& node, NodeStatus prev_status, NodeStatus status) @@ -16,7 +15,7 @@ void StdCoutLogger::callback(Duration timestamp, const TreeNode& node, constexpr const char* whitespaces = " "; constexpr const size_t ws_count = 25; - double since_epoch = duration(timestamp).count(); + const double since_epoch = duration(timestamp).count(); printf("[%.3f]: %s%s %s -> %s", since_epoch, node.name().c_str(), &whitespaces[std::min(ws_count, node.name().size())], toStr(prev_status, true).c_str(), toStr(status, true).c_str()); diff --git a/src/loggers/bt_file_logger_v2.cpp b/src/loggers/bt_file_logger_v2.cpp index 40d39381d..a1a5257fd 100644 --- a/src/loggers/bt_file_logger_v2.cpp +++ b/src/loggers/bt_file_logger_v2.cpp @@ -5,10 +5,13 @@ namespace BT { +namespace +{ int64_t ToUsec(Duration ts) { return std::chrono::duration_cast(ts).count(); } +} // namespace struct FileLogger2::PImpl { @@ -51,19 +54,19 @@ FileLogger2::FileLogger2(const BT::Tree& tree, std::filesystem::path const& file std::string const xml = WriteTreeToXML(tree, true, true); // serialize the length of the buffer in the first 4 bytes - char write_buffer[8]; - flatbuffers::WriteScalar(write_buffer, static_cast(xml.size())); - _p->file_stream.write(write_buffer, 4); + std::array write_buffer{}; + flatbuffers::WriteScalar(write_buffer.data(), static_cast(xml.size())); + _p->file_stream.write(write_buffer.data(), 4); // write the XML definition - _p->file_stream.write(xml.data(), int(xml.size())); + _p->file_stream.write(xml.data(), static_cast(xml.size())); _p->first_timestamp = std::chrono::system_clock::now().time_since_epoch(); // save the first timestamp in the next 8 bytes (microseconds) - int64_t timestamp_usec = ToUsec(_p->first_timestamp); - flatbuffers::WriteScalar(write_buffer, timestamp_usec); - _p->file_stream.write(write_buffer, 8); + const int64_t timestamp_usec = ToUsec(_p->first_timestamp); + flatbuffers::WriteScalar(write_buffer.data(), timestamp_usec); + _p->file_stream.write(write_buffer.data(), 8); _p->writer_thread = std::thread(&FileLogger2::writerLoop, this); } @@ -79,12 +82,12 @@ FileLogger2::~FileLogger2() void FileLogger2::callback(Duration timestamp, const TreeNode& node, NodeStatus /*prev_status*/, NodeStatus status) { - Transition trans; + Transition trans{}; trans.timestamp_usec = uint64_t(ToUsec(timestamp - _p->first_timestamp)); trans.node_uid = node.UID(); trans.status = static_cast(status); { - std::scoped_lock lock(_p->queue_mutex); + const std::scoped_lock lock(_p->queue_mutex); _p->transitions_queue.push_back(trans); } _p->queue_cv.notify_one(); @@ -114,7 +117,7 @@ void FileLogger2::writerLoop() while(!transitions.empty()) { const auto trans = transitions.front(); - std::array write_buffer; + std::array write_buffer{}; std::memcpy(write_buffer.data(), &trans.timestamp_usec, 6); std::memcpy(write_buffer.data() + 6, &trans.node_uid, 2); std::memcpy(write_buffer.data() + 8, &trans.status, 1); diff --git a/src/loggers/bt_minitrace_logger.cpp b/src/loggers/bt_minitrace_logger.cpp index ad2c9ec86..5b53a68a0 100644 --- a/src/loggers/bt_minitrace_logger.cpp +++ b/src/loggers/bt_minitrace_logger.cpp @@ -21,6 +21,8 @@ MinitraceLogger::~MinitraceLogger() mtr_shutdown(); } +namespace +{ const char* toConstStr(NodeType type) { switch(type) @@ -39,6 +41,7 @@ const char* toConstStr(NodeType type) return "Undefined"; } } +} // namespace void MinitraceLogger::callback(Duration /*timestamp*/, const TreeNode& node, NodeStatus prev_status, NodeStatus status) diff --git a/src/loggers/bt_sqlite_logger.cpp b/src/loggers/bt_sqlite_logger.cpp index 86038912f..d26d41766 100644 --- a/src/loggers/bt_sqlite_logger.cpp +++ b/src/loggers/bt_sqlite_logger.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace BT { @@ -13,11 +14,11 @@ namespace void execSQL(sqlite3* db, const std::string& sql) { char* err_msg = nullptr; - int rc = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &err_msg); + const int rc = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &err_msg); if(rc != SQLITE_OK) { std::string error = "SQL error: "; - if(err_msg) + if(err_msg != nullptr) { error += err_msg; sqlite3_free(err_msg); @@ -30,7 +31,7 @@ void execSQL(sqlite3* db, const std::string& sql) sqlite3_stmt* prepareStatement(sqlite3* db, const std::string& sql) { sqlite3_stmt* stmt = nullptr; - int rc = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr); + const int rc = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr); if(rc != SQLITE_OK) { throw RuntimeError(std::string("Failed to prepare statement: ") + sqlite3_errmsg(db)); @@ -41,7 +42,7 @@ sqlite3_stmt* prepareStatement(sqlite3* db, const std::string& sql) // Helper function to execute a prepared statement void execStatement(sqlite3_stmt* stmt) { - int rc = sqlite3_step(stmt); + const int rc = sqlite3_step(stmt); if(rc != SQLITE_DONE && rc != SQLITE_ROW) { throw RuntimeError(std::string("Failed to execute statement: ") + std::to_string(rc)); @@ -64,7 +65,7 @@ SqliteLogger::SqliteLogger(const Tree& tree, std::filesystem::path const& filepa enableTransitionToIdle(true); // Open database - int rc = sqlite3_open(filepath.string().c_str(), &db_); + const int rc = sqlite3_open(filepath.string().c_str(), &db_); if(rc != SQLITE_OK) { throw RuntimeError(std::string("Cannot open database: ") + sqlite3_errmsg(db_)); @@ -129,11 +130,18 @@ SqliteLogger::SqliteLogger(const Tree& tree, std::filesystem::path const& filepa SqliteLogger::~SqliteLogger() { - loop_ = false; - queue_cv_.notify_one(); - writer_thread_.join(); - flush(); - execSQL(db_, "PRAGMA optimize;"); + try + { + loop_ = false; + queue_cv_.notify_one(); + writer_thread_.join(); + flush(); + execSQL(db_, "PRAGMA optimize;"); + } + catch(const std::exception& ex) + { + std::cerr << "Exception in ~SqliteLogger(): " << ex.what() << std::endl; + } sqlite3_close(db_); } @@ -146,7 +154,7 @@ void SqliteLogger::callback(Duration timestamp, const TreeNode& node, NodeStatus prev_status, NodeStatus status) { using namespace std::chrono; - int64_t tm_usec = int64_t(duration_cast(timestamp).count()); + const int64_t tm_usec = int64_t(duration_cast(timestamp).count()); monotonic_timestamp_ = std::max(monotonic_timestamp_ + 1, tm_usec); long elapsed_time = 0; @@ -178,7 +186,7 @@ void SqliteLogger::callback(Duration timestamp, const TreeNode& node, } { - std::scoped_lock lk(queue_mutex_); + const std::scoped_lock lk(queue_mutex_); transitions_queue_.push_back(trans); } queue_cv_.notify_one(); diff --git a/src/loggers/groot2_publisher.cpp b/src/loggers/groot2_publisher.cpp index f6f0afd0d..847ab5f9b 100644 --- a/src/loggers/groot2_publisher.cpp +++ b/src/loggers/groot2_publisher.cpp @@ -1,6 +1,7 @@ #include "behaviortree_cpp/loggers/groot2_publisher.h" #include "behaviortree_cpp/loggers/groot2_protocol.h" #include "behaviortree_cpp/xml_parsing.h" +#include #include "zmq_addon.hpp" namespace BT @@ -29,19 +30,23 @@ struct Transition uint8_t padding[5]; }; +namespace +{ std::array CreateRandomUUID() { - std::mt19937 gen; + std::random_device rd; + std::mt19937 gen(rd()); std::uniform_int_distribution dist; - std::array out; + std::array out{}; char* bytes = out.data(); for(int i = 0; i < 16; i += 4) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) *reinterpret_cast(bytes + i) = dist(gen); } // variant must be 10xxxxxx - bytes[8] &= 0xBF; - bytes[8] |= 0x80; + bytes[8] &= static_cast(0xBF); + bytes[8] |= static_cast(0x80); // version must be 0100xxxx bytes[6] &= 0x4F; @@ -49,6 +54,7 @@ std::array CreateRandomUUID() return out; } +} // namespace struct Groot2Publisher::PImpl { @@ -57,11 +63,11 @@ struct Groot2Publisher::PImpl server.set(zmq::sockopt::linger, 0); publisher.set(zmq::sockopt::linger, 0); - int timeout_rcv = 100; + const int timeout_rcv = 100; server.set(zmq::sockopt::rcvtimeo, timeout_rcv); publisher.set(zmq::sockopt::rcvtimeo, timeout_rcv); - int timeout_ms = 1000; + const int timeout_ms = 1000; server.set(zmq::sockopt::sndtimeo, timeout_ms); publisher.set(zmq::sockopt::sndtimeo, timeout_ms); } @@ -94,7 +100,7 @@ struct Groot2Publisher::PImpl std::atomic_bool recording = false; std::deque transitions_buffer; - std::chrono::microseconds recording_fist_time; + std::chrono::microseconds recording_fist_time{}; std::thread heartbeat_thread; @@ -109,7 +115,7 @@ Groot2Publisher::Groot2Publisher(const BT::Tree& tree, unsigned server_port) _p->server_port = server_port; { - std::unique_lock lk(Groot2Publisher::used_ports_mutex); + const std::unique_lock lk(Groot2Publisher::used_ports_mutex); if(Groot2Publisher::used_ports.count(server_port) != 0 || Groot2Publisher::used_ports.count(server_port + 1) != 0) { @@ -189,7 +195,7 @@ Groot2Publisher::~Groot2Publisher() flush(); { - std::unique_lock lk(Groot2Publisher::used_ports_mutex); + const std::unique_lock lk(Groot2Publisher::used_ports_mutex); Groot2Publisher::used_ports.erase(_p->server_port); Groot2Publisher::used_ports.erase(_p->server_port + 1); } @@ -198,18 +204,18 @@ Groot2Publisher::~Groot2Publisher() void Groot2Publisher::callback(Duration ts, const TreeNode& node, NodeStatus prev_status, NodeStatus new_status) { - std::unique_lock lk(_p->status_mutex); + const std::unique_lock lk(_p->status_mutex); auto status = static_cast(new_status); if(new_status == NodeStatus::IDLE) { - status = 10 + static_cast(prev_status); + status = static_cast(10 + static_cast(prev_status)); } *(_p->status_buffermap.at(node.UID())) = status; if(_p->recording) { - Transition trans; + Transition trans{}; trans.node_uid = node.UID(); trans.status = static_cast(new_status); auto timestamp = ts - _p->recording_fist_time; @@ -280,7 +286,7 @@ void Groot2Publisher::serverLoop() break; case Monitor::RequestType::STATUS: { - std::unique_lock lk(_p->status_mutex); + const std::unique_lock lk(_p->status_mutex); reply_msg.addstr(_p->status_buffer); } break; @@ -311,7 +317,7 @@ void Groot2Publisher::serverLoop() if(auto hook = getHook(pos, node_uid)) { std::unique_lock lk(hook->mutex); - bool was_interactive = (hook->mode == Monitor::Hook::Mode::BREAKPOINT); + const bool was_interactive = (hook->mode == Monitor::Hook::Mode::BREAKPOINT); BT::Monitor::from_json(json, *hook); // if it WAS interactive and it is not anymore, unlock it @@ -355,10 +361,10 @@ void Groot2Publisher::serverLoop() } auto json = nlohmann::json::parse(requestMsg[1].to_string()); - uint16_t node_uid = json.at("uid").get(); - std::string status_str = json.at("desired_status").get(); + const uint16_t node_uid = json.at("uid").get(); + const std::string status_str = json.at("desired_status").get(); auto position = static_cast(json.at("position").get()); - bool remove = json.at("remove_when_done").get(); + const bool remove = json.at("remove_when_done").get(); NodeStatus desired_status = NodeStatus::SKIPPED; if(status_str == "SUCCESS") @@ -370,7 +376,7 @@ void Groot2Publisher::serverLoop() desired_status = NodeStatus::FAILURE; } - if(!unlockBreakpoint(position, uint16_t(node_uid), desired_status, remove)) + if(!unlockBreakpoint(position, node_uid, desired_status, remove)) { sendErrorReply("Node ID not found"); continue; @@ -396,10 +402,10 @@ void Groot2Publisher::serverLoop() } auto json = nlohmann::json::parse(requestMsg[1].to_string()); - uint16_t node_uid = json.at("uid").get(); + const uint16_t node_uid = json.at("uid").get(); auto position = static_cast(json.at("position").get()); - if(!removeHook(position, uint16_t(node_uid))) + if(!removeHook(position, node_uid)) { sendErrorReply("Node ID not found"); continue; @@ -408,10 +414,11 @@ void Groot2Publisher::serverLoop() break; case Monitor::RequestType::HOOKS_DUMP: { - std::unique_lock lk(_p->hooks_map_mutex); + const std::unique_lock lk(_p->hooks_map_mutex); auto json_out = nlohmann::json::array(); - for(auto [node_uid, breakpoint] : _p->pre_hooks) + for(const auto& [node_uid, breakpoint] : _p->pre_hooks) { + std::ignore = node_uid; // unused in this loop json_out.push_back(*breakpoint); } reply_msg.addstr(json_out.dump()); @@ -436,7 +443,7 @@ void Groot2Publisher::serverLoop() auto now = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()); reply_msg.addstr(std::to_string(now.count())); - std::unique_lock lk(_p->status_mutex); + const std::unique_lock lk(_p->status_mutex); _p->transitions_buffer.clear(); } else if(cmd == "stop") @@ -450,7 +457,7 @@ void Groot2Publisher::serverLoop() thread_local std::string trans_buffer; trans_buffer.resize(9 * _p->transitions_buffer.size()); - std::unique_lock lk(_p->status_mutex); + const std::unique_lock lk(_p->status_mutex); size_t offset = 0; for(const auto& trans : _p->transitions_buffer) { @@ -479,9 +486,10 @@ void Groot2Publisher::serverLoop() void BT::Groot2Publisher::enableAllHooks(bool enable) { - std::unique_lock lk(_p->hooks_map_mutex); - for(auto& [node_uid, hook] : _p->pre_hooks) + const std::unique_lock lk(_p->hooks_map_mutex); + for(const auto& [node_uid, hook] : _p->pre_hooks) { + std::ignore = node_uid; // unused in this loop std::unique_lock lk(hook->mutex); hook->enabled = enable; // when disabling, remember to wake up blocked ones @@ -502,7 +510,7 @@ void Groot2Publisher::heartbeatLoop() std::this_thread::sleep_for(std::chrono::milliseconds(10)); auto now = std::chrono::system_clock::now(); - bool prev_heartbeat = has_heartbeat; + const bool prev_heartbeat = has_heartbeat; has_heartbeat = (now - _p->last_heartbeat < _p->max_heartbeat_delay); @@ -543,7 +551,7 @@ bool Groot2Publisher::insertHook(std::shared_ptr hook) { return false; } - TreeNode::Ptr node = it->second.lock(); + const TreeNode::Ptr node = it->second.lock(); if(!node) { return false; @@ -557,7 +565,7 @@ bool Groot2Publisher::insertHook(std::shared_ptr hook) } // Notify that a breakpoint was reached, using the _p->publisher - Monitor::RequestHeader breakpoint_request(Monitor::BREAKPOINT_REACHED); + const Monitor::RequestHeader breakpoint_request(Monitor::BREAKPOINT_REACHED); zmq::multipart_t request_msg; request_msg.addstr(Monitor::SerializeHeader(breakpoint_request)); request_msg.addstr(std::to_string(hook->node_uid)); @@ -580,14 +588,14 @@ bool Groot2Publisher::insertHook(std::shared_ptr hook) if(hook->remove_when_done) { // self-destruction at the end of this lambda function - std::unique_lock lk(_p->hooks_map_mutex); + const std::unique_lock lk(_p->hooks_map_mutex); _p->pre_hooks.erase(hook->node_uid); node.setPreTickFunction({}); } return hook->desired_status; }; - std::unique_lock lk(_p->hooks_map_mutex); + const std::unique_lock lk(_p->hooks_map_mutex); _p->pre_hooks[node_uid] = hook; node->setPreTickFunction(injectedCallback); @@ -602,7 +610,7 @@ bool Groot2Publisher::unlockBreakpoint(Position pos, uint16_t node_uid, NodeStat { return false; } - TreeNode::Ptr node = it->second.lock(); + const TreeNode::Ptr node = it->second.lock(); if(!node) { return false; @@ -635,7 +643,7 @@ bool Groot2Publisher::removeHook(Position pos, uint16_t node_uid) { return false; } - TreeNode::Ptr node = it->second.lock(); + const TreeNode::Ptr node = it->second.lock(); if(!node) { return false; @@ -648,7 +656,7 @@ bool Groot2Publisher::removeHook(Position pos, uint16_t node_uid) } { - std::unique_lock lk(_p->hooks_map_mutex); + const std::unique_lock lk(_p->hooks_map_mutex); _p->pre_hooks.erase(node_uid); } node->setPreTickFunction({}); @@ -673,13 +681,14 @@ void Groot2Publisher::removeAllHooks() for(auto pos : { Position::PRE, Position::POST }) { uids.clear(); - auto hooks = pos == Position::PRE ? &_p->pre_hooks : &_p->post_hooks; + auto* hooks = pos == Position::PRE ? &_p->pre_hooks : &_p->post_hooks; std::unique_lock lk(_p->hooks_map_mutex); if(!hooks->empty()) { uids.reserve(hooks->size()); - for(auto [node_uid, _] : *hooks) + for(const auto& [node_uid, hook_ptr] : *hooks) { + std::ignore = hook_ptr; // unused in this loop uids.push_back(node_uid); } @@ -694,8 +703,8 @@ void Groot2Publisher::removeAllHooks() Monitor::Hook::Ptr Groot2Publisher::getHook(Position pos, uint16_t node_uid) { - auto hooks = pos == Position::PRE ? &_p->pre_hooks : &_p->post_hooks; - std::unique_lock lk(_p->hooks_map_mutex); + auto* hooks = pos == Position::PRE ? &_p->pre_hooks : &_p->post_hooks; + const std::unique_lock lk(_p->hooks_map_mutex); auto bk_it = hooks->find(node_uid); if(bk_it == hooks->end()) { diff --git a/src/script_parser.cpp b/src/script_parser.cpp index 95c629fa2..45ac22aa3 100644 --- a/src/script_parser.cpp +++ b/src/script_parser.cpp @@ -23,7 +23,7 @@ Expected ParseScript(const std::string& script) { try { - std::vector exprs = LEXY_MOV(result).value(); + const std::vector exprs = LEXY_MOV(result).value(); if(exprs.empty()) { return nonstd::make_unexpected("Empty Script"); @@ -32,7 +32,7 @@ Expected ParseScript(const std::string& script) return [exprs, script](Ast::Environment& env) -> Any { try { - for(auto i = 0u; i < exprs.size() - 1; ++i) + for(auto i = 0U; i < exprs.size() - 1; ++i) { exprs[i]->evaluate(env); } @@ -49,10 +49,7 @@ Expected ParseScript(const std::string& script) return nonstd::make_unexpected(err.what()); } } - else - { - return nonstd::make_unexpected(error_msgs_buffer); - } + return nonstd::make_unexpected(error_msgs_buffer); } BT::Expected ParseScriptAndExecute(Ast::Environment& env, const std::string& script) @@ -62,10 +59,8 @@ BT::Expected ParseScriptAndExecute(Ast::Environment& env, const std::string { return executor.value()(env); } - else // forward the error - { - return nonstd::make_unexpected(executor.error()); - } + // forward the error + return nonstd::make_unexpected(executor.error()); } Result ValidateScript(const std::string& script) @@ -80,7 +75,7 @@ Result ValidateScript(const std::string& script) { try { - std::vector exprs = LEXY_MOV(result).value(); + const std::vector exprs = LEXY_MOV(result).value(); if(exprs.empty()) { return nonstd::make_unexpected("Empty Script"); diff --git a/src/shared_library.cpp b/src/shared_library.cpp index d86c961d3..29a79f1d7 100644 --- a/src/shared_library.cpp +++ b/src/shared_library.cpp @@ -9,10 +9,11 @@ BT::SharedLibrary::SharedLibrary(const std::string& path, int flags) void* BT::SharedLibrary::getSymbol(const std::string& name) { void* result = findSymbol(name); - if(result) + if(result != nullptr) + { return result; - else - throw RuntimeError("[SharedLibrary::getSymbol]: can't find symbol ", name); + } + throw RuntimeError("[SharedLibrary::getSymbol]: can't find symbol ", name); } bool BT::SharedLibrary::hasSymbol(const std::string& name) diff --git a/src/shared_library_UNIX.cpp b/src/shared_library_UNIX.cpp index 90ee04f71..ea6da8ebd 100644 --- a/src/shared_library_UNIX.cpp +++ b/src/shared_library_UNIX.cpp @@ -6,34 +6,32 @@ namespace BT { -SharedLibrary::SharedLibrary() -{ - _handle = nullptr; -} +SharedLibrary::SharedLibrary() = default; void SharedLibrary::load(const std::string& path, int) { - std::unique_lock lock(_mutex); + const std::unique_lock lock(_mutex); - if(_handle) + if(_handle != nullptr) { throw RuntimeError("Library already loaded: " + path); } _handle = dlopen(path.c_str(), RTLD_NOW | RTLD_GLOBAL); - if(!_handle) + if(_handle == nullptr) { const char* err = dlerror(); - throw RuntimeError("Could not load library: " + (err ? std::string(err) : path)); + throw RuntimeError("Could not load library: " + + (err != nullptr ? std::string(err) : path)); } _path = path; } void SharedLibrary::unload() { - std::unique_lock lock(_mutex); + const std::unique_lock lock(_mutex); - if(_handle) + if(_handle != nullptr) { dlclose(_handle); _handle = nullptr; @@ -47,10 +45,10 @@ bool SharedLibrary::isLoaded() const void* SharedLibrary::findSymbol(const std::string& name) { - std::unique_lock lock(_mutex); + const std::unique_lock lock(_mutex); void* result = nullptr; - if(_handle) + if(_handle != nullptr) { result = dlsym(_handle, name.c_str()); } diff --git a/src/tree_node.cpp b/src/tree_node.cpp index fb5e4ebab..908f23418 100644 --- a/src/tree_node.cpp +++ b/src/tree_node.cpp @@ -54,10 +54,8 @@ TreeNode::TreeNode(std::string name, NodeConfig config) : _p(new PImpl(std::move(name), std::move(config))) {} -TreeNode::TreeNode(TreeNode&& other) noexcept -{ - this->_p = std::move(other._p); -} +TreeNode::TreeNode(TreeNode&& other) noexcept : _p(std::move(other._p)) +{} TreeNode& TreeNode::operator=(TreeNode&& other) noexcept { @@ -65,8 +63,7 @@ TreeNode& TreeNode::operator=(TreeNode&& other) noexcept return *this; } -TreeNode::~TreeNode() -{} +TreeNode::~TreeNode() = default; NodeStatus TreeNode::executeTick() { @@ -75,7 +72,7 @@ NodeStatus TreeNode::executeTick() PostTickCallback post_tick; TickMonitorCallback monitor_tick; { - std::scoped_lock lk(_p->callback_injection_mutex); + const std::scoped_lock lk(_p->callback_injection_mutex); pre_tick = _p->pre_tick_callback; post_tick = _p->post_tick_callback; monitor_tick = _p->tick_monitor_callback; @@ -160,9 +157,9 @@ void TreeNode::setStatus(NodeStatus new_status) "If you know what you are doing (?) use resetStatus() instead."); } - NodeStatus prev_status; + NodeStatus prev_status = NodeStatus::IDLE; { - std::unique_lock UniqueLock(_p->state_mutex); + const std::unique_lock UniqueLock(_p->state_mutex); prev_status = _p->status; _p->status = new_status; } @@ -197,7 +194,7 @@ Expected TreeNode::checkPreConditions() continue; } - const PreCond preID = PreCond(index); + const auto preID = static_cast(index); // Some preconditions are applied only when the node state is IDLE or SKIPPED if(_p->status == NodeStatus::IDLE || _p->status == NodeStatus::SKIPPED) @@ -209,11 +206,11 @@ Expected TreeNode::checkPreConditions() { return NodeStatus::FAILURE; } - else if(preID == PreCond::SUCCESS_IF) + if(preID == PreCond::SUCCESS_IF) { return NodeStatus::SUCCESS; } - else if(preID == PreCond::SKIP_IF) + if(preID == PreCond::SKIP_IF) { return NodeStatus::SKIPPED; } @@ -261,9 +258,9 @@ void TreeNode::checkPostConditions(NodeStatus status) void TreeNode::resetStatus() { - NodeStatus prev_status; + NodeStatus prev_status = NodeStatus::IDLE; { - std::unique_lock lock(_p->state_mutex); + const std::unique_lock lock(_p->state_mutex); prev_status = _p->status; _p->status = NodeStatus::IDLE; } @@ -278,7 +275,7 @@ void TreeNode::resetStatus() NodeStatus TreeNode::status() const { - std::lock_guard lock(_p->state_mutex); + const std::lock_guard lock(_p->state_mutex); return _p->status; } @@ -311,20 +308,20 @@ TreeNode::subscribeToStatusChange(TreeNode::StatusChangeCallback callback) void TreeNode::setPreTickFunction(PreTickCallback callback) { - std::unique_lock lk(_p->callback_injection_mutex); - _p->pre_tick_callback = callback; + const std::unique_lock lk(_p->callback_injection_mutex); + _p->pre_tick_callback = std::move(callback); } void TreeNode::setPostTickFunction(PostTickCallback callback) { - std::unique_lock lk(_p->callback_injection_mutex); - _p->post_tick_callback = callback; + const std::unique_lock lk(_p->callback_injection_mutex); + _p->post_tick_callback = std::move(callback); } void TreeNode::setTickMonitorCallback(TickMonitorCallback callback) { - std::unique_lock lk(_p->callback_injection_mutex); - _p->tick_monitor_callback = callback; + const std::unique_lock lk(_p->callback_injection_mutex); + _p->tick_monitor_callback = std::move(callback); } uint16_t TreeNode::UID() const @@ -385,7 +382,7 @@ bool TreeNode::isBlackboardPointer(StringView str, StringView* stripped_pointer) } const auto size = (last_index - front_index) + 1; auto valid = size >= 3 && str[front_index] == '{' && str[last_index] == '}'; - if(valid && stripped_pointer) + if(valid && stripped_pointer != nullptr) { *stripped_pointer = StringView(&str[front_index + 1], size - 2); } diff --git a/src/xml_parsing.cpp b/src/xml_parsing.cpp index dbdac5ea3..a094fea22 100644 --- a/src/xml_parsing.cpp +++ b/src/xml_parsing.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "behaviortree_cpp/basic_types.h" @@ -85,9 +86,12 @@ namespace BT { using namespace tinyxml2; +namespace +{ auto StrEqual = [](const char* str1, const char* str2) -> bool { return strcmp(str1, str2) == 0; }; +} // namespace struct SubtreeModel { @@ -114,15 +118,15 @@ struct XMLParser::PImpl std::list > opened_documents; std::map tree_roots; - const BehaviorTreeFactory& factory; + const BehaviorTreeFactory* factory = nullptr; std::filesystem::path current_path; std::map subtree_models; - int suffix_count; + int suffix_count = 0; explicit PImpl(const BehaviorTreeFactory& fact) - : factory(fact), current_path(std::filesystem::current_path()), suffix_count(0) + : factory(&fact), current_path(std::filesystem::current_path()) {} void clear() @@ -144,10 +148,8 @@ struct XMLParser::PImpl XMLParser::XMLParser(const BehaviorTreeFactory& factory) : _p(new PImpl(factory)) {} -XMLParser::XMLParser(XMLParser&& other) noexcept -{ - this->_p = std::move(other._p); -} +XMLParser::XMLParser(XMLParser&& other) noexcept : _p(std::move(other._p)) +{} XMLParser& XMLParser::operator=(XMLParser&& other) noexcept { @@ -160,7 +162,7 @@ XMLParser::~XMLParser() void XMLParser::loadFromFile(const std::filesystem::path& filepath, bool add_includes) { - _p->opened_documents.emplace_back(new XMLDocument()); + _p->opened_documents.push_back(std::make_unique()); XMLDocument* doc = _p->opened_documents.back().get(); doc->LoadFile(filepath.string().c_str()); @@ -172,7 +174,7 @@ void XMLParser::loadFromFile(const std::filesystem::path& filepath, bool add_inc void XMLParser::loadFromText(const std::string& xml_text, bool add_includes) { - _p->opened_documents.emplace_back(new XMLDocument()); + _p->opened_documents.push_back(std::make_unique()); XMLDocument* doc = _p->opened_documents.back().get(); doc->Parse(xml_text.c_str(), xml_text.size()); @@ -202,7 +204,7 @@ void BT::XMLParser::PImpl::loadSubtreeModel(const XMLElement* xml_root) auto subtree_id = sub_node->Attribute("ID"); auto& subtree_model = subtree_models[subtree_id]; - std::pair port_types[3] = { + const std::pair port_types[3] = { { "input_port", BT::PortDirection::INPUT }, { "output_port", BT::PortDirection::OUTPUT }, { "inout_port", BT::PortDirection::INOUT } @@ -215,7 +217,7 @@ void BT::XMLParser::PImpl::loadSubtreeModel(const XMLElement* xml_root) { BT::PortInfo port(direction); auto name = port_node->Attribute("name"); - if(!name) + if(name == nullptr) { throw RuntimeError("Missing attribute [name] in port (SubTree model)"); } @@ -239,18 +241,19 @@ void XMLParser::PImpl::loadDocImpl(XMLDocument* doc, bool add_includes) if(doc->Error()) { char buffer[512]; - snprintf(buffer, sizeof buffer, "Error parsing the XML: %s", doc->ErrorStr()); + std::ignore = + snprintf(buffer, sizeof buffer, "Error parsing the XML: %s", doc->ErrorStr()); throw RuntimeError(buffer); } const XMLElement* xml_root = doc->RootElement(); - if(!xml_root) + if(xml_root == nullptr) { throw RuntimeError("Invalid XML: missing root element"); } auto format = xml_root->Attribute("BTCPP_format"); - if(!format) + if(format == nullptr) { std::cout << "Warnings: The first tag of the XML () should contain the " "attribute [BTCPP_format=\"4\"]\n" @@ -268,7 +271,7 @@ void XMLParser::PImpl::loadDocImpl(XMLDocument* doc, bool add_includes) } const char* path_attr = incl_node->Attribute("path"); - if(!path_attr) + if(path_attr == nullptr) { throw RuntimeError("Invalid tag: missing 'path' attribute"); } @@ -281,7 +284,7 @@ void XMLParser::PImpl::loadDocImpl(XMLDocument* doc, bool add_includes) const char* ros_pkg_relative_path = incl_node->Attribute("ros_pkg"); - if(ros_pkg_relative_path) + if(ros_pkg_relative_path != nullptr) { if(file_path.is_absolute()) { @@ -290,7 +293,7 @@ void XMLParser::PImpl::loadDocImpl(XMLDocument* doc, bool add_includes) } else { - std::string ros_pkg_path; + std::string ros_pkg_path; // NOLINT(misc-const-correctness) #if defined USING_ROS2 ros_pkg_path = ament_index_cpp::get_package_share_directory(ros_pkg_relative_path); @@ -308,7 +311,7 @@ void XMLParser::PImpl::loadDocImpl(XMLDocument* doc, bool add_includes) file_path = current_path / file_path; } - opened_documents.emplace_back(new XMLDocument()); + opened_documents.push_back(std::make_unique()); XMLDocument* next_doc = opened_documents.back().get(); // change current path to the included file for handling additional relative paths @@ -324,7 +327,7 @@ void XMLParser::PImpl::loadDocImpl(XMLDocument* doc, bool add_includes) // Collect the names of all nodes registered with the behavior tree factory std::unordered_map registered_nodes; - for(const auto& it : factory.manifests()) + for(const auto& it : factory->manifests()) { registered_nodes.insert({ it.first, it.second.type }); } @@ -343,7 +346,7 @@ void XMLParser::PImpl::loadDocImpl(XMLDocument* doc, bool add_includes) bt_node = bt_node->NextSiblingElement("BehaviorTree")) { std::string tree_name; - if(bt_node->Attribute("ID")) + if(bt_node->Attribute("ID") != nullptr) { tree_name = bt_node->Attribute("ID"); } @@ -361,17 +364,19 @@ void VerifyXML(const std::string& xml_text, { XMLDocument doc; auto xml_error = doc.Parse(xml_text.c_str(), xml_text.size()); - if(xml_error) + if(xml_error != tinyxml2::XML_SUCCESS) { char buffer[512]; - snprintf(buffer, sizeof buffer, "Error parsing the XML: %s", doc.ErrorName()); + std::ignore = + snprintf(buffer, sizeof buffer, "Error parsing the XML: %s", doc.ErrorName()); throw RuntimeError(buffer); } //-------- Helper functions (lambdas) ----------------- auto ThrowError = [&](int line_num, const std::string& text) { char buffer[512]; - snprintf(buffer, sizeof buffer, "Error at line %d: -> %s", line_num, text.c_str()); + std::ignore = snprintf(buffer, sizeof buffer, "Error at line %d: -> %s", line_num, + text.c_str()); throw RuntimeError(buffer); }; @@ -388,21 +393,22 @@ void VerifyXML(const std::string& xml_text, const XMLElement* xml_root = doc.RootElement(); - if(!xml_root || !StrEqual(xml_root->Name(), "root")) + if(xml_root == nullptr || !StrEqual(xml_root->Name(), "root")) { throw RuntimeError("The XML must have a root node called "); } //------------------------------------------------- auto models_root = xml_root->FirstChildElement("TreeNodesModel"); - auto meta_sibling = - models_root ? models_root->NextSiblingElement("TreeNodesModel") : nullptr; + auto meta_sibling = models_root != nullptr ? models_root->NextSiblingElement("TreeNodes" + "Model") : + nullptr; - if(meta_sibling) + if(meta_sibling != nullptr) { ThrowError(meta_sibling->GetLineNum(), " Only a single node is " "supported"); } - if(models_root) + if(models_root != nullptr) { // not having a MetaModel is not an error. But consider that the // Graphical editor needs it. @@ -414,7 +420,7 @@ void VerifyXML(const std::string& xml_text, name == "Condition" || name == "Control") { const char* ID = node->Attribute("ID"); - if(!ID) + if(ID == nullptr) { ThrowError(node->GetLineNum(), "Error at line %d: -> The attribute " "[ID] is mandatory"); @@ -437,7 +443,7 @@ void VerifyXML(const std::string& xml_text, recursiveStep = [&](const XMLElement* node) { const int children_count = ChildrenCount(node); const std::string name = node->Name(); - const std::string ID = node->Attribute("ID") ? node->Attribute("ID") : ""; + const std::string ID = node->Attribute("ID") != nullptr ? node->Attribute("ID") : ""; const int line_number = node->GetLineNum(); // Precondition: built-in XML element types must define attribute [ID] @@ -485,7 +491,7 @@ void VerifyXML(const std::string& xml_text, // use ID for builtin node types, otherwise use the element name const auto lookup_name = is_builtin ? ID : name; const auto search = registered_nodes.find(lookup_name); - bool found = (search != registered_nodes.end()); + const bool found = (search != registered_nodes.end()); if(!found) { ThrowError(line_number, std::string("Node not recognized: ") + lookup_name); @@ -538,15 +544,7 @@ void VerifyXML(const std::string& xml_text, } } } - else if(node_type == NodeType::ACTION) - { - if(children_count != 0) - { - ThrowError(line_number, std::string("The node '") + registered_name + - "' must not have any child"); - } - } - else if(node_type == NodeType::CONDITION) + else if(node_type == NodeType::ACTION || node_type == NodeType::CONDITION) { if(children_count != 0) { @@ -631,13 +629,13 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element, { // This is the case of nodes like // check if the factory has this name - if(factory.builders().count(element_name) == 0) + if(factory->builders().count(element_name) == 0) { throw RuntimeError(element_name, " is not a registered node"); } type_ID = element_name; - if(element_ID) + if(element_ID != nullptr) { throw RuntimeError("Attribute [ID] is not allowed in <", type_ID, ">"); } @@ -645,7 +643,7 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element, else { // in this case, it is mandatory to have a field "ID" - if(!element_ID) + if(element_ID == nullptr) { throw RuntimeError("Attribute [ID] is mandatory in <", type_ID, ">"); } @@ -659,8 +657,8 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element, const TreeNodeManifest* manifest = nullptr; - auto manifest_it = factory.manifests().find(type_ID); - if(manifest_it != factory.manifests().end()) + auto manifest_it = factory->manifests().find(type_ID); + if(manifest_it != factory->manifests().end()) { manifest = &manifest_it->second; } @@ -668,7 +666,8 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element, PortsRemapping port_remap; NonPortAttributes other_attributes; - for(const XMLAttribute* att = element->FirstAttribute(); att; att = att->Next()) + for(const XMLAttribute* att = element->FirstAttribute(); att != nullptr; + att = att->Next()) { const std::string port_name = att->Name(); const std::string port_value = att->Value(); @@ -677,7 +676,7 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element, const std::string port_name = att->Name(); const std::string port_value = att->Value(); - if(manifest) + if(manifest != nullptr) { auto port_model_it = manifest->ports.find(port_name); if(port_model_it == manifest->ports.end()) @@ -691,10 +690,11 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element, else { const auto& port_model = port_model_it->second; - bool is_blacbkboard = port_value.size() >= 3 && port_value.front() == '{' && - port_value.back() == '}'; + const bool is_blackboard = port_value.size() >= 3 && + port_value.front() == '{' && + port_value.back() == '}'; // let's test already if conversion is possible - if(!is_blacbkboard && port_model.converter() && port_model.isStronglyTyped()) + if(!is_blackboard && port_model.converter() && port_model.isStronglyTyped()) { // This may throw try @@ -758,13 +758,13 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element, { config.input_ports = port_remap; new_node = - factory.instantiateTreeNode(instance_name, toStr(NodeType::SUBTREE), config); + factory->instantiateTreeNode(instance_name, toStr(NodeType::SUBTREE), config); auto subtree_node = dynamic_cast(new_node.get()); subtree_node->setSubtreeID(type_ID); } else { - if(!manifest) + if(manifest == nullptr) { auto msg = StrCat("Missing manifest for element_ID: ", element_ID, ". It shouldn't happen. Please report this issue."); @@ -772,8 +772,9 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element, } //Check that name in remapping can be found in the manifest - for(const auto& [name_in_subtree, _] : port_remap) + for(const auto& [name_in_subtree, remap_value] : port_remap) { + std::ignore = remap_value; // unused in this loop if(manifest->ports.count(name_in_subtree) == 0) { throw RuntimeError("Possible typo? In the XML, you tried to remap port \"", @@ -793,7 +794,7 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element, { continue; } - StringView remapped_port = remap_it->second; + const StringView remapped_port = remap_it->second; if(auto param_res = TreeNode::getRemappedKey(port_name, remapped_port)) { @@ -872,17 +873,17 @@ TreeNode::Ptr XMLParser::PImpl::createNodeFromXML(const XMLElement* element, } } - new_node = factory.instantiateTreeNode(instance_name, type_ID, config); + new_node = factory->instantiateTreeNode(instance_name, type_ID, config); } // add the pointer of this node to the parent if(node_parent != nullptr) { - if(auto control_parent = dynamic_cast(node_parent.get())) + if(auto* control_parent = dynamic_cast(node_parent.get())) { control_parent->addChild(new_node.get()); } - else if(auto decorator_parent = dynamic_cast(node_parent.get())) + else if(auto* decorator_parent = dynamic_cast(node_parent.get())) { decorator_parent->setChild(new_node.get()); } @@ -911,7 +912,7 @@ void BT::XMLParser::PImpl::recursivelyCreateSubtree(const std::string& tree_ID, // common case: iterate through all children if(node->type() != NodeType::SUBTREE) { - for(auto child_element = element->FirstChildElement(); child_element; + for(auto child_element = element->FirstChildElement(); child_element != nullptr; child_element = child_element->NextSiblingElement()) { recursiveStep(node, subtree, prefix, child_element); @@ -926,7 +927,7 @@ void BT::XMLParser::PImpl::recursivelyCreateSubtree(const std::string& tree_ID, for(auto attr = element->FirstAttribute(); attr != nullptr; attr = attr->Next()) { - std::string attr_name = attr->Name(); + const std::string attr_name = attr->Name(); std::string attr_value = attr->Value(); if(attr_value == "{=}") { @@ -982,7 +983,7 @@ void BT::XMLParser::PImpl::recursivelyCreateSubtree(const std::string& tree_ID, if(TreeNode::isBlackboardPointer(attr_value)) { // do remapping - StringView port_name = TreeNode::stripBlackboardPointer(attr_value); + const StringView port_name = TreeNode::stripBlackboardPointer(attr_value); new_bb->addSubtreeRemapping(attr_name, port_name); } else @@ -1051,13 +1052,15 @@ void XMLParser::PImpl::getPortsRecursively(const XMLElement* element, } } - for(auto child_element = element->FirstChildElement(); child_element; + for(auto child_element = element->FirstChildElement(); child_element != nullptr; child_element = child_element->NextSiblingElement()) { getPortsRecursively(child_element, output_ports); } } +namespace +{ void addNodeModelToXML(const TreeNodeManifest& model, XMLDocument& doc, XMLElement* model_root) { @@ -1121,7 +1124,7 @@ void addTreeToXML(const Tree& tree, XMLDocument& doc, XMLElement* rootXML, addNode = [&](const TreeNode& node, XMLElement* parent_elem) { XMLElement* elem = nullptr; - if(auto subtree = dynamic_cast(&node)) + if(const auto* subtree = dynamic_cast(&node)) { elem = doc.NewElement(node.registrationName().c_str()); elem->SetAttribute("ID", subtree->subtreeID().c_str()); @@ -1165,14 +1168,14 @@ void addTreeToXML(const Tree& tree, XMLDocument& doc, XMLElement* rootXML, parent_elem->InsertEndChild(elem); - if(auto control = dynamic_cast(&node)) + if(const auto* control = dynamic_cast(&node)) { for(const auto& child : control->children()) { addNode(*child, elem); } } - else if(auto decorator = dynamic_cast(&node)) + else if(const auto* decorator = dynamic_cast(&node)) { if(decorator->type() != NodeType::SUBTREE) { @@ -1198,7 +1201,7 @@ void addTreeToXML(const Tree& tree, XMLDocument& doc, XMLElement* rootXML, std::map ordered_models; for(const auto& [registration_ID, model] : tree.manifests) { - if(add_builtin_models || !temp_factory.builtinNodes().count(registration_ID)) + if(add_builtin_models || temp_factory.builtinNodes().count(registration_ID) == 0) { ordered_models.insert({ registration_ID, &model }); } @@ -1209,6 +1212,7 @@ void addTreeToXML(const Tree& tree, XMLDocument& doc, XMLElement* rootXML, addNodeModelToXML(*model, doc, model_root); } } +} // namespace std::string writeTreeNodesModelXML(const BehaviorTreeFactory& factory, bool include_builtin) @@ -1513,22 +1517,6 @@ std::string writeTreeXSD(const BehaviorTreeFactory& factory) return std::string(printer.CStr(), size_t(printer.CStrSize() - 1)); } -Tree buildTreeFromText(const BehaviorTreeFactory& factory, const std::string& text, - const Blackboard::Ptr& blackboard) -{ - XMLParser parser(factory); - parser.loadFromText(text); - return parser.instantiateTree(blackboard); -} - -Tree buildTreeFromFile(const BehaviorTreeFactory& factory, const std::string& filename, - const Blackboard::Ptr& blackboard) -{ - XMLParser parser(factory); - parser.loadFromFile(filename); - return parser.instantiateTree(blackboard); -} - std::string WriteTreeToXML(const Tree& tree, bool add_metadata, bool add_builtin_models) { XMLDocument doc; From 8aaddc87dc175124d705f4e5bdeeef8f48b381ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Dec 2025 23:44:48 +0100 Subject: [PATCH 051/147] Bump codecov/codecov-action from 3 to 5 (#1048) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 5. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v5) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/cmake_ubuntu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake_ubuntu.yml b/.github/workflows/cmake_ubuntu.yml index 1d4ba93f1..e94eac6d8 100644 --- a/.github/workflows/cmake_ubuntu.yml +++ b/.github/workflows/cmake_ubuntu.yml @@ -53,4 +53,4 @@ jobs: run: ctest --test-dir build/${{env.BUILD_TYPE}} - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v5 From 9bc29a84069137f9387dce03e342460b38009ca7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Dec 2025 23:45:05 +0100 Subject: [PATCH 052/147] Bump prefix-dev/setup-pixi from 0.8.1 to 0.9.3 (#1049) Bumps [prefix-dev/setup-pixi](https://github.com/prefix-dev/setup-pixi) from 0.8.1 to 0.9.3. - [Release notes](https://github.com/prefix-dev/setup-pixi/releases) - [Commits](https://github.com/prefix-dev/setup-pixi/compare/v0.8.1...v0.9.3) --- updated-dependencies: - dependency-name: prefix-dev/setup-pixi dependency-version: 0.9.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pixi.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pixi.yaml b/.github/workflows/pixi.yaml index ddd1cbfb8..93d5ad413 100644 --- a/.github/workflows/pixi.yaml +++ b/.github/workflows/pixi.yaml @@ -18,7 +18,7 @@ jobs: steps: # Pixi is the tool used to create/manage conda environment - uses: actions/checkout@v3 - - uses: prefix-dev/setup-pixi@v0.8.1 + - uses: prefix-dev/setup-pixi@v0.9.3 with: pixi-version: v0.40.3 - name: Build From 4ed7ef74d80e15142c70d70e8726ba609113aab1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Dec 2025 23:45:20 +0100 Subject: [PATCH 053/147] Bump actions/setup-python from 5 to 6 (#1051) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pre-commit.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 5414862c3..7eb19d3b7 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 - uses: pre-commit/action@v3.0.1 clang-tidy: From ff0830ae455048993174208347d1d5bcef8320c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Dec 2025 23:46:28 +0100 Subject: [PATCH 054/147] Bump actions/checkout from 2 to 6 (#1050) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Davide Faconti --- .github/workflows/cmake_ubuntu.yml | 2 +- .github/workflows/cmake_ubuntu_sanitizers.yml | 2 +- .github/workflows/cmake_windows.yml | 2 +- .github/workflows/pixi.yaml | 3 ++- .github/workflows/pre-commit.yaml | 4 ++-- .github/workflows/ros2-rolling.yaml | 2 +- .github/workflows/ros2.yaml | 2 +- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/cmake_ubuntu.yml b/.github/workflows/cmake_ubuntu.yml index e94eac6d8..ca4c87344 100644 --- a/.github/workflows/cmake_ubuntu.yml +++ b/.github/workflows/cmake_ubuntu.yml @@ -23,7 +23,7 @@ jobs: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 - name: Install Conan id: conan diff --git a/.github/workflows/cmake_ubuntu_sanitizers.yml b/.github/workflows/cmake_ubuntu_sanitizers.yml index c379171a1..337bc79bb 100644 --- a/.github/workflows/cmake_ubuntu_sanitizers.yml +++ b/.github/workflows/cmake_ubuntu_sanitizers.yml @@ -25,7 +25,7 @@ jobs: sanitizer: [asan_ubsan, tsan] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 - name: Install Conan id: conan diff --git a/.github/workflows/cmake_windows.yml b/.github/workflows/cmake_windows.yml index 5082acdc7..87ab18bdc 100644 --- a/.github/workflows/cmake_windows.yml +++ b/.github/workflows/cmake_windows.yml @@ -23,7 +23,7 @@ jobs: os: [windows-latest] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Conan id: conan diff --git a/.github/workflows/pixi.yaml b/.github/workflows/pixi.yaml index 93d5ad413..d198747e6 100644 --- a/.github/workflows/pixi.yaml +++ b/.github/workflows/pixi.yaml @@ -17,7 +17,8 @@ jobs: runs-on: ${{ matrix.os }} steps: # Pixi is the tool used to create/manage conda environment - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 + - uses: prefix-dev/setup-pixi@v0.8.1 - uses: prefix-dev/setup-pixi@v0.9.3 with: pixi-version: v0.40.3 diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 7eb19d3b7..c05f06d4b 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -11,14 +11,14 @@ jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: actions/setup-python@v6 - uses: pre-commit/action@v3.0.1 clang-tidy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install LLVM 21 run: | diff --git a/.github/workflows/ros2-rolling.yaml b/.github/workflows/ros2-rolling.yaml index 446c49879..15d3b06ce 100644 --- a/.github/workflows/ros2-rolling.yaml +++ b/.github/workflows/ros2-rolling.yaml @@ -15,7 +15,7 @@ jobs: - {ROS_DISTRO: rolling, ROS_REPO: main} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: 'ros-industrial/industrial_ci@master' env: ${{matrix.env}} with: diff --git a/.github/workflows/ros2.yaml b/.github/workflows/ros2.yaml index 099cc04f2..8fbbc25e6 100644 --- a/.github/workflows/ros2.yaml +++ b/.github/workflows/ros2.yaml @@ -16,7 +16,7 @@ jobs: - {ROS_DISTRO: jazzy, ROS_REPO: main} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: 'ros-industrial/industrial_ci@master' env: ${{matrix.env}} with: From 871848f37942f118534a3758f9deee5e66ffcbde Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Tue, 30 Dec 2025 10:06:14 +0100 Subject: [PATCH 055/147] release 4.8.3 --- CHANGELOG.rst | 23 +++++++++++++++++++++++ CMakeLists.txt | 2 +- package.xml | 2 +- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 22080f36b..a755d99fd 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,29 @@ Changelog for package behaviortree_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +4.8.3 (2025-12-29) +------------------ +* minor change +* remove nolint +* Entry should be non copyable +* miscellaneus +* fix +* run clang tidy in CI +* fix remaining warnings +* apply the rulke of 5 +* fix compilation in c++17 +* add clang tidy and fix warnings +* update copyright year +* add unit test +* fix multiple issues with SimpleString +* Merge pull request `#1043 `_ from uilianries/fix/cppzmq-visibility + [fix] Make cppzmq as public dependency to avoid linkage errors for tools +* Turn cppzmq dependency public +* Restore Star History and add Contributors section + Reintroduced the Star History section and added Contributors section to the README. +* Update copyright year in README.md +* Contributors: Davide Faconti, Uilian Ries + 4.8.2 (2025-10-30) ------------------ * Merge pull request `#996 `_ from EnjoyRobotics/make-sequence-node-inheritable diff --git a/CMakeLists.txt b/CMakeLists.txt index 888d56c56..278c67b33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16.3) # version on Ubuntu Focal -project(behaviortree_cpp VERSION 4.8.2 LANGUAGES C CXX) +project(behaviortree_cpp VERSION 4.8.3 LANGUAGES C CXX) # create compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/package.xml b/package.xml index 956dd1319..02a23e507 100644 --- a/package.xml +++ b/package.xml @@ -1,7 +1,7 @@ behaviortree_cpp - 4.8.2 + 4.8.3 This package provides the Behavior Trees core library. From 6e469c6ba133aaa842dac9b096b41f2d33ee2b0e Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Tue, 30 Dec 2025 10:08:13 +0100 Subject: [PATCH 056/147] fix pixi CI --- .github/workflows/pixi.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/pixi.yaml b/.github/workflows/pixi.yaml index d198747e6..368cac67f 100644 --- a/.github/workflows/pixi.yaml +++ b/.github/workflows/pixi.yaml @@ -18,7 +18,6 @@ jobs: steps: # Pixi is the tool used to create/manage conda environment - uses: actions/checkout@v6 - - uses: prefix-dev/setup-pixi@v0.8.1 - uses: prefix-dev/setup-pixi@v0.9.3 with: pixi-version: v0.40.3 From 5f51577ff92c1aef04289ea0e2ac1b2b143f689e Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Tue, 30 Dec 2025 10:38:24 +0100 Subject: [PATCH 057/147] add contributors guide --- .github/pull_request_template.md | 14 +++++++------- CONTRIBUTORS_GUIDE.md | 18 ++++++++++++++++++ run_clang_format.sh | 3 --- 3 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 CONTRIBUTORS_GUIDE.md delete mode 100755 run_clang_format.sh diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 27c6ae66a..052ed3a65 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,11 +1,11 @@ diff --git a/CONTRIBUTORS_GUIDE.md b/CONTRIBUTORS_GUIDE.md new file mode 100644 index 000000000..8fd3c9f6e --- /dev/null +++ b/CONTRIBUTORS_GUIDE.md @@ -0,0 +1,18 @@ +# Contributors Guide + +Before submitting a Pull Request, please follow these instructions: + +- Unless your code is self explaining, add comments. +- Consider if your proposed change introduces API, ABI, back-compatibility or behavioral changes. +- If your code is fixing a bug, please create a unit test to reproduce the bug, i.e. a test that fails before the fix and pass after the fix. +- You use [pre-commit](https://pre-commit.com/) to apply automatically all the required linting rules (clang-format in particular). +- You should also execute the script `./run_clang_tidy.sh` and correct all the warnings. + +You will need to install the latest **clang-tidy-21** as follows: + +``` + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 21 + sudo apt install clangd-21 clang-tidy-21 +``` diff --git a/run_clang_format.sh b/run_clang_format.sh deleted file mode 100755 index cf3d51a14..000000000 --- a/run_clang_format.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -find . -name '*.h' -or -name '*.hpp' -or -name '*.cpp' | xargs clang-format-3.8 -i -style=file $1 From 93f0f1ab47085fc9405e403be69c702f09f54183 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Tue, 30 Dec 2025 10:56:52 +0100 Subject: [PATCH 058/147] cleanup doc --- docs/PORT_CONNECTION_RULES.md | 130 +++------------------------------- 1 file changed, 11 insertions(+), 119 deletions(-) diff --git a/docs/PORT_CONNECTION_RULES.md b/docs/PORT_CONNECTION_RULES.md index 7eb353430..61a964875 100644 --- a/docs/PORT_CONNECTION_RULES.md +++ b/docs/PORT_CONNECTION_RULES.md @@ -21,26 +21,21 @@ InputPort("goal") ### 2. Generic/Weakly Typed Ports (AnyTypeAllowed) A port is **generic** (not strongly typed) when: -- Declared without a type parameter: `InputPort<>("my_port")` -- Declared with `AnyTypeAllowed`: `InputPort("my_port")` -- Declared with `BT::Any`: `InputPort("my_port")` +- Declared without a type parameter +- Declared with `AnyTypeAllowed` +- Declared with `BT::Any` +- Declared with `std::string` (**OoutputPort** only) ```cpp // All of these create generic ports: InputPort<>("value") // defaults to AnyTypeAllowed InputPort("value") // explicit AnyTypeAllowed InputPort("value") // BT::Any type +OoutputPort("value") // Can be connected to strong typed input ``` The `isStronglyTyped()` method returns `false` for these ports: -```cpp -// From basic_types.h -bool isStronglyTyped() const -{ - return type_info_ != typeid(AnyTypeAllowed) && type_info_ != typeid(BT::Any); -} -``` ## Port Connection Rules @@ -49,13 +44,9 @@ bool isStronglyTyped() const Ports of the **exact same type** can always be connected: ```cpp -// Node A +// Connection allowed OutputPort("value") // writes int - -// Node B InputPort("value") // reads int - -// Connection: OK ``` **Test reference:** `gtest_port_type_rules.cpp` - `SameType_IntToInt`, `SameType_StringToString`, `SameType_CustomTypeToCustomType` tests @@ -65,13 +56,9 @@ InputPort("value") // reads int A **generic port** (`AnyTypeAllowed` or `BT::Any`) can connect to any other port: ```cpp -// Node A with generic output +// Connection: OK - generic port accepts any type OutputPort<>("output") // generic, can write anything - -// Node B with typed input InputPort("input_int") // expects int - -// Connection: OK - generic port accepts any type ``` **Test reference:** `gtest_port_type_rules.cpp` - `GenericPort_AcceptsInt`, `GenericPort_AcceptsString`, `GenericOutput_ToTypedInput` tests @@ -80,24 +67,13 @@ InputPort("input_int") // expects int When a blackboard entry is created as `std::string`, it can be connected to ports of **any type** that has a `convertFromString()` specialization. This is the "string as generic port" rule. -**Source:** `xml_parsing.cpp` -```cpp -// special case related to convertFromString -bool const string_input = (prev_info->type() == typeid(std::string)); - -if(port_type_mismatch && !string_input) -{ - // Error thrown only if NOT a string input - throw RuntimeError("The creation of the tree failed..."); -} -``` +Note that this may cause a run-time error if the string is not convertible. **Example:** ```xml - @@ -113,15 +89,6 @@ if(port_type_mismatch && !string_input) When using `Blackboard::set()`, the entry is created with `AnyTypeAllowed` type, not `std::string`: -**Source:** `blackboard.h` -```cpp -// if a new generic port is created with a string, it's type should be AnyTypeAllowed -if constexpr(std::is_same_v) -{ - entry = createEntryImpl(key, PortInfo(PortDirection::INOUT)); // AnyTypeAllowed -} -``` - This allows subsequent writes of different types to the same entry. **Test reference:** `gtest_port_type_rules.cpp` - `BlackboardSetString_CreatesGenericEntry`, `StringEntry_CanBecomeTyped` tests @@ -130,37 +97,15 @@ This allows subsequent writes of different types to the same entry. Once a blackboard entry receives a **strongly typed** value, its type is locked: -**Source:** `blackboard.h` -```cpp -// special case: entry exists but it is not strongly typed... yet -if(!entry.info.isStronglyTyped()) -{ - // Use the new type to create a strongly typed entry - entry.info = TypeInfo::Create(); - // ... - return; -} -``` - -After this, writing a different type will fail (with exceptions noted below). +After this, writing a different type will fail (with exception). **Test reference:** `gtest_port_type_rules.cpp` - `TypeLock_CannotChangeAfterTypedWrite`, `TypeLock_XMLTreeCreation_TypeMismatch`, `TypeLock_RuntimeTypeChange_Fails` tests ### Rule 6: BT::Any Bypasses Type Checking -When a blackboard entry is **created with type `BT::Any`**, it can store different types over time. This requires the entry to be explicitly created as `BT::Any` type. +When a blackboard entry is **created with type `BT::Any`**, it can store different types over time. -**Important:** Wrapping a value with `BT::Any()` does **not** bypass type checking - the wrapper is unwrapped and the inner type is used: - -```cpp -// This creates an entry of type int, NOT BT::Any -bb->set("key", BT::Any(42)); - -// This will FAIL - entry is int, not BT::Any -bb->set("key", BT::Any("hello")); // throws LogicError -``` - -To actually allow different types, create the entry as `BT::Any`: +This requires the entry to be explicitly created as `BT::Any` type. ```cpp // Create entry explicitly as BT::Any type @@ -282,56 +227,3 @@ The following names **cannot** be used for ports: - Names starting with `_` - Reserved for internal use **Test reference:** `gtest_port_type_rules.cpp` - `ReservedPortName_ThrowsOnRegistration` test - -## Common Patterns - -### Pattern 1: Type-Safe Port Chain -```xml - - - - -``` - -### Pattern 2: String Literal to Typed Port -```xml - - - - -``` - -### Pattern 3: Generic Intermediate Storage -```xml - - - - - -``` - -## Error Messages - -Common type-related errors: - -1. **"The creation of the tree failed because the port [X] was initially created with type [A] and, later type [B] was used somewhere else."** - - Cause: Two nodes use same blackboard key with incompatible types - - Solution: Ensure consistent types or use string/generic ports - -2. **"Blackboard::set(X): once declared, the type of a port shall not change."** - - Cause: Runtime attempt to change entry type - - Solution: Use consistent types or BT::Any - -3. **"The port with name X and value Y can not be converted to Z"** - - Cause: Literal value cannot be parsed to port type - - Solution: Fix value format or add `convertFromString` specialization - -## References - -- Source: `include/behaviortree_cpp/basic_types.h` - Type system definitions -- Source: `include/behaviortree_cpp/blackboard.h` - Blackboard type checking -- Source: `src/xml_parsing.cpp` - Tree creation validation -- Tests: `tests/gtest_port_type_rules.cpp` - Comprehensive port type rule tests -- Tests: `tests/gtest_ports.cpp` - Port connection tests -- Tests: `tests/gtest_blackboard.cpp` - Blackboard tests -- Tutorial: `examples/t03_generic_ports.cpp` - Custom type example From 3e8c29283d974f6577afd3e0f2f214fcc1dac49b Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Fri, 9 Jan 2026 09:27:57 +0100 Subject: [PATCH 059/147] Fullpath fix (#1053) * add claude file * ignore generated files * detect duplicated instance names --- .gitignore | 2 + CLAUDE.md | 131 ++++++++++++++++++++++++++++++++ src/xml_parsing.cpp | 14 ++++ tests/gtest_subtree.cpp | 164 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 311 insertions(+) create mode 100644 CLAUDE.md diff --git a/.gitignore b/.gitignore index 0b25bb78b..99a502680 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ tags /clang_tidy_output.log /.clang-tidy-venv/* /llvm.sh +t11_groot_howto.btlog +minitrace.json diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..4459bce1e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,131 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Build Commands + +```bash +# Plain CMake (recommended for development) +mkdir build && cmake -S . -B build && cmake --build build --parallel + +# With Conan (CMake 3.23+ required) +conan install . -s build_type=Release --build=missing +cmake --preset conan-release +cmake --build --preset conan-release + +# Pixi/Conda +pixi run build +``` + +Requires CMake 3.16.3+ and C++17 compiler. The project exports `CMAKE_EXPORT_COMPILE_COMMANDS=ON` by default. + +## Testing + +```bash +# Run all tests via CTest +ctest --test-dir build + +# Run test executable directly +./build/tests/behaviortree_cpp_test + +# Run specific test +./build/tests/behaviortree_cpp_test --gtest_filter="TestName*" + +# Pixi +pixi run test +``` + +Test files are in `tests/` using Google Test. Key test categories: `gtest_blackboard.cpp`, `gtest_factory.cpp`, `gtest_tree.cpp`, `gtest_sequence.cpp`, `gtest_fallback.cpp`, `gtest_parallel.cpp`, `gtest_decorator.cpp`, `gtest_reactive.cpp`, `gtest_ports.cpp`, `gtest_port_type_rules.cpp`. + +## Linting and Formatting + +```bash +# Pre-commit hooks (clang-format, codespell) +pre-commit install +pre-commit run -a + +# Clang-tidy (requires clangd-21, build must exist for compile_commands.json) +./run_clang_tidy.sh [build_path] +``` + +Install clang-tidy-21: +```bash +wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh +sudo ./llvm.sh 21 +sudo apt install clangd-21 clang-tidy-21 +``` + +Code style: Google C++ with 90-char line limit, 2-space indent. See `.clang-format` and `.clang-tidy` for details. + +## Architecture + +**Namespace:** `BT::` +**Library:** `behaviortree_cpp` + +### Node Hierarchy + +All behavior tree nodes inherit from `TreeNode` (`include/behaviortree_cpp/tree_node.h`): + +- **LeafNode**: `ActionNode`, `ConditionNode` - user-defined tasks and checks +- **ControlNode**: `SequenceNode`, `FallbackNode`, `ParallelNode`, `ReactiveSequence`, `ReactiveFallback`, `SwitchNode`, `IfThenElseNode`, `WhileDoElseNode` +- **DecoratorNode**: `InverterNode`, `RetryNode`, `RepeatNode`, `TimeoutNode`, `DelayNode`, `SubtreeNode` + +### Node Status + +`TreeNodeStatus`: `IDLE`, `RUNNING`, `SUCCESS`, `FAILURE`, `SKIPPED` + +### Key Components + +| Component | Location | Purpose | +|-----------|----------|---------| +| `BehaviorTreeFactory` | `bt_factory.h` | Node registration, XML parsing, tree creation | +| `Blackboard` | `blackboard.h` | Shared typed key-value storage between nodes | +| Port System | `basic_types.h` | Type-safe dataflow: `InputPort`, `OutputPort`, `BidirectionalPort` | +| XML Parser | `xml_parsing.cpp` | Loads trees from XML with type validation | +| Script Parser | `scripting/` | Embedded expression language for conditions | + +### Port System Rules + +Ports enable type-safe data passing between nodes via the Blackboard: +- Same-typed ports always connect +- Generic ports (`AnyTypeAllowed`, `BT::Any`) accept any type +- `std::string` output acts as "universal donor" (converts via `convertFromString`) +- Type locks after first strongly-typed write +- Reserved names: `name`, `ID`, names starting with `_` + +See `docs/PORT_CONNECTION_RULES.md` for detailed rules. + +### Source Layout + +``` +src/ +├── *.cpp # Core: tree_node, blackboard, xml_parsing, bt_factory +├── actions/ # Built-in action nodes +├── controls/ # Control flow nodes (sequence, fallback, parallel, etc.) +├── decorators/ # Decorator nodes (retry, repeat, timeout, etc.) +└── loggers/ # Logging infrastructure (Groot2, SQLite, file) + +include/behaviortree_cpp/ +├── *.h # Public API headers +├── controls/ # Control node headers +├── decorators/ # Decorator node headers +├── loggers/ # Logger headers +├── scripting/ # Script parser (lexy-based) +└── contrib/ # Third-party contributions +``` + +### Integration Points + +- **Groot2**: Visual editor integration via ZeroMQ (`BTCPP_GROOT_INTERFACE` option) +- **ROS2**: Auto-detected via `ament_cmake`, uses colcon build +- **Conan**: Package manager support for non-ROS builds + +### Vendored Dependencies + +All in `3rdparty/`: TinyXML2, cppzmq, flatbuffers, lexy, minicoro, minitrace. Controlled via `USE_VENDORED_*` CMake options. + +## Contributing + +- Run `pre-commit run -a` and `./run_clang_tidy.sh` before PRs +- Bug fixes should include a failing test that passes after the fix +- Consider API/ABI compatibility implications diff --git a/src/xml_parsing.cpp b/src/xml_parsing.cpp index a094fea22..5c3e07ee1 100644 --- a/src/xml_parsing.cpp +++ b/src/xml_parsing.cpp @@ -1010,6 +1010,20 @@ void BT::XMLParser::PImpl::recursivelyCreateSubtree(const std::string& tree_ID, subtree_path += subtree_ID + "::" + std::to_string(node->UID()); } + // Check if the path already exists - duplicate paths cause issues in Groot2 + // and TreeObserver (see Groot2 issue #56) + for(const auto& sub : output_tree.subtrees) + { + if(sub->instance_name == subtree_path) + { + throw RuntimeError("Duplicate SubTree path detected: '", subtree_path, + "'. Multiple SubTree nodes with the same 'name' attribute " + "under the same parent are not allowed. " + "Please use unique names or omit the 'name' attribute " + "to auto-generate unique paths."); + } + } + recursivelyCreateSubtree(subtree_ID, subtree_path, // name subtree_path + "/", //prefix diff --git a/tests/gtest_subtree.cpp b/tests/gtest_subtree.cpp index 6402bafba..f8810091d 100644 --- a/tests/gtest_subtree.cpp +++ b/tests/gtest_subtree.cpp @@ -1,4 +1,5 @@ #include +#include #include "behaviortree_cpp/bt_factory.h" #include "../sample_nodes/dummy_nodes.h" #include "../sample_nodes/movebase_node.h" @@ -726,3 +727,166 @@ TEST(SubTree, SubtreeNameNotRegistered) ASSERT_ANY_THROW(auto tree = factory.createTreeFromText(xml_text)); ASSERT_ANY_THROW(factory.registerBehaviorTreeFromText(xml_text)); } + +// Test for Groot2 issue #56: duplicate _fullpath when multiple subtrees have the same name +// https://github.com/BehaviorTree/Groot2/issues/56 +// +// When two SubTree nodes under the same parent have the same "name" attribute, +// tree creation should fail with a clear error message. +TEST(SubTree, DuplicateSubTreeName_Groot2Issue56) +{ + // clang-format off + static const char* xml_text = R"( + + + + + + + + + + + + +)"; + // clang-format on + + BehaviorTreeFactory factory; + + // Should throw RuntimeError because of duplicate SubTree names + ASSERT_THROW(factory.createTreeFromText(xml_text), RuntimeError); +} + +// Additional test to verify the error message content +TEST(SubTree, DuplicateSubTreeName_ErrorMessage) +{ + // clang-format off + static const char* xml_text = R"( + + + + + + + + + + + + +)"; + // clang-format on + + BehaviorTreeFactory factory; + + try + { + factory.createTreeFromText(xml_text); + FAIL() << "Expected RuntimeError to be thrown"; + } + catch(const RuntimeError& e) + { + std::string msg = e.what(); + EXPECT_TRUE(msg.find("Duplicate SubTree path") != std::string::npos) + << "Error message should mention 'Duplicate SubTree path'. Got: " << msg; + EXPECT_TRUE(msg.find("my_task") != std::string::npos) + << "Error message should mention the duplicate path 'my_task'. Got: " << msg; + } +} + +// Test that unique names under the same parent work correctly +TEST(SubTree, UniqueSubTreeNames_WorksCorrectly) +{ + // clang-format off + static const char* xml_text = R"( + + + + + + + + + + + + +)"; + // clang-format on + + BehaviorTreeFactory factory; + Tree tree = factory.createTreeFromText(xml_text); + + // Verify paths are unique + std::set all_paths; + tree.applyVisitor([&](TreeNode* node) { + EXPECT_EQ(all_paths.count(node->fullPath()), 0); + all_paths.insert(node->fullPath()); + }); + + ASSERT_EQ(tree.subtrees.size(), 3); + auto status = tree.tickWhileRunning(); + ASSERT_EQ(status, NodeStatus::SUCCESS); +} + +// Test that omitting name attribute auto-generates unique paths +TEST(SubTree, NoNameAttribute_AutoGeneratesUniquePaths) +{ + // clang-format off + static const char* xml_text = R"( + + + + + + + + + + + + +)"; + // clang-format on + + BehaviorTreeFactory factory; + Tree tree = factory.createTreeFromText(xml_text); + + // Verify paths are unique (auto-generated with UID) + std::set all_paths; + tree.applyVisitor([&](TreeNode* node) { + EXPECT_EQ(all_paths.count(node->fullPath()), 0); + all_paths.insert(node->fullPath()); + }); + + ASSERT_EQ(tree.subtrees.size(), 3); + auto status = tree.tickWhileRunning(); + ASSERT_EQ(status, NodeStatus::SUCCESS); +} + +// Test nested subtrees - duplicate names at the same level should fail +TEST(SubTree, NestedDuplicateNames_ShouldFail) +{ + // clang-format off + static const char* xml_text = R"( + + + + + + + + + + + + +)"; + // clang-format on + + BehaviorTreeFactory factory; + + // Should throw RuntimeError because of duplicate SubTree names + ASSERT_THROW(factory.createTreeFromText(xml_text), RuntimeError); +} From 8f86eb99f1e4eab366fe77bbc676cec997599016 Mon Sep 17 00:00:00 2001 From: Davide Faconti Date: Fri, 9 Jan 2026 09:34:44 +0100 Subject: [PATCH 060/147] add new name validation rules --- docs/name_validation_rules.md | 134 ++++++++++++ src/basic_types.cpp | 6 +- src/xml_parsing.cpp | 86 ++++++++ tests/CMakeLists.txt | 1 + tests/gtest_name_validation.cpp | 361 ++++++++++++++++++++++++++++++++ tests/gtest_port_type_rules.cpp | 26 +++ tests/gtest_reactive.cpp | 2 +- 7 files changed, 613 insertions(+), 3 deletions(-) create mode 100644 docs/name_validation_rules.md create mode 100644 tests/gtest_name_validation.cpp diff --git a/docs/name_validation_rules.md b/docs/name_validation_rules.md new file mode 100644 index 000000000..35abb4a5e --- /dev/null +++ b/docs/name_validation_rules.md @@ -0,0 +1,134 @@ +# Name Validation Rules + +This document describes the validation rules for names in Groot2 and BehaviorTree.CPP. These rules ensure XML compatibility while supporting Unicode characters (Chinese, Japanese, Korean, etc.). + +## Overview + +The validation uses a **blacklist approach**: all characters are allowed except those explicitly forbidden. This enables Unicode support while blocking characters that would break XML serialization or cause path/filesystem issues. + +## Forbidden Characters (Model Names & Port Names) + +The following ASCII characters are forbidden in **Model Names** and **Port Names**: + +| Category | Characters | Reason | +|----------|------------|--------| +| Whitespace | `space`, `\t`, `\n`, `\r` | Breaks XML element/attribute names | +| XML special | `<`, `>`, `&`, `"`, `'` | Reserved in XML | +| Path separators | `/`, `\`, `:` | Filesystem conflicts | +| Wildcards | `*`, `?`, `\|` | Shell/glob conflicts | +| Period | `.` | Ambiguous in port names (e.g., `request.name`) | +| Control chars | ASCII 0-31, 127 | Non-printable | + +## Allowed Characters + +| Category | Examples | +|----------|----------| +| ASCII letters | `a-z`, `A-Z` | +| Digits | `0-9` | +| Underscore | `_` | +| Hyphen | `-` | +| Unicode letters | `中文`, `日本語`, `한국어`, `Ümlauts` | + +## Validation Rules by Name Type + +### Model Name (Node Type Name) +- **Cannot be empty** +- **Cannot be "Root"** (reserved) +- No forbidden characters (see table above) + +### Port Name +- **Cannot be empty** +- **Cannot start with a digit** +- **Cannot be a reserved attribute**: `ID`, `name`, `_description`, `_skipIf`, `_successIf`, `_failureIf`, `_while`, `_onSuccess`, `_onFailure`, `_onHalted`, `_post`, `_autoremap`, `__shared_blackboard` +- No forbidden characters (see table above) + +### Instance Name +- **Can be empty** (defaults to model name) +- Instance names are XML attribute **values** (not element/attribute names), so most characters are allowed including spaces, periods, etc. +- Only invalid XML control characters are forbidden (ASCII 0-8, 11-12, 14-31, 127) + +## Implementation + +### C++ Reference Implementation + +```cpp +#include +#include +#include + +// Returns the forbidden character if found, or '\0' if valid +static char findForbiddenChar(const std::string& name) +{ + static constexpr std::array forbidden = { + ' ', '\t', '\n', '\r', '<', '>', '&', '"', '\'', '/', '\\', ':', '*', '?', '|', '.'}; + + for (unsigned char c : name) + { + // Allow UTF-8 multibyte sequences (high bit set) + if (c >= 0x80) + { + continue; + } + // Block control characters + if (c < 32 || c == 127) + { + return static_cast(c); + } + // Check forbidden list + if (std::find(forbidden.begin(), forbidden.end(), c) != forbidden.end()) + { + return static_cast(c); + } + } + return '\0'; +} +``` + +## Examples + +### Valid Model/Port Names +``` +MyAction +my_action +My-Action +检查门状态 (Chinese) +ドアを開ける (Japanese) +Tür_öffnen (German) +``` + +### Invalid Model/Port Names +``` +My Action (contains space) +request.name (contains period) +My (contains XML chars) +path/to/node (contains path separator) +Root (reserved) +``` + +### Valid Instance Names +Instance names have relaxed rules since they are XML attribute values: +``` +My Action (spaces allowed) +node.name (periods allowed) +Success 1 (spaces allowed) +检查门状态 (Unicode allowed) +``` + +### Invalid Instance Names +``` +name_with_null\0 (null character) +name_with_bell\x07 (control character) +``` + +## Related Issues + +- [#59](https://github.com/BehaviorTree/Groot2/issues/59) - Unicode support in node names +- [#60](https://github.com/BehaviorTree/Groot2/issues/60) - i18n support request +- [#64](https://github.com/BehaviorTree/Groot2/issues/64) - Clear error for forbidden characters in port names + +## Files Modified in BehaviorTree.CPP + +- `include/behaviortree_cpp/basic_types.h` - `findForbiddenChar()` declaration +- `src/basic_types.cpp` - `findForbiddenChar()` implementation, `IsAllowedPortName()` update +- `src/xml_parsing.cpp` - Validation functions and integration in XML parsing +- `tests/gtest_name_validation.cpp` - Comprehensive tests for validation diff --git a/src/basic_types.cpp b/src/basic_types.cpp index 285349d6e..f83d622b6 100644 --- a/src/basic_types.cpp +++ b/src/basic_types.cpp @@ -2,10 +2,12 @@ #include "behaviortree_cpp/tree_node.h" #include "behaviortree_cpp/json_export.h" +#include +#include +#include +#include #include #include -#include -#include #include namespace BT diff --git a/src/xml_parsing.cpp b/src/xml_parsing.cpp index 5c3e07ee1..42024e1c4 100644 --- a/src/xml_parsing.cpp +++ b/src/xml_parsing.cpp @@ -91,6 +91,92 @@ namespace auto StrEqual = [](const char* str1, const char* str2) -> bool { return strcmp(str1, str2) == 0; }; + +// Helper to format forbidden character for error messages +std::string formatForbiddenChar(char c) +{ + if(c < 32 || c == 127) + { + return "control character (ASCII " + std::to_string(static_cast(c)) + ")"; + } + return std::string("'") + c + "'"; +} + +void validateModelName(const std::string& name, int line_number) +{ + const auto line_str = std::to_string(line_number); + if(name.empty()) + { + throw RuntimeError("Error at line ", line_str, + ": Model/Node type name cannot be empty"); + } + if(name == "Root" || name == "root") + { + throw RuntimeError("Error at line ", line_str, + ": 'Root' is a reserved name and cannot be used as a node type"); + } + if(char c = findForbiddenChar(name); c != '\0') + { + throw RuntimeError("Error at line ", line_str, ": Model name '", name, + "' contains forbidden character ", formatForbiddenChar(c)); + } +} + +void validatePortName(const std::string& name, int line_number) +{ + const auto line_str = std::to_string(line_number); + if(name.empty()) + { + throw RuntimeError("Error at line ", line_str, ": Port name cannot be empty"); + } + if(std::isdigit(static_cast(name[0]))) + { + throw RuntimeError("Error at line ", line_str, ": Port name '", name, + "' cannot start with a digit"); + } + if(char c = findForbiddenChar(name); c != '\0') + { + throw RuntimeError("Error at line ", line_str, ": Port name '", name, + "' contains forbidden character ", formatForbiddenChar(c)); + } + if(IsReservedAttribute(name)) + { + throw RuntimeError("Error at line ", line_str, ": Port name '", name, + "' is a reserved attribute name"); + } +} + +void validateInstanceName(const std::string& name, int line_number) +{ + // Instance name CAN be empty (defaults to model name) + // Instance names are XML attribute VALUES, so they can contain spaces, + // periods, and most characters. We only reject control characters that + // are invalid in XML. + if(name.empty()) + { + return; + } + for(const char c : name) + { + const auto uc = static_cast(c); + // Only reject control characters that are invalid in XML + // (XML allows tab=0x09, newline=0x0A, carriage return=0x0D) + if(uc < 32 && uc != 0x09 && uc != 0x0A && uc != 0x0D) + { + const auto line_str = std::to_string(line_number); + throw RuntimeError("Error at line ", line_str, ": Instance name '", name, + "' contains invalid control character (ASCII ", + std::to_string(static_cast(uc)), ")"); + } + if(uc == 127) + { + const auto line_str = std::to_string(line_number); + throw RuntimeError("Error at line ", line_str, ": Instance name '", name, + "' contains invalid control character (ASCII 127)"); + } + } +} + } // namespace struct SubtreeModel diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 14a4fef8e..6367ef4c3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,6 +18,7 @@ set(BT_TESTS gtest_port_type_rules.cpp gtest_postconditions.cpp gtest_match.cpp + gtest_name_validation.cpp gtest_json.cpp gtest_reactive.cpp gtest_reactive_backchaining.cpp diff --git a/tests/gtest_name_validation.cpp b/tests/gtest_name_validation.cpp new file mode 100644 index 000000000..01f45414a --- /dev/null +++ b/tests/gtest_name_validation.cpp @@ -0,0 +1,361 @@ +#include +#include "behaviortree_cpp/bt_factory.h" +#include "behaviortree_cpp/xml_parsing.h" +#include "behaviortree_cpp/basic_types.h" + +using namespace BT; + +// ============== Tests for findForbiddenChar() ============== + +TEST(NameValidation, ForbiddenCharDetection_ValidNames) +{ + // Valid ASCII names + EXPECT_EQ(findForbiddenChar("ValidName"), '\0'); + EXPECT_EQ(findForbiddenChar("my_action"), '\0'); + EXPECT_EQ(findForbiddenChar("My-Action"), '\0'); + EXPECT_EQ(findForbiddenChar("action123"), '\0'); + EXPECT_EQ(findForbiddenChar("CamelCaseNode"), '\0'); + EXPECT_EQ(findForbiddenChar("snake_case_node"), '\0'); + EXPECT_EQ(findForbiddenChar("kebab-case-node"), '\0'); +} + +TEST(NameValidation, ForbiddenCharDetection_Unicode) +{ + // Unicode names should be allowed (UTF-8 multibyte sequences) + EXPECT_EQ(findForbiddenChar("检查门状态"), '\0'); // Chinese + EXPECT_EQ(findForbiddenChar("ドアを開ける"), '\0'); // Japanese + EXPECT_EQ(findForbiddenChar("Tür_öffnen"), '\0'); // German with umlaut + EXPECT_EQ(findForbiddenChar("проверка"), '\0'); // Russian + EXPECT_EQ(findForbiddenChar("действие"), '\0'); // Russian +} + +TEST(NameValidation, ForbiddenCharDetection_ForbiddenChars) +{ + // Space and whitespace + EXPECT_EQ(findForbiddenChar("My Action"), ' '); + EXPECT_EQ(findForbiddenChar("with\ttab"), '\t'); + EXPECT_EQ(findForbiddenChar("with\nnewline"), '\n'); + EXPECT_EQ(findForbiddenChar("with\rcarriage"), '\r'); + + // XML special characters + EXPECT_EQ(findForbiddenChar("My"), '<'); + EXPECT_EQ(findForbiddenChar("Node>End"), '>'); + EXPECT_EQ(findForbiddenChar("A&B"), '&'); + EXPECT_EQ(findForbiddenChar("say\"hello\""), '"'); + EXPECT_EQ(findForbiddenChar("it's"), '\''); + + // Filesystem problematic characters + EXPECT_EQ(findForbiddenChar("path/to/node"), '/'); + EXPECT_EQ(findForbiddenChar("path\\to\\node"), '\\'); + EXPECT_EQ(findForbiddenChar("C:drive"), ':'); + EXPECT_EQ(findForbiddenChar("wild*card"), '*'); + EXPECT_EQ(findForbiddenChar("what?"), '?'); + EXPECT_EQ(findForbiddenChar("pipe|char"), '|'); + + // Period (can cause issues) + EXPECT_EQ(findForbiddenChar("request.name"), '.'); + EXPECT_EQ(findForbiddenChar("file.ext"), '.'); +} + +TEST(NameValidation, ForbiddenCharDetection_ControlChars) +{ + // Control characters should be forbidden + std::string with_null = "test"; + with_null += '\0'; + with_null += "name"; + EXPECT_EQ(findForbiddenChar(with_null), '\0'); // null char detected + + // Bell character (ASCII 7) - use string concatenation to avoid hex digit issues + std::string with_bell = "test"; + with_bell += '\x07'; + with_bell += "bell"; + EXPECT_EQ(findForbiddenChar(with_bell), '\x07'); + + // DEL character (ASCII 127) + std::string with_del = "test"; + with_del += '\x7F'; + with_del += "del"; + EXPECT_EQ(findForbiddenChar(with_del), '\x7F'); +} + +// ============== Tests for IsAllowedPortName() ============== + +TEST(NameValidation, IsAllowedPortName_Valid) +{ + EXPECT_TRUE(IsAllowedPortName("input")); + EXPECT_TRUE(IsAllowedPortName("output_value")); + EXPECT_TRUE(IsAllowedPortName("myPort123")); + EXPECT_TRUE(IsAllowedPortName("Port_With_Underscore")); +} + +TEST(NameValidation, IsAllowedPortName_Invalid) +{ + // Empty + EXPECT_FALSE(IsAllowedPortName("")); + + // Starts with digit + EXPECT_FALSE(IsAllowedPortName("1port")); + EXPECT_FALSE(IsAllowedPortName("123")); + + // Starts with underscore (reserved) + EXPECT_FALSE(IsAllowedPortName("_private")); + + // Reserved names + EXPECT_FALSE(IsAllowedPortName("name")); + EXPECT_FALSE(IsAllowedPortName("ID")); + EXPECT_FALSE(IsAllowedPortName("_failureIf")); + EXPECT_FALSE(IsAllowedPortName("_successIf")); + EXPECT_FALSE(IsAllowedPortName("_skipIf")); + EXPECT_FALSE(IsAllowedPortName("_while")); + EXPECT_FALSE(IsAllowedPortName("_onSuccess")); + EXPECT_FALSE(IsAllowedPortName("_onFailure")); + EXPECT_FALSE(IsAllowedPortName("_onHalted")); + EXPECT_FALSE(IsAllowedPortName("_post")); + EXPECT_FALSE(IsAllowedPortName("_autoremap")); + + // Forbidden characters + EXPECT_FALSE(IsAllowedPortName("port name")); // space + EXPECT_FALSE(IsAllowedPortName("port.name")); // period + EXPECT_FALSE(IsAllowedPortName("port")); // angle brackets +} + +// ============== Tests for XML parsing validation ============== + +class NameValidationXMLTest : public testing::Test +{ +protected: + BehaviorTreeFactory factory; +}; + +TEST_F(NameValidationXMLTest, ValidBehaviorTreeID) +{ + const char* xml = R"( + + + + + )"; + EXPECT_NO_THROW(factory.createTreeFromText(xml)); +} + +TEST_F(NameValidationXMLTest, ValidBehaviorTreeID_WithUnderscore) +{ + const char* xml = R"( + + + + + )"; + EXPECT_NO_THROW(factory.createTreeFromText(xml)); +} + +TEST_F(NameValidationXMLTest, InvalidBehaviorTreeID_Root) +{ + const char* xml = R"( + + + + + )"; + EXPECT_THROW(factory.createTreeFromText(xml), RuntimeError); +} + +TEST_F(NameValidationXMLTest, InvalidBehaviorTreeID_root_lowercase) +{ + const char* xml = R"( + + + + + )"; + EXPECT_THROW(factory.createTreeFromText(xml), RuntimeError); +} + +TEST_F(NameValidationXMLTest, InvalidBehaviorTreeID_WithSpace) +{ + const char* xml = R"( + + + + + )"; + EXPECT_THROW(factory.createTreeFromText(xml), RuntimeError); +} + +TEST_F(NameValidationXMLTest, InvalidBehaviorTreeID_WithPeriod) +{ + const char* xml = R"( + + + + + )"; + EXPECT_THROW(factory.createTreeFromText(xml), RuntimeError); +} + +TEST_F(NameValidationXMLTest, ValidInstanceName) +{ + const char* xml = R"( + + + + + )"; + EXPECT_NO_THROW(factory.createTreeFromText(xml)); +} + +TEST_F(NameValidationXMLTest, ValidInstanceName_WithSpace) +{ + // Instance names are XML attribute VALUES, so spaces are allowed + const char* xml = R"( + + + + + )"; + EXPECT_NO_THROW(factory.createTreeFromText(xml)); +} + +TEST_F(NameValidationXMLTest, ValidInstanceName_WithPeriod) +{ + // Instance names are XML attribute VALUES, so periods are allowed + const char* xml = R"( + + + + + )"; + EXPECT_NO_THROW(factory.createTreeFromText(xml)); +} + +TEST_F(NameValidationXMLTest, ValidSubTreeID) +{ + const char* xml = R"( + + + + + + + + )"; + EXPECT_NO_THROW(factory.createTreeFromText(xml)); +} + +TEST_F(NameValidationXMLTest, InvalidSubTreeID_WithSpace) +{ + const char* xml = R"( + + + + + + + + )"; + EXPECT_THROW(factory.createTreeFromText(xml), RuntimeError); +} + +// ============== Tests for Unicode support ============== + +TEST_F(NameValidationXMLTest, UnicodeTreeID_Chinese) +{ + const char* xml = R"( + + + + + )"; + EXPECT_NO_THROW(factory.createTreeFromText(xml)); +} + +TEST_F(NameValidationXMLTest, UnicodeInstanceName_Japanese) +{ + const char* xml = R"( + + + + + )"; + EXPECT_NO_THROW(factory.createTreeFromText(xml)); +} + +TEST_F(NameValidationXMLTest, UnicodeTreeID_German) +{ + const char* xml = R"( + + + + + )"; + EXPECT_NO_THROW(factory.createTreeFromText(xml)); +} + +// ============== Tests for SubTree port validation ============== + +TEST_F(NameValidationXMLTest, ValidSubTreePortName) +{ + const char* xml = R"( + + + + + + + + + + + + + )"; + EXPECT_NO_THROW(factory.createTreeFromText(xml)); +} + +TEST_F(NameValidationXMLTest, InvalidSubTreePortName_WithSpace) +{ + const char* xml = R"( + + + + + + + + + + )"; + EXPECT_THROW(factory.createTreeFromText(xml), RuntimeError); +} + +TEST_F(NameValidationXMLTest, InvalidSubTreePortName_Reserved) +{ + const char* xml = R"( + + + + + + + + + + )"; + EXPECT_THROW(factory.createTreeFromText(xml), RuntimeError); +} + +TEST_F(NameValidationXMLTest, InvalidSubTreePortName_StartsWithDigit) +{ + const char* xml = R"( + + + + + + + + + + )"; + EXPECT_THROW(factory.createTreeFromText(xml), RuntimeError); +} diff --git a/tests/gtest_port_type_rules.cpp b/tests/gtest_port_type_rules.cpp index 66541761c..476af1d03 100644 --- a/tests/gtest_port_type_rules.cpp +++ b/tests/gtest_port_type_rules.cpp @@ -1001,6 +1001,32 @@ TEST(PortTypeRules, CustomTypeStringLiteral_InvalidFormat) EXPECT_THROW(auto tree = factory.createTreeFromText(xml), LogicError); } +TEST(PortTypeRules, StringToDifferentTypes) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("NodeWithStringPorts"); + factory.registerNodeType("NodeWithIntPorts"); + factory.registerNodeType("NodeWithDoublePorts"); + + // Missing second coordinate + std::string xml = R"( + + + + + + + + + + )"; + + auto tree = factory.createTreeFromText(xml); + tree.tickWhileRunning(); + ASSERT_EQ(tree.rootBlackboard()->get("test_int"), 84); + ASSERT_DOUBLE_EQ(tree.rootBlackboard()->get("test_double"), 42.0); +} + //============================================================================== // TEST SECTION 10: Reserved Port Names //============================================================================== diff --git a/tests/gtest_reactive.cpp b/tests/gtest_reactive.cpp index 47e4435ae..7246b8d25 100644 --- a/tests/gtest_reactive.cpp +++ b/tests/gtest_reactive.cpp @@ -61,7 +61,7 @@ TEST(Reactive, Issue587) static const char* reactive_xml_text = R"( - +