/* REPLACEABLE-ITEM.hpp - adapter to take snapshot from non-assignable values Copyright (C) Lumiera.org 2017, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** @file replaceable-item.hpp ** Adapter wrapper to treat non-assignable values as if they were assignable. ** Prerequisite is for the type to be copy constructible (maybe even just move constructible). ** The typical usage is when we want to take a _snapshot_ from some value, but do not actually ** need the ability to assign something to a specific object instance. In such a situation, ** we may just destroy a previous snapshot and then place a new copy initialised value into ** the same storage space. For all really assignable types we fall back to a trivial ** implementation. ** ** \par motivation ** The typical usage is in library or framework code, where we do not know what types to expect. ** For example, the Lumiera command framework automatically captures an _UNDO memento_ based ** on the return type of a state capturing function. It would be highly surprising for the ** user if it wasn't possible to capture e.g. time values or durations as _old state_, ** because these entities can not be re-assigned with new values, only created. ** But obviously a command handling framework needs the ability to capture ** state consecutively several times, and this adapter was created ** to bridge this conceptual discrepancy ** ** \par extensions ** - the type can be solely move constructible, in which case the payload needs to be ** moved explicitly when embedding into this adapter ///////////////////////////////////////////////TICKET 1059 : does not work yet on GCC-4.9 ** - when the payload is equality comparable, the container is as well, by delegation ** - container instances can be default created, which emplaces an _empty value_. ** The actual value to embed is retrieved from the lib::NullValue template, ** which is a static per-type singleton. ** - thus types which are not even default constructible can still be used, ** by providing an explicit specialisation of lib::NullValue for this type. ** ** @see ReplaceableIterm_test ** @see proc::control::MementoTie ** */ #ifndef LIB_REPLACEABLE_ITEM_H #define LIB_REPLACEABLE_ITEM_H #include "lib/error.hpp" #include "lib/null-value.hpp" #include "lib/meta/util.hpp" #include "lib/util.hpp" #include #include namespace lib { namespace wrapper { using util::isSameObject; using util::unConst; using std::forward; /** * Adapter container to take snapshots from non-assignable values. * Implemented by placing the subject into an inline buffer. * @note uses lib::NullValue to retrieve an _empty payload_. */ template class ReplaceableItem { char content_[sizeof(X)]; public: ReplaceableItem() { emplace (NullValue::get()); } ~ReplaceableItem() { destroy(); } template ReplaceableItem (Z&& otherValue) { emplace (forward (otherValue)); } template ReplaceableItem& operator= (Z&& otherValue) { if (not isSameObject(*this, otherValue)) reAssign (forward (otherValue)); return *this; } ReplaceableItem& clear() noexcept { reAssign (NullValue::get()); return *this; } operator X&() { return access(); } operator X const&() const { return unConst(this)->access(); } X& get() { return access(); } X const& get() const { return unConst(this)->access(); } private: X& access() { return reinterpret_cast(content_); } void destroy() { access().~X(); } template void emplace (Z&& otherValue) try { new(&content_) X{forward (otherValue)}; } catch(...) { NullValue::build (&content_); } template void reAssign (Z&& otherValue) { destroy(); emplace (forward (otherValue)); } }; template struct is_assignable_value : std::__and_, std::__not_>> { }; /** * simple delegating implementation * to use for regular cases */ template class ReplaceableItem>> { X val_; public: ReplaceableItem() : val_(NullValue::get()) { } ReplaceableItem(X const& val) : val_(val) { } ReplaceableItem(X && val) : val_(forward(val)) { } ReplaceableItem& operator= (X const& val) { val_=val; return *this; } ReplaceableItem& operator= (X && val) { val_=forward(val); return *this; } ReplaceableItem& clear() noexcept { val_=NullValue::get(); return *this; } operator X&() { return val_; } operator X const&() const { return val_; } X& get() { return val_; } X const& get() const { return val_; } }; /** disallow embedding references */ template class ReplaceableItem>> { static_assert( not sizeof(X), "ReplaceableItem for references is pointless"); }; /* ===== Equality comparison delegated to contained element ===== */ template inline bool operator== (ReplaceableItem const& li, ReplaceableItem const& ri) { return li.get() == ri.get(); } template inline bool operator!= (ReplaceableItem const& li, ReplaceableItem const& ri) { return li.get() != ri.get(); } template inline bool operator== (ReplaceableItem const& item, Z const& something) { return item.get() == something; } template inline bool operator!= (ReplaceableItem const& item, Z const& something) { return item.get() != something; } template inline bool operator== (Z const& something, ReplaceableItem const& item) { return item.get() == something; } template inline bool operator!= (Z const& something, ReplaceableItem const& item) { return item.get() != something; } }} // namespace lib::wrap #endif /*LIB_REPLACEABLE_ITEM_H*/