diff --git a/CMakeLists.txt b/CMakeLists.txt index 34836c6..0173194 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,9 @@ if(BUILD_TESTING) ) ament_target_dependencies(test_get_env rcutils) + ament_add_gtest(test_scope_exit test/test_scope_exit.cpp) + target_link_libraries(test_scope_exit ${PROJECT_NAME}) + ament_add_gtest(test_split test/test_split.cpp) ament_add_gtest(test_filesystem_helper test/test_filesystem_helper.cpp diff --git a/include/rcpputils/scope_exit.hpp b/include/rcpputils/scope_exit.hpp new file mode 100644 index 0000000..c55ade1 --- /dev/null +++ b/include/rcpputils/scope_exit.hpp @@ -0,0 +1,68 @@ +// Copyright 2015-2020 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef RCPPUTILS__SCOPE_EXIT_HPP_ +#define RCPPUTILS__SCOPE_EXIT_HPP_ + +#include + +#include "rcutils/macros.h" + +namespace rcpputils +{ + +template +struct scope_exit final +{ + explicit scope_exit(CallableT && callable) + : callable_(std::forward(callable)) + { + } + + scope_exit(const scope_exit &) = delete; + scope_exit(scope_exit &&) = default; + + scope_exit & operator=(const scope_exit &) = delete; + scope_exit & operator=(scope_exit &&) = default; + + ~scope_exit() + { + if (!cancelled_) { + callable_(); + } + } + + void cancel() + { + cancelled_ = true; + } + +private: + CallableT callable_; + bool cancelled_{false}; +}; + +template +scope_exit +make_scope_exit(CallableT && callable) +{ + return scope_exit(std::forward(callable)); +} + +} // namespace rcpputils + +#define RCPPUTILS_SCOPE_EXIT(code) \ + auto RCUTILS_JOIN(scope_exit_, __LINE__) = rcpputils::make_scope_exit([&]() {code;}) + +#endif // RCPPUTILS__SCOPE_EXIT_HPP_ diff --git a/test/test_scope_exit.cpp b/test/test_scope_exit.cpp new file mode 100644 index 0000000..30b22e8 --- /dev/null +++ b/test/test_scope_exit.cpp @@ -0,0 +1,64 @@ +// Copyright 2020 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "rcpputils/scope_exit.hpp" + +TEST(test_scope_exit, should_run) { + bool called = false; + + { + auto on_exit = + rcpputils::make_scope_exit( + [&called]() { + called = true; + }); + (void)on_exit; + } + + EXPECT_TRUE(called); +} + +TEST(test_scope_exit, should_not_run) { + bool called = false; + + { + auto on_exit = + rcpputils::make_scope_exit( + [&called]() { + called = true; + }); + on_exit.cancel(); + } + + EXPECT_FALSE(called); +} + +TEST(test_scope_exit, code_types) { + auto code = []() {}; + rcpputils::make_scope_exit(code); + + const auto const_code = []() {}; + rcpputils::make_scope_exit(const_code); + + struct NonCopyableCode + { + NonCopyableCode() = default; + NonCopyableCode(const NonCopyableCode &) = delete; + NonCopyableCode(NonCopyableCode &&) = default; + void operator()() {} + }; + rcpputils::make_scope_exit(NonCopyableCode()); +}