Unverified Commit 2a83ea58 authored by Joseph Noir's avatar Joseph Noir Committed by GitHub

Merge pull request #791

Add copy-on-write tuple implementation
parents 9cdc4b4b e71d8bce
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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 <tuple>
#include "caf/detail/comparable.hpp"
#include "caf/detail/tuple_vals.hpp"
#include "caf/make_copy_on_write.hpp"
namespace caf {
/// A copy-on-write tuple implementation.
template <class... Ts>
class cow_tuple : detail::comparable<cow_tuple<Ts...>>,
detail::comparable<cow_tuple<Ts...>, std::tuple<Ts...>> {
public:
// -- member types -----------------------------------------------------------
using impl = detail::tuple_vals<Ts...>;
using data_type = std::tuple<Ts...>;
// -- constructors, destructors, and assignment operators --------------------
explicit cow_tuple(Ts... xs)
: ptr_(make_copy_on_write<impl>(std::move(xs)...)) {
// nop
}
cow_tuple() : ptr_(make_copy_on_write<impl>()) {
// nop
}
cow_tuple(cow_tuple&&) = default;
cow_tuple(const cow_tuple&) = default;
cow_tuple& operator=(cow_tuple&&) = default;
cow_tuple& operator=(const cow_tuple&) = default;
// -- properties -------------------------------------------------------------
/// Returns the managed tuple.
const data_type& data() const noexcept {
return ptr_->data();
}
/// Returns a mutable reference to the managed tuple, guaranteed to have a
/// reference count of 1.
data_type& unshared() {
return ptr_.unshared().data();
}
/// Returns whether the reference count of the managed object is 1.
bool unique() const noexcept {
return ptr_->unique();
}
/// @private
const intrusive_cow_ptr<impl>& ptr() const noexcept {
return ptr_;
}
// -- comparison -------------------------------------------------------------
template <class... Us>
int compare(const std::tuple<Us...>& other) const noexcept {
return data() < other ? -1 : (data() == other ? 0 : 1);
}
template <class... Us>
int compare(const cow_tuple<Us...>& other) const noexcept {
return compare(other.data());
}
private:
// -- member variables -------------------------------------------------------
intrusive_cow_ptr<impl> ptr_;
};
/// @relates cow_tuple
template <class Inspector, class... Ts>
typename std::enable_if<Inspector::reads_state,
typename Inspector::result_type>::type
inspect(Inspector& f, const cow_tuple<Ts...>& x) {
return f(x.data());
}
/// @relates cow_tuple
template <class Inspector, class... Ts>
typename std::enable_if<Inspector::writes_state,
typename Inspector::result_type>::type
inspect(Inspector& f, cow_tuple<Ts...>& x) {
return f(x.unshared());
}
/// Creates a new copy-on-write tuple from given arguments.
/// @relates cow_tuple
template <class... Ts>
cow_tuple<typename std::decay<Ts>::type...> make_cow_tuple(Ts&&... xs) {
return cow_tuple<typename std::decay<Ts>::type...>{std::forward<Ts>(xs)...};
}
/// Convenience function for calling `get<N>(xs.data())`.
/// @relates cow_tuple
template <size_t N, class... Ts>
auto get(const cow_tuple<Ts...>& xs) -> decltype(std::get<N>(xs.data())) {
return std::get<N>(xs.data());
}
} // 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 cow_tuple
#include "caf/cow_tuple.hpp"
#include "caf/test/dsl.hpp"
using std::make_tuple;
using std::string;
using std::tuple;
using namespace caf;
CAF_TEST(default_construction) {
cow_tuple<string, string> x;
CAF_CHECK_EQUAL(x.unique(), true);
CAF_CHECK_EQUAL(get<0>(x), "");
CAF_CHECK_EQUAL(get<1>(x), "");
}
CAF_TEST(value_construction) {
cow_tuple<int, int> x{1, 2};
CAF_CHECK_EQUAL(x.unique(), true);
CAF_CHECK_EQUAL(get<0>(x), 1);
CAF_CHECK_EQUAL(get<1>(x), 2);
CAF_CHECK_EQUAL(x, make_cow_tuple(1, 2));
}
CAF_TEST(copy_construction) {
cow_tuple<int, int> x{1, 2};
cow_tuple<int, int> y{x};
CAF_CHECK_EQUAL(x, y);
CAF_CHECK_EQUAL(x.ptr(), y.ptr());
CAF_CHECK_EQUAL(x.unique(), false);
CAF_CHECK_EQUAL(y.unique(), false);
}
CAF_TEST(move_construction) {
cow_tuple<int, int> x{1, 2};
cow_tuple<int, int> y{std::move(x)};
CAF_CHECK_EQUAL(x.ptr(), nullptr);
CAF_CHECK_EQUAL(y, make_tuple(1, 2));
CAF_CHECK_EQUAL(y.unique(), true);
}
CAF_TEST(copy_assignment) {
cow_tuple<int, int> x{1, 2};
cow_tuple<int, int> y{3, 4};
CAF_CHECK_NOT_EQUAL(x, y);
x = y;
CAF_CHECK_EQUAL(x, y);
CAF_CHECK_EQUAL(x.ptr(), y.ptr());
CAF_CHECK_EQUAL(x.unique(), false);
CAF_CHECK_EQUAL(y.unique(), false);
}
CAF_TEST(move_assignment) {
cow_tuple<int, int> x{1, 2};
cow_tuple<int, int> y{3, 4};
CAF_CHECK_NOT_EQUAL(x, y);
x = std::move(y);
CAF_CHECK_EQUAL(x, make_tuple(3, 4));
CAF_CHECK_EQUAL(x.unique(), true);
CAF_CHECK_EQUAL(y.ptr(), nullptr);
}
CAF_TEST(make_cow_tuple) {
cow_tuple<int, int> x{1, 2};
auto y = make_cow_tuple(1, 2);
CAF_CHECK_EQUAL(x, y);
CAF_CHECK_EQUAL(x.unique(), true);
CAF_CHECK_EQUAL(y.unique(), true);
}
CAF_TEST(unsharing) {
auto x = make_cow_tuple(string{"old"}, string{"school"});
auto y = x;
CAF_CHECK_EQUAL(x.unique(), false);
CAF_CHECK_EQUAL(y.unique(), false);
get<0>(y.unshared()) = "new";
CAF_CHECK_EQUAL(x.unique(), true);
CAF_CHECK_EQUAL(y.unique(), true);
CAF_CHECK_EQUAL(x.data(), make_tuple("old", "school"));
CAF_CHECK_EQUAL(y.data(), make_tuple("new", "school"));
}
CAF_TEST(to_string) {
auto x = make_cow_tuple(1, string{"abc"});
CAF_CHECK_EQUAL(deep_to_string(x), "(1, \"abc\")");
}
CAF_TEST_FIXTURE_SCOPE(cow_tuple_tests, test_coordinator_fixture<>)
CAF_TEST(serialization) {
auto x = make_cow_tuple(1, 2, 3);
auto y = roundtrip(x);
CAF_CHECK_EQUAL(x, y);
CAF_CHECK_EQUAL(x.unique(), true);
CAF_CHECK_EQUAL(y.unique(), true);
CAF_CHECK_NOT_EQUAL(x.ptr(), y.ptr());
}
CAF_TEST_FIXTURE_SCOPE_END()
......@@ -551,6 +551,9 @@ public:
/// A deterministic scheduler type.
using scheduler_type = caf::scheduler::test_coordinator;
/// A buffer for serializing or deserializing objects.
using byte_buffer = std::vector<char>;
// -- constructors, destructors, and assignment operators --------------------
template <class... Ts>
......@@ -702,6 +705,29 @@ public:
return dynamic_cast<T&>(*ptr);
}
template <class... Ts>
byte_buffer serialize(const Ts&... xs) {
byte_buffer buf;
caf::binary_serializer sink{sys, buf};
if (auto err = sink(xs...))
CAF_FAIL("serialization failed: " << sys.render(err));
return buf;
}
template <class... Ts>
void deserialize(const byte_buffer& buf, Ts&... xs) {
caf::binary_deserializer source{sys, buf};
if (auto err = source(xs...))
CAF_FAIL("deserialization failed: " << sys.render(err));
}
template <class T>
T roundtrip(const T& x) {
T result;
deserialize(serialize(x), result);
return result;
}
// -- member variables -------------------------------------------------------
/// The user-generated system config.
......
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