Skip to content

Instantly share code, notes, and snippets.

@inetic
Created July 10, 2019 12:44
Show Gist options
  • Save inetic/dc6fcef2b7c13d0adc1e4f011eab68cf to your computer and use it in GitHub Desktop.
Save inetic/dc6fcef2b7c13d0adc1e4f011eab68cf to your computer and use it in GitHub Desktop.
#pragma once
#include <boost/optional.hpp>
namespace util {
namespace shared_value_detail {
using boost::optional;
// Some values that we'd like to put into shared_value don't have the move
// assign operator. As long as they have the move constructor we can still
// use them.
template<typename T, bool has_move_assign_op> struct assign;
template<typename T>
struct assign<optional<T>, true> {
static void move(optional<T>& dst, optional<T>& src) {
dst = std::move(src);
}
};
template<typename T>
struct assign<optional<T>, false> {
static void move(optional<T>& dst, optional<T>& src) {
// Destroying should be cheap because we always destroy
// non initialized optional<T>.
assert(dst == boost::none);
dst.~optional<T>();
new (&dst) optional<T>(std::move(src));
}
};
};
template<typename T>
class shared_value {
public:
using value_type = std::decay_t<T>;
shared_value() = default;
shared_value(const shared_value&);
shared_value& operator=(const shared_value&);
template<typename K> shared_value(K);
value_type& operator*() { return *value(); }
const value_type& operator*() const { return *value(); }
value_type* operator->() { return value(); }
const value_type* operator->() const { return value(); }
~shared_value();
private:
void remove_self();
void append_self_to_end(const shared_value&);
value_type* value();
private:
shared_value* _prev = nullptr;
shared_value* _next = nullptr;
// We use optional to handle the case where value_type
// doesn't have the default constructor.
boost::optional<value_type> _value;
};
template<typename T>
inline
shared_value<T>::shared_value(const shared_value& other)
{
append_self_to_end(other);
}
template<typename T>
template<typename K>
inline
shared_value<T>::shared_value(K value)
: _value(std::move(value))
{
}
template<typename T>
inline
shared_value<T>& shared_value<T>::operator=(const shared_value& other)
{
// Do nothing if this and other already share the same value.
if (value() == other.value()) return *this;
remove_self();
append_self_to_end(other);
return this;
}
template<typename T>
inline
void shared_value<T>::append_self_to_end(const shared_value& other)
{
shared_value* last = const_cast<shared_value*>(&other);
while (last->_next) { last = last->_next; }
last->_next = this;
_prev = last;
assert(_next = nullptr);
}
template<typename T>
inline
void shared_value<T>::remove_self()
{
if (_prev) {
// We're not the first, so we don't have the value
assert(!_value);
_prev->_next = _next;
if (_next) { _next->_prev = _prev; _next = nullptr; }
_prev = nullptr;
return;
}
if (_next == nullptr) {
// We're the value owners, but we're not sharing it with anyone else.
// Thus destroy the value and exit.
_value = boost::none;
return;
}
// We're the owners of the value and we're sharing it with others. Move the
// value to the next one in line.
shared_value_detail::assign< boost::optional<value_type>
, std::is_move_assignable<value_type>::value
>::move(_next->_value, _value);
_value = boost::none;
_next->_prev = nullptr;
_next = nullptr;
}
template<typename T>
inline
typename shared_value<T>::value_type* shared_value<T>::value()
{
shared_value* first = this;
while (first->_prev) { first = first->_prev; }
if (!first->_value) return nullptr;
return &*first->_value;
}
template<typename T>
inline
shared_value<T>::~shared_value()
{
remove_self();
}
} // namespace
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment