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/cmake_ubuntu.yml b/.github/workflows/cmake_ubuntu.yml index 1d4ba93f1..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 @@ -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 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 ddd1cbfb8..368cac67f 100644 --- a/.github/workflows/pixi.yaml +++ b/.github/workflows/pixi.yaml @@ -17,8 +17,8 @@ jobs: runs-on: ${{ matrix.os }} steps: # Pixi is the tool used to create/manage conda environment - - uses: actions/checkout@v3 - - uses: prefix-dev/setup-pixi@v0.8.1 + - uses: actions/checkout@v6 + - uses: prefix-dev/setup-pixi@v0.9.3 with: pixi-version: v0.40.3 - name: Build diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index ee7fa9229..c05f06d4b 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@v6 + - uses: actions/setup-python@v6 - uses: pre-commit/action@v3.0.1 + + clang-tidy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - 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/.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: 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/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 c9528bda6..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) @@ -240,7 +240,7 @@ target_link_libraries(${BTCPP_LIBRARY} if(BTCPP_GROOT_INTERFACE) target_link_libraries(${BTCPP_LIBRARY} - PRIVATE + PUBLIC $ ) endif() @@ -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/README.md b/README.md index d80513ecf..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,11 +102,21 @@ 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) -Copyright (c) 2019-2023 Davide Faconti +Copyright (c) 2019-2025 Davide Faconti Copyright (c) 2018-2019 Davide Faconti, Eurecat diff --git a/include/behaviortree_cpp/action_node.h b/include/behaviortree_cpp/action_node.h index 409e57987..3e25957fb 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, @@ -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/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..6fced9e97 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, @@ -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/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/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/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..993c4b5e1 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, @@ -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/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/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 a1806a196..1cd4a3b12 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, @@ -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/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..091905dbe 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, @@ -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 9062ca24e..85b4f796c 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, @@ -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 515e00e04..cf1235730 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, @@ -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 40834a61e..560d835c4 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, @@ -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 ef92dbc56..7f977363d 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, @@ -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 fe807ef89..dafeece1c 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, @@ -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 ac2d7acd0..77e56fc13 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, @@ -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/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..f49190586 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, @@ -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 8bd351e72..9c9a0544e 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, @@ -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 7ffb989a2..2056f0b3c 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, @@ -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 4a6243f6e..844608181 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, @@ -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/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..02dd01a44 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, @@ -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/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..0565f6d28 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, @@ -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/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..6581a268f 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, @@ -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 7a903ef39..acfdd7dca 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, @@ -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/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..b9a5d55ba 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, @@ -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 b52cc1263..fd895e96b 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, @@ -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 d570aafb2..39b7f10d7 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, @@ -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/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/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 61d7a1ca1..eac254be3 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, @@ -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/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..890257f37 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, @@ -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 6baaa03fd..690c1970e 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, @@ -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 ce94b2eb7..086ec1825 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, @@ -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 484780e46..f12724ca6 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,25 @@ 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) + { + std::swap(_storage, other._storage); + } return *this; } @@ -99,46 +109,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 +175,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) @@ -169,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/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. 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 2bf78f964..c25673ee3 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, @@ -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 15be65600..1bc0f0713 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, @@ -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 1b07739c9..ed610ddca 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, @@ -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 d7dd1dbb0..487ea6034 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, @@ -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/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..535165376 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, @@ -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 4b8fb3afb..333e90db0 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, @@ -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 bb50a235b..7d33099c6 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, @@ -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 82e7e443b..d8461fd4a 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, @@ -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/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..54399f0a6 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, @@ -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 2fc4110dd..0a6f7ae6c 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, @@ -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 a19c6b9ec..46afdab88 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, @@ -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/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..e43e70a4e 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, @@ -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 943f559d3..40c58086e 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, @@ -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 94294e34c..ab41215ed 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, @@ -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/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..d502397c3 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, @@ -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 d8c689c78..c6a11f1b5 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, @@ -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 ae1018baa..5fc254954 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, @@ -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 b4c9dd764..8ddc8d8af 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, @@ -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 946492318..908f23418 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, @@ -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 5426cc2e4..a094fea22 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, @@ -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; 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); +}