Skip to content
Snippets Groups Projects
Unverified Commit f0b5892c authored by Sam Yates's avatar Sam Yates Committed by GitHub
Browse files

Wrap std::function for sup::on_scope_exit. (#665)

* Provide a helper wrapper for use behind the scenes in the
implementation of `sup::on_scope_exit` so that we can work around
`std::function` not being nothrow move constructible (and maintaining
the nothrow move on the `sup::scope_exit` structure).

Fixes #664.
parent 6db581c1
No related branches found
No related tags found
No related merge requests found
#pragma once
#include <functional>
#include <type_traits>
#include <utility>
......@@ -42,9 +43,34 @@ public:
}
};
// std::function is not nothrow move constructable before C++20, so, er, cheat.
namespace impl {
template <typename R>
struct wrap_std_function {
std::function<R ()> f;
wrap_std_function() noexcept {}
wrap_std_function(const std::function<R ()>& f): f(f) {}
wrap_std_function(std::function<R ()>&& f): f(std::move(f)) {}
wrap_std_function(wrap_std_function&& other) noexcept {
try {
f = std::move(other.f);
}
catch (...) {}
}
void operator()() const { f(); }
};
}
template <typename F>
scope_exit<std::decay_t<F>> on_scope_exit(F&& f) {
auto on_scope_exit(F&& f) {
return scope_exit<std::decay_t<F>>(std::forward<F>(f));
}
template <typename R>
auto on_scope_exit(std::function<R ()> f) {
return on_scope_exit(impl::wrap_std_function<R>(std::move(f)));
}
} // namespace sup
......@@ -94,6 +94,7 @@ set(unit_sources
test_schedule.cpp
test_spike_source.cpp
test_local_context.cpp
test_scope_exit.cpp
test_simd.cpp
test_span.cpp
test_spikes.cpp
......
#include <functional>
#include "../gtest.h"
#include <sup/scope_exit.hpp>
using sup::on_scope_exit;
TEST(scope_exit, basic) {
bool a = false;
{
auto guard = on_scope_exit([&a] { a = true; });
EXPECT_FALSE(a);
}
EXPECT_TRUE(a);
}
TEST(scope_exit, noexceptcall) {
auto guard1 = on_scope_exit([] {});
using G1 = decltype(guard1);
EXPECT_FALSE(noexcept(guard1.~G1()));
auto guard2 = on_scope_exit([]() noexcept {});
using G2 = decltype(guard2);
EXPECT_TRUE(noexcept(guard2.~G2()));
}
TEST(scope_exit, function) {
// on_scope_exit has a special overload for std::function
// to work around its non-noexcept move ctor.
bool a = false;
std::function<void ()> setter = [&a] { a = true; };
{
auto guard = on_scope_exit(setter);
EXPECT_FALSE(a);
}
EXPECT_TRUE(a);
a = false;
std::function<int ()> setter2 = [&a] { a = true; return 3; };
{
auto guard = on_scope_exit(setter2);
EXPECT_FALSE(a);
}
EXPECT_TRUE(a);
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment