/* WRAPPER.hpp - some smart wrapping and reference managing helper templates Copyright (C) Lumiera.org 2009, 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 wrapper.hpp ** Library implementation: smart-pointer variations, wrappers and managing holders. ** This is (intended to become) a loose collection of the various small helper templates ** for wrapping, containing, placing or handling a somewhat \em problematic other object. ** Mostly these are implemented to suit a specific need and then factored out later on. ** - ItemWrapper is a similar concept, but more like a smart-ptr. Moreover, ** it can be instantiated with a value type, a pointer or a reference type, ** yielding the same behaviour in all cases (useful for building templates) ** - FunctionResult is the combination of ItemWrapper with a functor object ** to cache the function result value. ** ** @see lib::TransformIter ** ** @todo 2017 consider to split off the FunctionResult into a dedicated header to reduce includes ** */ #ifndef LIB_WRAPPER_H #define LIB_WRAPPER_H #include "lib/error.hpp" #include "lib/nocopy.hpp" #include "lib/meta/function.hpp" #include "lib/meta/function-closure.hpp" #include "lib/meta/util.hpp" #include "lib/util.hpp" #include #include namespace lib { namespace wrapper { using util::unConst; using util::isSameObject; using lib::meta::_Fun; using LERR_(BOTTOM_VALUE); using std::function; /** * Reference wrapper implemented as constant function, * returning the (fixed) reference on invocation */ template class ReturnRef { T& ref_; public: ReturnRef(T& target) : ref_(target) { } T& operator() () const { return ref_;} }; template ReturnRef refFunction (T& target) { return ReturnRef (target); } /** * Universal value/ref wrapper accessible similar to a pointer. * A copyable, assignable value object to hold a given value within * an embedded inline buffer. It can be default constructed and `bool` * evaluated to detect an empty holder. The value is retrieved through * a pointer-like interface, by explicit dereferentiation. (Contrast this * to std::ref, where the original reference is retrieved by conversion). * @note when the embedded value is a pointer, ItemWrapper does _not_ take * ownership of and manage data the pointer is pointing to. * * The purpose of this template is to be able to remember * pretty much any kind of value or pointer or reference, * and to subsume this handling within a single template. * An example would be to remember the value yielded * by a function, without any further assumptions * regarding this function. */ template class ItemWrapper { using TY_unconst = std::remove_const_t; alignas(TY) mutable std::byte content_[sizeof(TY)]; bool created_; template void build (REF&& ref) { new(&content_) TY{std::forward (ref)}; created_ = true; } void discard () { if (created_) access().~TY(); created_ = false; } TY& access () const { return * std::launder (reinterpret_cast (&content_)); } TY_unconst& access_unconst() const ///< used to assign new buffer contents { return const_cast (access()); } public: ItemWrapper() : created_(false) { } explicit ItemWrapper(TY const& o) : created_(false) { build (o); } explicit ItemWrapper(TY && ro) : created_(false) { build (std::move(ro)); } ~ItemWrapper() { discard(); } /* == copy and assignment == */ ItemWrapper (ItemWrapper const& ref) : created_(false) { if (ref.isValid()) build (*ref); } ItemWrapper (ItemWrapper && rref) : created_(false) { if (rref.isValid()) { build (std::move (*rref)); rref.discard(); } } ItemWrapper& operator= (ItemWrapper const& cref) { if (!cref.isValid()) discard(); else this->operator= (*cref); return *this; } ItemWrapper& operator= (ItemWrapper & ref) { return *this = (ItemWrapper const&)ref; } ItemWrapper& operator= (ItemWrapper && rref) { if (!rref.isValid()) discard(); else { this->operator= (std::move(*rref)); rref.discard(); } return *this; } /** * Emulate »assignment« by discarding and then construction of a new payload * @param something from which TY can be (copy/move)constructed * @remark allows handling »move-only« types; for the typical use case, something * new is fabricated in a lambda and then moved into the ItemWrapper; thus * the performance overhead of destroy/re-created is not deemed relevant. * SFINAE tricks on hidden/deleted assignment operator can be unreliable. */ template ItemWrapper& operator= (X&& something) { if (!isSameObject (something, access() )) { discard(); build (std::forward(something)); } return *this; } /** implant a default-initialised instance of the payload type */ ItemWrapper& defaultInit() { discard(); build (TY()); return *this; } operator bool() const { return isValid(); } /* == value access == */ TY& operator* () const { if (!created_) throw lumiera::error::State ("accessing uninitialised value/ref wrapper" , LERR_(BOTTOM_VALUE)); return access(); } TY* operator-> () const { return & **this; } bool isValid () const { return created_; } void reset () { discard(); } }; /** * Specialisation of the ItemWrapper to deal with references, * as if they were pointer values. Allows the reference value * to be default constructed to ⟂ (invalid) and to be re-assigned. */ template class ItemWrapper { TY * content_; public: ItemWrapper() : content_() { } explicit ItemWrapper(TY& o) : content_( &o ) { } // using default copy and assignment operator bool() const { return isValid(); } /** allow to re-bind the reference */ ItemWrapper& operator= (TY& otherRef) { content_ = &otherRef; return *this; } ItemWrapper& defaultInit() ///< @note just reset for this specialisation { reset(); return *this; } /* == value access == */ TY& operator* () const { if (!content_) throw lumiera::error::State ("accessing uninitialised reference wrapper" , LERR_(BOTTOM_VALUE)); return *content_; } bool isValid () const { return bool(content_); } void reset () { content_ = 0; } }; /** * Fallback-specialisation for `ItemWrapper`. * @remark This can be relevant when ItemWrapper is used to capture function results, * yet the given function has return type `void` and is used for side-effects. */ template<> class ItemWrapper { public: ItemWrapper() { } // using default copy and assignment operator bool() const { return true; } bool isValid () const { return true; } void reset () { /* NOP */ } /** @warning does nothing */ void operator*() const { /* NOP */ } ItemWrapper& defaultInit() ///< no effect for this specialisation { return *this; } }; /** allow equality comparison if the wrapped types are comparable */ template inline bool operator== (ItemWrapper const& w1, ItemWrapper const& w2) { return (!w1 && !w2) || ( w1 && w2 && (*w1)==(*w2)); } template inline bool operator!= (ItemWrapper const& w1, ItemWrapper const& w2) { return not (w1 == w2); } /** * Extension of ItemWrapper: a function remembering the result of the * last invocation. Initially, the "value" is bottom (undefined, NIL), * until the function is invoked for the first time. After that, the * result of the last invocation can be accessed by `operator* ()` * @note deliberately non-copyable, since we capture a reference * to `this` in order to write to the embedded ItemWrapper. * (to alleviate, we'd have to re-link after copying/moving) */ template struct FunctionResult : public function , util::NonCopyable { using Res = typename _Fun::Ret; using ResWrapper = ItemWrapper; ResWrapper lastResult_; public: /** by default locked to _invalid state_ */ FunctionResult() = default; /** * Create result-remembering functor by outfitting a _copy_ * of the given function with an adaptor to _capture_ each * produced result. * @warning if function result is a value, it is copied. */ template FunctionResult (FUN&& targetFunction) : function{lib::meta::func::chained ( std::forward (targetFunction) , [this](Res res) -> Res { lastResult_ = res; return std::forward (res); })} { } /** retrieve the last function result observed */ Res& operator*() const { return *lastResult_; } bool isValid () const { return lastResult_.isValid(); } explicit operator bool() const { return isValid(); } }; }} // namespace lib::wrap #endif