based

Opinionated utility library
git clone git://git.dimitrijedobrota.com/based.git
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING

commit 9eca6e4aef6bd73e894a009f5ab5ee22707d4c50
parent 4d76aec6d751582155fc6ca5802dd8c7932cc8f4
author Dimitrije Dobrota < mail@dimitrijedobrota.com >
date Tue, 29 Apr 2025 15:00:43 +0200

Test scopeguard, fix some issues

* Found bug in gcc

Diffstat:
M include/based/template.hpp | +++++++++++++ --------------
M test/CMakeLists.txt | +
A test/source/scopeguard_test.cpp | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

3 files changed, 152 insertions(+), 14 deletions(-)


diff --git a/ include/based/template.hpp b/ include/based/template.hpp

@@ -7,7 +7,6 @@

#include <type_traits>
#include <utility>

#include "based/type_traits.hpp"
#include "based/utility.hpp"

namespace based

@@ -372,19 +371,19 @@ template<typename T>

function(T) -> function<typename signature<std::decay_t<T>>::sig_type>;
*/

template<Procedure Func, bool on_success = false, bool on_failure = false>
template<typename Func, bool on_success = false, bool on_failure = false>
class scopeguard
{
uncaught_exception_detector m_detector;
Func m_func;

public:
explicit scopeguard(Func&& func)
scopeguard(Func&& func) // NOLINT explicit
: m_func(std::move(func))
{
}

explicit scopeguard(const Func& func)
scopeguard(const Func& func) // NOLINT explicit
: m_func(func)
{
}

@@ -392,8 +391,8 @@ public:

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

scopeguard(scopeguard&&) = default;
scopeguard& operator=(scopeguard&&) = default;
scopeguard(scopeguard&&) = delete;
scopeguard& operator=(scopeguard&&) = delete;

~scopeguard()
{

@@ -403,19 +402,19 @@ public:

}
};

template<Procedure Func>
template<typename Func>
class scopeguard<Func, false, false>
{
bool m_commit = false;
Func m_func;

public:
explicit scopeguard(Func&& func)
scopeguard(Func&& func) // NOLINT explicit
: m_func(std::move(func))
{
}

explicit scopeguard(const Func& func)
scopeguard(const Func& func) // NOLINT explicit
: m_func(func)
{
}

@@ -423,8 +422,8 @@ public:

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

scopeguard(scopeguard&&) = default;
scopeguard& operator=(scopeguard&&) = default;
scopeguard(scopeguard&&) = delete;
scopeguard& operator=(scopeguard&&) = delete;

~scopeguard()
{

@@ -435,13 +434,13 @@ public:

void commit() { m_commit = true; }
};

template<Procedure Func>
template<typename Func>
using scopeguard_exit = scopeguard<Func, true, true>;

template<Procedure Func>
template<typename Func>
using scopeguard_success = scopeguard<Func, true, false>;

template<Procedure Func>
template<typename Func>
using scopeguard_failure = scopeguard<Func, false, true>;

} // namespace based

diff --git a/ test/CMakeLists.txt b/ test/CMakeLists.txt

@@ -68,6 +68,7 @@ add_test(string_literal_test)

add_test(signature_test)
add_test(buffer_test)
add_test(function_test)
add_test(scopeguard_test)

# ---- End-of-file commands ----

diff --git a/ test/source/scopeguard_test.cpp b/ test/source/scopeguard_test.cpp

@@ -0,0 +1,138 @@

#define CATCH_CONFIG_RUNTIME_STATIC_REQUIRE

#include <stdexcept>

#include <catch2/catch_test_macros.hpp>

#include "based/template.hpp"

// NOLINTBEGIN cognitive-complexity

struct set
{
set(int& val)
: m_val(&val)
{
}

void operator()() const { *m_val = 1; }

private:
int* m_val;
};

template class based::scopeguard<set>;

TEST_CASE("manual", "[template/scopeguard]")
{
SECTION("commit")
{
int test = 0;
try {
based::scopeguard guard = set(test);
guard.commit();
} catch (...) {
REQUIRE(false);
}
REQUIRE(test == 1);
}

// breaks coverage for some reason
SECTION("no commit")
{
int test = 0;
try {
based::scopeguard guard = set(test);
} catch (...) {
test *= 1;
}
REQUIRE(test == 0);
}
}

TEST_CASE("on success", "[template/scopeguard]")
{
SECTION("success")
{
int test = 0;
auto func = set(test);

try {
const based::scopeguard_success guard = func;
} catch (...) {
test *= 1;
}
REQUIRE(test == 1);
}

// breaks coverage for some reason
SECTION("failure")
{
int test = 0;
auto func = set(test);

try {
const based::scopeguard_success guard = func;
throw std::runtime_error {"should not leak"};
} catch (...) {
test *= 1;
}
REQUIRE(test == 0);
}
}

TEST_CASE("on failure", "[template/scopeguard]")
{
SECTION("success")
{
int test = 0;
try {
const based::scopeguard_failure guard = set(test);
} catch (...) {
test *= 1;
}
REQUIRE(test == 0);
}

// breaks coverage for some reason
SECTION("failure")
{
int test = 0;
try {
const based::scopeguard_failure guard = set(test);
throw std::runtime_error {"should not leak"};
} catch (...) {
REQUIRE(true);
}
REQUIRE(test == 1);
}
}

TEST_CASE("exit", "[template/scopeguard]")
{
SECTION("success")
{
int test = 0;
try {
const based::scopeguard_exit guard = set(test);
} catch (...) {
REQUIRE(false);
}
REQUIRE(test == 1);
}

// breaks coverage for some reason
SECTION("failure")
{
int test = 0;
try {
const based::scopeguard_exit guard = set(test);
throw std::runtime_error {"should not leak"};
} catch (...) {
test *= 1;
}
REQUIRE(test == 1);
}
}

// NOLINTEND cognitive-complexity