Instantly share code, notes, and snippets.
Created
July 10, 2019 12:44
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save inetic/dc6fcef2b7c13d0adc1e4f011eab68cf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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