Commit 63fc9aa4 authored by Dominik Charousset's avatar Dominik Charousset Committed by Dominik Charousset

Add move-only replacement for std::function

parent ec6a8927
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <type_traits>
namespace caf {
namespace detail {
/// A move-only replacement for `std::function`.
template <class Signature>
class unique_function;
template <class R, class... Ts>
class unique_function<R (Ts...)> {
public:
// -- member types -----------------------------------------------------------
/// Function object that dispatches application with a virtual member
/// function.
class wrapper {
public:
virtual ~wrapper() {
// nop
}
virtual R operator()(Ts...) = 0;
};
/// Native function pointer.
using raw_pointer = R (*)(Ts...);
/// Pointer to a function wrapper.
using wrapper_pointer = wrapper*;
// -- factory functions ------------------------------------------------------
/// Creates a new function object wrapper.
template <class F>
static wrapper_pointer make_wrapper(F&& f) {
class impl final : public wrapper {
public:
impl(F&& fun) : fun_(std::move(fun)) {
// nop
}
R operator()(Ts... xs) override {
return fun_(xs...);
}
private:
F fun_;
};
return new impl(std::forward<F>(f));
}
unique_function() : holds_wrapper_(false), fptr_(nullptr) {
// nop
}
unique_function(unique_function&& other)
: holds_wrapper_(other.holds_wrapper_) {
fptr_ = other.fptr_;
if (other.holds_wrapper_)
other.holds_wrapper_ = false;
other.fptr_ = nullptr;
}
unique_function(const unique_function&) = delete;
explicit unique_function(raw_pointer fun)
: holds_wrapper_(false), fptr_(fun) {
// nop
}
explicit unique_function(wrapper_pointer ptr)
: holds_wrapper_(true), wptr_(ptr) {
// nop
}
template <
class T,
class = typename std::enable_if<
!std::is_convertible<T, raw_pointer>::value
&& std::is_same<decltype((std::declval<T&>())(std::declval<Ts>()...)),
R>::value>::type>
explicit unique_function(T f) : unique_function(make_wrapper(std::move(f))) {
// nop
}
unique_function& operator=(unique_function&& other) {
destroy();
if (other.holds_wrapper_) {
holds_wrapper_ = true;
wptr_ = other.wptr_;
other.holds_wrapper_ = false;
other.fptr_ = nullptr;
} else {
holds_wrapper_ = false;
fptr_ = other.fptr_;
}
return *this;
}
unique_function& operator=(raw_pointer f) {
return *this = unique_function{f};
}
unique_function& operator=(const unique_function&) = delete;
~unique_function() {
destroy();
}
R operator()(Ts... xs) {
if (holds_wrapper_)
return (*wptr_)(std::move(xs)...);
return (*fptr_)(std::move(xs)...);
}
bool is_nullptr() const noexcept {
// No type dispatching needed, because the union puts both pointers into
// the same memory location.
return fptr_ == nullptr;
}
bool holds_wrapper() const noexcept {
return holds_wrapper_;
}
explicit operator bool() const noexcept {
// No type dispatching needed, because the union puts both pointers into
// the same memory location.
return !is_nullptr();
}
bool operator!() const noexcept {
return is_nullptr();
}
private:
/// Destroys the managed wrapper.
void destroy() {
if (holds_wrapper_)
delete wptr_;
}
// -- member variables -------------------------------------------------------
bool holds_wrapper_;
union {
raw_pointer fptr_;
wrapper_pointer wptr_;
};
};
template <class T>
bool operator==(const unique_function<T>& x, std::nullptr_t) noexcept {
return x.is_nullptr();
}
template <class T>
bool operator==(std::nullptr_t, const unique_function<T>& x) noexcept {
return x.is_nullptr();
}
template <class T>
bool operator!=(const unique_function<T>& x, std::nullptr_t) noexcept {
return !x.is_nullptr();
}
template <class T>
bool operator!=(std::nullptr_t, const unique_function<T>& x) noexcept {
return !x.is_nullptr();
}
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE unique_function
#include "caf/detail/unique_function.hpp"
#include "caf/test/dsl.hpp"
namespace {
using int_fun = caf::detail::unique_function<int()>;
int fourty_two() {
return 42;
}
class instance_counting_wrapper final: public int_fun::wrapper {
public:
instance_counting_wrapper(size_t* instance_counter)
: instance_counter_(instance_counter) {
*instance_counter_ += 1;
}
~instance_counting_wrapper() {
*instance_counter_ -= 1;
}
int operator()() final {
return 42;
}
private:
size_t* instance_counter_;
};
} // namespace <anonymous>
#define CHECK_VALID(f) \
CAF_CHECK(!f.is_nullptr()); \
CAF_CHECK(f); \
CAF_CHECK(f != nullptr); \
CAF_CHECK(nullptr != f); \
CAF_CHECK(!(f == nullptr)); \
CAF_CHECK(!(nullptr == f)); \
CAF_CHECK(f() == 42)
#define CHECK_INVALID(f) \
CAF_CHECK(f.is_nullptr()); \
CAF_CHECK(!f); \
CAF_CHECK(f == nullptr); \
CAF_CHECK(nullptr == f); \
CAF_CHECK(!(f != nullptr)); \
CAF_CHECK(!(nullptr != f)); \
CAF_CHECK(!f.holds_wrapper())
CAF_TEST(default construction) {
int_fun f;
CHECK_INVALID(f);
}
CAF_TEST(raw function pointer construction) {
int_fun f{fourty_two};
CHECK_VALID(f);
}
CAF_TEST(stateless lambda construction) {
int_fun f{[] { return 42; }};
CHECK_VALID(f);
CAF_CHECK(!f.holds_wrapper());
}
CAF_TEST(stateful lambda construction) {
int i = 42;
int_fun f{[=] { return i; }};
CHECK_VALID(f);
CAF_CHECK(f.holds_wrapper());
}
CAF_TEST(custom wrapper construction) {
size_t instances = 0;
{ // lifetime scope of our counting wrapper
int_fun f{new instance_counting_wrapper(&instances)};
CHECK_VALID(f);
CAF_CHECK(f.holds_wrapper());
CAF_CHECK(instances == 1);
}
CAF_CHECK(instances == 0);
}
CAF_TEST(function move construction) {
int_fun f{fourty_two};
int_fun g{std::move(f)};
CHECK_INVALID(f);
CHECK_VALID(g);
CAF_CHECK(!g.holds_wrapper());
}
CAF_TEST(stateful lambda move construction) {
int i = 42;
int_fun f{[=] { return i; }};
int_fun g{std::move(f)};
CHECK_INVALID(f);
CHECK_VALID(g);
CAF_CHECK(g.holds_wrapper());
}
CAF_TEST(custom wrapper move construction) {
size_t instances = 0;
{ // lifetime scope of our counting wrapper
int_fun f{new instance_counting_wrapper(&instances)};
int_fun g{std::move(f)};
CHECK_INVALID(f);
CHECK_VALID(g);
CAF_CHECK(g.holds_wrapper());
CAF_CHECK(instances == 1);
}
CAF_CHECK(instances == 0);
}
CAF_TEST(function assign) {
size_t instances = 0;
int_fun f;
int_fun g{fourty_two};
int_fun h{new instance_counting_wrapper(&instances)};
CAF_CHECK(instances == 1);
CHECK_INVALID(f);
CHECK_VALID(g);
CHECK_VALID(h);
f = fourty_two;
g = fourty_two;
h = fourty_two;
CAF_CHECK(instances == 0);
CHECK_VALID(f);
CHECK_VALID(g);
CHECK_VALID(h);
}
CAF_TEST(move assign) {
size_t instances = 0;
int_fun f;
int_fun g{fourty_two};
int_fun h{new instance_counting_wrapper(&instances)};
CAF_CHECK(instances == 1);
CHECK_INVALID(f);
CHECK_VALID(g);
CHECK_VALID(h);
g = std::move(h);
CAF_CHECK(instances == 1);
CHECK_INVALID(f);
CHECK_VALID(g);
CHECK_INVALID(h);
f = std::move(g);
CAF_CHECK(instances == 1);
CHECK_VALID(f);
CHECK_INVALID(g);
CHECK_INVALID(h);
f = int_fun{};
CAF_CHECK(instances == 0);
CHECK_INVALID(f);
CHECK_INVALID(g);
CHECK_INVALID(h);
}
Markdown is supported
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