Skip to content

Commit

Permalink
Merge pull request #3968 from Rohde-Schwarz/feature/scoped_cleanup
Browse files Browse the repository at this point in the history
Add scoped_cleanup helper
  • Loading branch information
reneme committed Apr 4, 2024
2 parents dcada9d + b6e216d commit b059347
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 0 deletions.
33 changes: 33 additions & 0 deletions src/lib/utils/stl_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <algorithm>
#include <functional>
#include <map>
#include <optional>
#include <set>
#include <span>
#include <string>
Expand Down Expand Up @@ -328,6 +329,38 @@ struct overloaded : Ts... {
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;

/**
* @brief Helper class to create a RAII-style cleanup callback
*
* Ensures that the cleanup callback given in the object's constructor is called
* when the object is destroyed. Use this to ensure some cleanup code runs when
* leaving the current scope.
*/
template <std::invocable FunT>
class scoped_cleanup {
public:
explicit scoped_cleanup(FunT cleanup) : m_cleanup(std::move(cleanup)) {}

scoped_cleanup(const scoped_cleanup&) = delete;
scoped_cleanup& operator=(const scoped_cleanup&) = delete;
scoped_cleanup(scoped_cleanup&&) = delete;
scoped_cleanup& operator=(scoped_cleanup&&) = delete;

~scoped_cleanup() {
if(m_cleanup.has_value()) {
m_cleanup.value()();
}
}

/**
* Disengage the cleanup callback, i.e., prevent it from being called
*/
void disengage() { m_cleanup.reset(); }

private:
std::optional<FunT> m_cleanup;
};

} // namespace Botan

#endif
67 changes: 67 additions & 0 deletions src/tests/test_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,73 @@ class Formatter_Tests : public Test {

BOTAN_REGISTER_TEST("utils", "fmt", Formatter_Tests);

class ScopedCleanup_Tests : public Test {
public:
std::vector<Test::Result> run() override {
return {
Botan_Tests::CHECK("leaving a scope results in cleanup",
[](Test::Result& result) {
bool ran = false;
{
auto clean = Botan::scoped_cleanup([&] { ran = true; });
}
result.confirm("cleanup ran", ran);
}),

Botan_Tests::CHECK("leaving a function, results in cleanup",
[](Test::Result& result) {
bool ran = false;
bool fn_called = false;
auto fn = [&] {
auto clean = Botan::scoped_cleanup([&] { ran = true; });
fn_called = true;
};

result.confirm("cleanup not yet ran", !ran);
fn();
result.confirm("fn called", fn_called);
result.confirm("cleanup ran", ran);
}),

Botan_Tests::CHECK("stack unwinding results in cleanup",
[](Test::Result& result) {
bool ran = false;
bool fn_called = false;
bool exception_caught = false;
auto fn = [&] {
auto clean = Botan::scoped_cleanup([&] { ran = true; });
fn_called = true;
throw std::runtime_error("test");
};

result.confirm("cleanup not yet ran", !ran);
try {
fn();
} catch(const std::exception&) {
exception_caught = true;
}

result.confirm("fn called", fn_called);
result.confirm("cleanup ran", ran);
result.confirm("exception caught", exception_caught);
}),

Botan_Tests::CHECK("cleanup isn't called after disengaging",
[](Test::Result& result) {
bool ran = false;
{
auto clean = Botan::scoped_cleanup([&] { ran = true; });
clean.disengage();
}
result.confirm("cleanup not ran", !ran);
}),

};
}
};

BOTAN_REGISTER_TEST("utils", "scoped_cleanup", ScopedCleanup_Tests);

#endif

} // namespace
Expand Down

0 comments on commit b059347

Please sign in to comment.