2009-07-04 03:32:15 +02:00
|
|
|
/*
|
|
|
|
|
OPAQUE-HOLDER.hpp - buffer holding an object inline while hiding the concrete type
|
2010-12-17 23:28:49 +01:00
|
|
|
|
2009-07-04 03:32:15 +02:00
|
|
|
Copyright (C) Lumiera.org
|
|
|
|
|
2009, Hermann Vosseler <Ichthyostega@web.de>
|
2010-12-17 23:28:49 +01:00
|
|
|
|
2009-07-04 03:32:15 +02:00
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
|
modify it under the terms of the GNU General Public License as
|
2010-12-17 23:28:49 +01:00
|
|
|
published by the Free Software Foundation; either version 2 of
|
|
|
|
|
the License, or (at your option) any later version.
|
|
|
|
|
|
2009-07-04 03:32:15 +02:00
|
|
|
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.
|
2010-12-17 23:28:49 +01:00
|
|
|
|
2009-07-04 03:32:15 +02:00
|
|
|
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.
|
2010-12-17 23:28:49 +01:00
|
|
|
|
2009-07-04 03:32:15 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/** @file opaque-holder.hpp
|
|
|
|
|
** Helper allowing type erasure while holding the actual object inline.
|
|
|
|
|
** Controlling the actual storage of objects usually binds us to commit
|
|
|
|
|
** to a specific type, thus ruling out polymorphism. But sometimes, when
|
|
|
|
|
** we are able to control the maximum storage for a family of classes, we
|
|
|
|
|
** can escape this dilemma by using the type erasure pattern combined with
|
2009-07-04 04:35:17 +02:00
|
|
|
** an inline buffer holding an object of the concrete subclass. Typically,
|
2009-07-04 03:32:15 +02:00
|
|
|
** this situation arises when dealing with functor objects.
|
|
|
|
|
**
|
2009-07-06 02:25:19 +02:00
|
|
|
** These templates help with building custom objects and wrappers based on
|
2010-11-19 05:01:43 +01:00
|
|
|
** this pattern: lib::InPlaceAnyHolder provides a buffer for target objects
|
2009-07-06 02:25:19 +02:00
|
|
|
** and controls access through a two-layer capsule; while the outer container
|
|
|
|
|
** exposes a neutral interface, the inner container keeps track of the actual
|
|
|
|
|
** type by means of a vtable. OpaqueHolder is built on top of InPlaceAnyHolder
|
|
|
|
|
** additionally to support a "common base interface" and re-access of the
|
|
|
|
|
** embedded object through this interface. For this to work, all of the
|
|
|
|
|
** stored types need to be derived from this common base interface.
|
|
|
|
|
** OpaqueHolder then may be even used like a smart-ptr, exposing this
|
|
|
|
|
** base interface. To the contrary, InPlaceAnyHolder has lesser requirements
|
|
|
|
|
** on the types to be stored within. It can be configured with policy classes
|
|
|
|
|
** to control the re-access; when using InPlaceAnyHolder_unrelatedTypes
|
|
|
|
|
** the individual types to be stored need not be related in any way, but
|
|
|
|
|
** of course this rules out anything beyond re-accessing the embedded object
|
|
|
|
|
** by knowing it's exact type. Generally speaking, re-accessing the concrete
|
2009-07-04 03:32:15 +02:00
|
|
|
** object requires knowledge of the actual type, similar to boost::any
|
2010-11-19 05:01:43 +01:00
|
|
|
** (but contrary to OpaqueHolder the latter uses heap storage).
|
|
|
|
|
**
|
|
|
|
|
** As a supplement, a more lightweight implementation is provided as
|
|
|
|
|
** lib::InPlaceBuffer, requiring just the object storage and lacking the
|
|
|
|
|
** ability to track the actual type of the embedded object.
|
2009-07-04 03:32:15 +02:00
|
|
|
**
|
2009-07-04 04:35:17 +02:00
|
|
|
** Using this approach is bound to specific stipulations regarding the
|
2009-07-04 03:32:15 +02:00
|
|
|
** properties of the contained object and the kind of access needed.
|
|
|
|
|
** When, to the contrary, the contained types are \em not related
|
|
|
|
|
** and you need to re-discover their concrete type, then maybe
|
|
|
|
|
** a visitor or variant record might be a better solution.
|
|
|
|
|
**
|
|
|
|
|
** @see opaque-holder-test.cpp
|
|
|
|
|
** @see function-erasure.hpp usage example
|
|
|
|
|
** @see variant.hpp
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef LIB_OPAQUE_HOLDER_H
|
|
|
|
|
#define LIB_OPAQUE_HOLDER_H
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/error.hpp"
|
2015-04-28 04:49:08 +02:00
|
|
|
#include "lib/access-casted.hpp"
|
2017-08-11 20:23:46 +02:00
|
|
|
#include "lib/meta/util.hpp"
|
2009-07-04 19:30:54 +02:00
|
|
|
#include "lib/util.hpp"
|
2009-07-04 03:32:15 +02:00
|
|
|
|
2016-02-06 22:17:48 +01:00
|
|
|
#include <utility>
|
2014-05-09 00:49:15 +02:00
|
|
|
#include <type_traits>
|
2009-07-18 22:07:46 +02:00
|
|
|
#include <boost/noncopyable.hpp>
|
|
|
|
|
|
2009-07-04 03:32:15 +02:00
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
|
2015-04-28 04:49:08 +02:00
|
|
|
namespace error = lumiera::error;
|
|
|
|
|
|
2009-07-04 19:30:54 +02:00
|
|
|
using util::isSameObject;
|
|
|
|
|
using util::unConst;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace { // implementation helpers...
|
|
|
|
|
|
2017-08-11 20:23:46 +02:00
|
|
|
using lib::meta::enable_if;
|
|
|
|
|
using lib::meta::disable_if;
|
2014-05-09 00:49:15 +02:00
|
|
|
using std::is_constructible;
|
2009-07-04 19:30:54 +02:00
|
|
|
|
2014-05-09 00:49:15 +02:00
|
|
|
template<typename X>
|
2017-08-11 20:23:46 +02:00
|
|
|
enable_if< is_constructible<bool,X>,
|
|
|
|
|
bool >
|
2014-05-09 00:49:15 +02:00
|
|
|
validitySelfCheck (X const& boolConvertible)
|
2009-07-04 19:30:54 +02:00
|
|
|
{
|
2014-05-09 00:49:15 +02:00
|
|
|
return bool(boolConvertible);
|
2009-07-04 19:30:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<typename X>
|
2017-08-11 20:23:46 +02:00
|
|
|
disable_if< is_constructible<bool,X>,
|
|
|
|
|
bool >
|
2009-07-04 19:30:54 +02:00
|
|
|
validitySelfCheck (X const&)
|
|
|
|
|
{
|
|
|
|
|
return true; // just pass if this type doesn't provide a validity check...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-06 02:25:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ==== Policy classes controlling re-Access ==== */
|
2009-07-05 03:38:33 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Standard policy for accessing the contents via
|
|
|
|
|
* a common base class interface. Using this policy
|
2009-07-06 02:25:19 +02:00
|
|
|
* causes static or dynamic casts or direct conversion
|
|
|
|
|
* to be employed as appropriate.
|
2009-07-05 03:38:33 +02:00
|
|
|
*/
|
2009-07-05 22:05:11 +02:00
|
|
|
template<class BA>
|
|
|
|
|
struct InPlaceAnyHolder_useCommonBase
|
2009-07-05 03:38:33 +02:00
|
|
|
{
|
2009-07-05 22:05:11 +02:00
|
|
|
typedef BA Base;
|
2009-07-05 03:38:33 +02:00
|
|
|
|
2009-07-05 22:05:11 +02:00
|
|
|
template<class SUB>
|
|
|
|
|
static Base*
|
|
|
|
|
convert2base (SUB& obj)
|
|
|
|
|
{
|
|
|
|
|
SUB* oPtr = &obj;
|
|
|
|
|
BA* asBase = util::AccessCasted<BA*>::access (oPtr);
|
|
|
|
|
if (asBase)
|
|
|
|
|
return asBase;
|
|
|
|
|
|
2015-04-28 04:49:08 +02:00
|
|
|
throw error::Logic ("Unable to convert concrete object to Base interface"
|
|
|
|
|
, error::LUMIERA_ERROR_WRONG_TYPE
|
|
|
|
|
);
|
2009-07-05 22:05:11 +02:00
|
|
|
}
|
2009-07-05 03:38:33 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
2009-07-06 02:25:19 +02:00
|
|
|
* Alternative policy for accessing the contents without
|
2009-07-05 03:38:33 +02:00
|
|
|
* a common interface; use this policy if the intention is
|
|
|
|
|
* to use OpaqueHolder with a family of similar classes,
|
|
|
|
|
* \em without requiring all of them to be derived from
|
2018-01-12 03:03:25 +01:00
|
|
|
* a common base class. (E.g. std::function objects).
|
2009-07-06 02:25:19 +02:00
|
|
|
* In this case, the "Base" type will be defined to void*
|
|
|
|
|
* As a consequence, we loose all type information and
|
|
|
|
|
* no conversions are possible on re-access. You need
|
|
|
|
|
* to know the \em exact type to get back at the object.
|
2009-07-05 03:38:33 +02:00
|
|
|
*/
|
2009-07-05 22:05:11 +02:00
|
|
|
struct InPlaceAnyHolder_unrelatedTypes
|
2009-07-05 03:38:33 +02:00
|
|
|
{
|
2009-07-05 22:05:11 +02:00
|
|
|
typedef void Base;
|
2009-07-05 03:38:33 +02:00
|
|
|
|
2009-07-05 22:05:11 +02:00
|
|
|
template<class SUB>
|
|
|
|
|
static void*
|
|
|
|
|
convert2base (SUB& obj)
|
|
|
|
|
{
|
|
|
|
|
return static_cast<void*> (&obj);
|
|
|
|
|
}
|
2009-07-05 03:38:33 +02:00
|
|
|
};
|
|
|
|
|
|
2009-07-06 02:25:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-07-04 03:32:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-04-16 00:17:35 +02:00
|
|
|
* Inline buffer to hold and own an object while concealing the
|
2009-07-06 02:25:19 +02:00
|
|
|
* concrete type. The object is given either as ctor parameter or
|
|
|
|
|
* by direct assignment; it is copy-constructed into the buffer.
|
|
|
|
|
* It is necessary to specify the required buffer storage space
|
|
|
|
|
* as a template parameter. InPlaceAnyHolder may be created empty
|
|
|
|
|
* or cleared afterwards, and this #empty() state may be detected
|
|
|
|
|
* at runtime. In a similar vein, when the stored object has a
|
|
|
|
|
* \c bool validity check, this can be accessed though #isValid().
|
2013-10-25 06:34:38 +02:00
|
|
|
* Moreover `!empty() && isValid()` may be tested as by `bool`
|
|
|
|
|
* conversion of the Holder object. The whole compound
|
2009-07-06 02:25:19 +02:00
|
|
|
* is copyable if and only if the contained object is copyable.
|
2009-07-04 19:30:54 +02:00
|
|
|
*
|
2009-07-06 02:25:19 +02:00
|
|
|
* @note assertion failure when trying to place an instance not
|
|
|
|
|
* fitting into given size.
|
|
|
|
|
* @note \em not threadsafe!
|
2015-04-16 00:17:35 +02:00
|
|
|
* @todo add support for moving of rvalue refs
|
2009-07-04 03:32:15 +02:00
|
|
|
*/
|
|
|
|
|
template
|
2009-07-05 22:05:11 +02:00
|
|
|
< size_t siz ///< maximum storage required for the targets to be held inline
|
|
|
|
|
, class AccessPolicy = InPlaceAnyHolder_unrelatedTypes
|
|
|
|
|
///< how to access the contents via a common interface?
|
2009-07-04 03:32:15 +02:00
|
|
|
>
|
2009-07-05 22:05:11 +02:00
|
|
|
class InPlaceAnyHolder
|
2009-07-04 03:32:15 +02:00
|
|
|
{
|
2009-07-05 22:05:11 +02:00
|
|
|
typedef typename AccessPolicy::Base * BaseP;
|
2009-07-04 03:32:15 +02:00
|
|
|
|
2009-07-05 22:05:11 +02:00
|
|
|
/** Inner capsule managing the contained object (interface) */
|
|
|
|
|
struct Buffer
|
2009-07-04 04:35:17 +02:00
|
|
|
{
|
|
|
|
|
char content_[siz];
|
2009-07-04 19:30:54 +02:00
|
|
|
void* ptr() { return &content_; }
|
|
|
|
|
|
2011-06-04 16:33:35 +02:00
|
|
|
virtual ~Buffer() {} ///< this is an ABC with VTable
|
2009-07-06 02:25:19 +02:00
|
|
|
virtual bool isValid() const =0;
|
|
|
|
|
virtual bool empty() const =0;
|
|
|
|
|
virtual BaseP getBase() const =0;
|
2009-07-04 19:30:54 +02:00
|
|
|
|
2009-07-06 02:25:19 +02:00
|
|
|
virtual void clone (void* targetStorage) const =0;
|
2009-07-05 22:05:11 +02:00
|
|
|
};
|
|
|
|
|
|
2009-07-06 02:25:19 +02:00
|
|
|
/** special case: no stored object */
|
2009-07-05 22:05:11 +02:00
|
|
|
struct EmptyBuff : Buffer
|
|
|
|
|
{
|
2009-07-05 03:38:33 +02:00
|
|
|
virtual bool isValid() const { return false; }
|
|
|
|
|
virtual bool empty() const { return true; }
|
2009-07-05 22:05:11 +02:00
|
|
|
|
|
|
|
|
BaseP
|
|
|
|
|
getBase() const
|
|
|
|
|
{
|
2015-04-28 04:49:08 +02:00
|
|
|
throw error::Invalid("accessing empty holder"
|
|
|
|
|
, error::LUMIERA_ERROR_BOTTOM_VALUE);
|
2009-07-05 22:05:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
|
clone (void* targetStorage) const
|
|
|
|
|
{
|
|
|
|
|
new(targetStorage) EmptyBuff();
|
|
|
|
|
}
|
2009-07-04 04:35:17 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2015-04-16 00:17:35 +02:00
|
|
|
/** concrete subclass to manage a specific kind of contained object.
|
2009-07-06 02:25:19 +02:00
|
|
|
* @note invariant: #content_ always contains a valid SUB object */
|
2009-07-04 04:35:17 +02:00
|
|
|
template<typename SUB>
|
2009-07-06 02:25:19 +02:00
|
|
|
struct Buff : Buffer
|
2009-07-04 04:35:17 +02:00
|
|
|
{
|
2015-04-16 00:17:35 +02:00
|
|
|
static_assert (siz >= sizeof(SUB), "InPlaceAnyHolder: insufficient Buffer size");
|
2012-01-01 06:20:42 +01:00
|
|
|
|
2009-07-06 02:25:19 +02:00
|
|
|
SUB&
|
|
|
|
|
get() const ///< core operation: target is contained within the inline buffer
|
|
|
|
|
{
|
|
|
|
|
return *reinterpret_cast<SUB*> (unConst(this)->ptr());
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-04 04:35:17 +02:00
|
|
|
~Buff()
|
|
|
|
|
{
|
|
|
|
|
get().~SUB();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
explicit
|
|
|
|
|
Buff (SUB const& obj)
|
|
|
|
|
{
|
2009-07-04 19:30:54 +02:00
|
|
|
new(Buffer::ptr()) SUB (obj);
|
2009-07-04 04:35:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Buff (Buff const& oBuff)
|
|
|
|
|
{
|
2009-07-04 19:30:54 +02:00
|
|
|
new(Buffer::ptr()) SUB (oBuff.get());
|
2009-07-04 04:35:17 +02:00
|
|
|
}
|
2009-07-06 02:25:19 +02:00
|
|
|
|
2009-07-04 04:35:17 +02:00
|
|
|
Buff&
|
2009-07-06 02:25:19 +02:00
|
|
|
operator= (Buff const& ref) ///< currently not used
|
2009-07-04 04:35:17 +02:00
|
|
|
{
|
|
|
|
|
if (&ref != this)
|
|
|
|
|
get() = ref.get();
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-06 02:25:19 +02:00
|
|
|
/* == virtual access functions == */
|
2009-07-04 04:35:17 +02:00
|
|
|
|
2009-07-05 03:38:33 +02:00
|
|
|
virtual void
|
2009-07-04 04:35:17 +02:00
|
|
|
clone (void* targetStorage) const
|
|
|
|
|
{
|
2009-07-06 02:25:19 +02:00
|
|
|
new(targetStorage) Buff(get());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual BaseP
|
|
|
|
|
getBase() const
|
|
|
|
|
{
|
|
|
|
|
return AccessPolicy::convert2base (get());
|
2009-07-04 04:35:17 +02:00
|
|
|
}
|
|
|
|
|
|
2009-07-05 03:38:33 +02:00
|
|
|
virtual bool
|
2009-07-04 04:35:17 +02:00
|
|
|
empty() const
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-05 03:38:33 +02:00
|
|
|
virtual bool
|
2009-07-04 04:35:17 +02:00
|
|
|
isValid() const
|
|
|
|
|
{
|
2009-07-05 03:38:33 +02:00
|
|
|
return validitySelfCheck (this->get());
|
2009-07-04 04:35:17 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2009-07-06 02:25:19 +02:00
|
|
|
|
2009-07-04 04:35:17 +02:00
|
|
|
enum{ BUFFSIZE = sizeof(Buffer) };
|
|
|
|
|
|
|
|
|
|
/** embedded buffer actually holding the concrete Buff object,
|
2009-07-04 19:30:54 +02:00
|
|
|
* which in turn holds and manages the target object.
|
|
|
|
|
* @note Invariant: always contains a valid Buffer subclass */
|
2009-07-04 04:35:17 +02:00
|
|
|
char storage_[BUFFSIZE];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-07-05 02:26:59 +02:00
|
|
|
|
2009-07-06 02:25:19 +02:00
|
|
|
|
|
|
|
|
protected: /* === internal interface for managing the storage === */
|
|
|
|
|
|
2009-07-04 04:35:17 +02:00
|
|
|
Buffer&
|
|
|
|
|
buff()
|
|
|
|
|
{
|
|
|
|
|
return *reinterpret_cast<Buffer*> (&storage_);
|
|
|
|
|
}
|
2009-07-04 19:30:54 +02:00
|
|
|
const Buffer&
|
|
|
|
|
buff() const
|
|
|
|
|
{
|
|
|
|
|
return *reinterpret_cast<const Buffer *> (&storage_);
|
|
|
|
|
}
|
2009-07-04 03:32:15 +02:00
|
|
|
|
2009-07-05 02:26:59 +02:00
|
|
|
|
2009-07-06 02:25:19 +02:00
|
|
|
void
|
|
|
|
|
killBuffer()
|
|
|
|
|
{
|
2009-07-04 19:30:54 +02:00
|
|
|
buff().~Buffer();
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-06 02:25:19 +02:00
|
|
|
void
|
|
|
|
|
make_emptyBuff()
|
2009-07-04 19:30:54 +02:00
|
|
|
{
|
2009-07-05 22:05:11 +02:00
|
|
|
new(&storage_) EmptyBuff();
|
2009-07-04 04:35:17 +02:00
|
|
|
}
|
2009-07-04 03:32:15 +02:00
|
|
|
|
|
|
|
|
template<class SUB>
|
2009-07-06 02:25:19 +02:00
|
|
|
void
|
|
|
|
|
place_inBuff (SUB const& obj)
|
2009-07-04 19:30:54 +02:00
|
|
|
{
|
2009-07-04 04:35:17 +02:00
|
|
|
new(&storage_) Buff<SUB> (obj);
|
|
|
|
|
}
|
2009-07-04 03:32:15 +02:00
|
|
|
|
2009-07-06 02:25:19 +02:00
|
|
|
void
|
|
|
|
|
clone_inBuff (InPlaceAnyHolder const& ref)
|
2009-07-04 19:30:54 +02:00
|
|
|
{
|
|
|
|
|
ref.buff().clone (storage_);
|
|
|
|
|
}
|
2009-07-05 02:26:59 +02:00
|
|
|
|
2009-10-05 00:49:37 +02:00
|
|
|
BaseP
|
|
|
|
|
asBase () const ///< @internal backdoor e.g. for comparisons
|
|
|
|
|
{
|
|
|
|
|
BaseP asBase = buff().getBase();
|
|
|
|
|
ASSERT (asBase);
|
|
|
|
|
return asBase;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-07-05 02:26:59 +02:00
|
|
|
|
2009-07-04 03:32:15 +02:00
|
|
|
|
2009-07-04 19:30:54 +02:00
|
|
|
public:
|
2009-07-06 02:25:19 +02:00
|
|
|
|
2009-07-05 22:05:11 +02:00
|
|
|
~InPlaceAnyHolder()
|
2009-07-04 19:30:54 +02:00
|
|
|
{
|
|
|
|
|
killBuffer();
|
|
|
|
|
}
|
2009-07-04 03:32:15 +02:00
|
|
|
|
|
|
|
|
void
|
|
|
|
|
clear ()
|
|
|
|
|
{
|
2009-07-04 19:30:54 +02:00
|
|
|
killBuffer();
|
|
|
|
|
make_emptyBuff();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-07-05 22:05:11 +02:00
|
|
|
InPlaceAnyHolder()
|
2009-07-04 19:30:54 +02:00
|
|
|
{
|
|
|
|
|
make_emptyBuff();
|
2009-07-04 03:32:15 +02:00
|
|
|
}
|
|
|
|
|
|
2009-07-04 19:30:54 +02:00
|
|
|
template<class SUB>
|
2009-07-05 22:05:11 +02:00
|
|
|
InPlaceAnyHolder(SUB const& obj)
|
2009-07-10 19:04:01 +02:00
|
|
|
{
|
2009-07-04 19:30:54 +02:00
|
|
|
place_inBuff (obj);
|
|
|
|
|
}
|
2009-07-04 03:32:15 +02:00
|
|
|
|
2009-07-05 22:05:11 +02:00
|
|
|
InPlaceAnyHolder (InPlaceAnyHolder const& ref)
|
2009-07-04 04:35:17 +02:00
|
|
|
{
|
2009-07-04 19:30:54 +02:00
|
|
|
clone_inBuff (ref);
|
2009-07-04 04:35:17 +02:00
|
|
|
}
|
2009-07-04 03:32:15 +02:00
|
|
|
|
2009-07-05 22:05:11 +02:00
|
|
|
InPlaceAnyHolder&
|
|
|
|
|
operator= (InPlaceAnyHolder const& ref)
|
2009-07-04 03:32:15 +02:00
|
|
|
{
|
2009-07-04 19:30:54 +02:00
|
|
|
if (!isSameObject (*this, ref))
|
|
|
|
|
{
|
|
|
|
|
killBuffer();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
clone_inBuff (ref);
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
make_emptyBuff();
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-07-04 03:32:15 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
2009-07-04 19:30:54 +02:00
|
|
|
|
|
|
|
|
template<class SUB>
|
2009-07-05 22:05:11 +02:00
|
|
|
InPlaceAnyHolder&
|
2009-07-04 19:30:54 +02:00
|
|
|
operator= (SUB const& newContent)
|
|
|
|
|
{
|
2009-07-05 22:05:11 +02:00
|
|
|
if ( empty()
|
|
|
|
|
|| !isSameObject (*buff().getBase(), newContent)
|
2009-07-04 19:30:54 +02:00
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
killBuffer();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
place_inBuff (newContent);
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
make_emptyBuff();
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-07-04 03:32:15 +02:00
|
|
|
|
2009-07-06 02:25:19 +02:00
|
|
|
/** re-accessing the concrete contained object.
|
2015-04-28 04:49:08 +02:00
|
|
|
* This requires exact knowledge of the actual type
|
|
|
|
|
* of the element currently in storage. OpaqueHolder
|
|
|
|
|
* does not provide any "probing" or visitation mechanism.
|
|
|
|
|
* @remarks You might consider lib::Variant or some visitor instead.
|
|
|
|
|
* @throws lumiera::error::Logic when conversion/access fails
|
|
|
|
|
* @throws lumiera::error::Invalid when accessing an empty holder
|
2009-07-06 02:25:19 +02:00
|
|
|
*/
|
2009-07-04 03:32:15 +02:00
|
|
|
template<class SUB>
|
|
|
|
|
SUB& get() const
|
|
|
|
|
{
|
2009-07-04 19:30:54 +02:00
|
|
|
typedef const Buffer* Iface;
|
|
|
|
|
typedef const Buff<SUB> * Actual;
|
|
|
|
|
Iface interface = &buff();
|
|
|
|
|
Actual actual = dynamic_cast<Actual> (interface);
|
|
|
|
|
if (actual)
|
|
|
|
|
return actual->get();
|
|
|
|
|
|
2015-04-28 04:49:08 +02:00
|
|
|
if (this->empty())
|
|
|
|
|
throw error::Invalid("accessing empty holder"
|
|
|
|
|
, error::LUMIERA_ERROR_BOTTOM_VALUE);
|
|
|
|
|
else
|
|
|
|
|
throw error::Logic ("Attempt to access OpaqueHolder's contents "
|
|
|
|
|
"specifying incompatible target type"
|
|
|
|
|
, error::LUMIERA_ERROR_WRONG_TYPE
|
|
|
|
|
);
|
2009-07-04 03:32:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-10-04 16:43:49 +02:00
|
|
|
|
2009-07-04 19:30:54 +02:00
|
|
|
bool
|
|
|
|
|
empty() const
|
2009-07-04 03:32:15 +02:00
|
|
|
{
|
2009-07-04 19:30:54 +02:00
|
|
|
return buff().empty();
|
2009-07-04 03:32:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-07-04 19:30:54 +02:00
|
|
|
bool
|
|
|
|
|
isValid() const
|
|
|
|
|
{
|
|
|
|
|
return buff().isValid();
|
|
|
|
|
}
|
2017-04-02 06:33:56 +02:00
|
|
|
|
|
|
|
|
explicit
|
|
|
|
|
operator bool() const
|
|
|
|
|
{
|
|
|
|
|
return isValid();
|
|
|
|
|
}
|
2009-07-04 03:32:15 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-07-06 02:25:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-07-05 22:05:11 +02:00
|
|
|
/**
|
2015-04-16 00:17:35 +02:00
|
|
|
* Inline buffer to hold and own an object while concealing the
|
2009-07-05 22:05:11 +02:00
|
|
|
* concrete type. Access to the contained object is similar to a
|
|
|
|
|
* smart-pointer, but the object isn't heap allocated. OpaqueHolder
|
|
|
|
|
* may be created empty, which can be checked by a bool test.
|
2009-07-06 02:25:19 +02:00
|
|
|
* The whole compound is copyable if and only if the contained
|
|
|
|
|
* object is copyable.
|
2009-07-05 22:05:11 +02:00
|
|
|
*
|
2016-03-06 02:26:42 +01:00
|
|
|
* ## using OpaqueHolder
|
2009-07-05 22:05:11 +02:00
|
|
|
* OpaqueHolder instances are copyable value objects. They are created
|
|
|
|
|
* either empty, by copy from an existing OpaqueHolder, or by directly
|
|
|
|
|
* specifying the concrete object to embed. This target object will be
|
|
|
|
|
* \em copy-constructed into the internal buffer. Additionally, you
|
|
|
|
|
* may assign a new value, which causes the old value object to be
|
|
|
|
|
* destroyed and a new one to be copy-constructed into the buffer.
|
|
|
|
|
* Later on, the embedded value might be accessed
|
|
|
|
|
* - using the smart-ptr-like access through the common base interface BA
|
|
|
|
|
* - when knowing the exact type to access, the templated #get might be an option
|
2016-03-06 02:26:42 +01:00
|
|
|
* - the empty state of the container and a `isValid()` on the target may be checked
|
|
|
|
|
* - a combination of both is available as a `bool` check on the OpaqueHolder instance.
|
2009-07-05 22:05:11 +02:00
|
|
|
*
|
2016-03-06 02:26:42 +01:00
|
|
|
* For using OpaqueHolder, several *assumptions* need to be fulfilled
|
2009-07-05 22:05:11 +02:00
|
|
|
* - any instance placed into OpaqueHolder is below the specified maximum size
|
|
|
|
|
* - the caller cares for thread safety. No concurrent get calls while in mutation!
|
2016-03-06 02:26:42 +01:00
|
|
|
*
|
|
|
|
|
* @tparam BA the nominal Base/Interface class for a family of types
|
|
|
|
|
* @tparam siz maximum storage required for the targets to be held inline
|
2009-07-05 22:05:11 +02:00
|
|
|
*/
|
|
|
|
|
template
|
2016-03-06 02:26:42 +01:00
|
|
|
< class BA
|
|
|
|
|
, size_t siz = sizeof(BA)
|
2009-07-05 22:05:11 +02:00
|
|
|
>
|
|
|
|
|
class OpaqueHolder
|
2016-12-23 04:23:03 +01:00
|
|
|
: public InPlaceAnyHolder<siz, InPlaceAnyHolder_useCommonBase<BA>>
|
2009-07-05 22:05:11 +02:00
|
|
|
{
|
2016-12-23 04:23:03 +01:00
|
|
|
typedef InPlaceAnyHolder<siz, InPlaceAnyHolder_useCommonBase<BA>> InPlaceHolder;
|
2009-07-05 22:05:11 +02:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
OpaqueHolder() : InPlaceHolder() {}
|
|
|
|
|
|
|
|
|
|
template<class SUB>
|
|
|
|
|
OpaqueHolder(SUB const& obj) : InPlaceHolder(obj) {}
|
|
|
|
|
|
|
|
|
|
template<class SUB>
|
|
|
|
|
OpaqueHolder&
|
|
|
|
|
operator= (SUB const& newContent)
|
|
|
|
|
{
|
|
|
|
|
static_cast<InPlaceHolder&>(*this) = newContent;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-18 22:07:46 +02:00
|
|
|
// note: using standard copy operations
|
2009-07-05 22:05:11 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* === smart-ptr style access === */
|
|
|
|
|
|
|
|
|
|
BA&
|
|
|
|
|
operator* () const
|
|
|
|
|
{
|
|
|
|
|
ASSERT (!InPlaceHolder::empty());
|
|
|
|
|
return *InPlaceHolder::buff().getBase();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BA*
|
|
|
|
|
operator-> () const
|
|
|
|
|
{
|
|
|
|
|
ASSERT (!InPlaceHolder::empty());
|
|
|
|
|
return InPlaceHolder::buff().getBase();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-07-04 03:32:15 +02:00
|
|
|
|
2009-07-18 22:07:46 +02:00
|
|
|
|
|
|
|
|
/**
|
2013-09-21 00:22:36 +02:00
|
|
|
* Buffer to place and maintain an object instance privately within another object.
|
|
|
|
|
* Variation of a similar concept as with OpaqueHolder, but implemented here
|
2009-07-18 22:07:46 +02:00
|
|
|
* with reduced security and lesser overhead. InPlaceBuffer is just a chunk of
|
|
|
|
|
* storage, which can be accessed through a common base class interface and
|
|
|
|
|
* allows to place new objects there. It has no way to keep track of the
|
|
|
|
|
* actual object living currently in the buffer. Thus, using InPlaceBuffer
|
|
|
|
|
* requires the placed class(es) themselves to maintain their lifecycle,
|
|
|
|
|
* and especially it is mandatory for the base class to provide a
|
|
|
|
|
* virtual dtor. On the other hand, just the (alignment rounded)
|
|
|
|
|
* storage for the object(s) placed into the buffer is required.
|
2016-09-04 23:21:15 +02:00
|
|
|
* @remarks as a complement, PlantingHandle may be used on APIs to offer
|
|
|
|
|
* a lightweight way for clients to provide a callback.
|
|
|
|
|
* @warning InPlaceBuffer really takes ownership, and even creates a
|
|
|
|
|
* default constructed instance of the base class right away.
|
|
|
|
|
* Yet the requirement for a virtual dtor is deliberately not
|
|
|
|
|
* enforced here, to allow use for types without VTable.
|
2016-03-06 02:26:42 +01:00
|
|
|
*
|
|
|
|
|
* @tparam BA the nominal Base/Interface class for a family of types
|
|
|
|
|
* @tparam siz maximum storage required for the targets to be held inline
|
|
|
|
|
* @tparam DEFAULT the default instance to place initially
|
2009-07-18 22:07:46 +02:00
|
|
|
*/
|
|
|
|
|
template
|
2016-03-06 02:26:42 +01:00
|
|
|
< class BA
|
|
|
|
|
, size_t siz = sizeof(BA)
|
|
|
|
|
, class DEFAULT = BA
|
2009-07-18 22:07:46 +02:00
|
|
|
>
|
|
|
|
|
class InPlaceBuffer
|
|
|
|
|
: boost::noncopyable
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
mutable char buf_[siz];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BA&
|
|
|
|
|
getObj() const
|
|
|
|
|
{
|
|
|
|
|
return reinterpret_cast<BA&> (buf_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
placeDefault()
|
|
|
|
|
{
|
2015-04-16 00:17:35 +02:00
|
|
|
static_assert (siz >= sizeof(DEFAULT), "InPlaceBuffer to small");
|
2011-12-28 06:17:26 +01:00
|
|
|
|
2009-07-18 22:07:46 +02:00
|
|
|
new(&buf_) DEFAULT();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
destroy()
|
|
|
|
|
{
|
|
|
|
|
getObj().~BA();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
2016-10-02 22:15:55 +02:00
|
|
|
~InPlaceBuffer ()
|
|
|
|
|
{
|
|
|
|
|
destroy();
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-18 22:07:46 +02:00
|
|
|
InPlaceBuffer ()
|
|
|
|
|
{
|
|
|
|
|
placeDefault();
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-02 22:15:55 +02:00
|
|
|
/** immediately emplace an embedded subclass type */
|
|
|
|
|
template<class TY, typename...ARGS>
|
|
|
|
|
InPlaceBuffer (TY*, ARGS&& ...args)
|
2009-07-18 22:07:46 +02:00
|
|
|
{
|
2016-10-02 23:34:07 +02:00
|
|
|
static_assert (siz >= sizeof(TY), "InPlaceBuffer to small");
|
|
|
|
|
|
2016-10-02 22:15:55 +02:00
|
|
|
new(&buf_) TY (std::forward<ARGS> (args)...);
|
2009-07-18 22:07:46 +02:00
|
|
|
}
|
|
|
|
|
|
2016-10-02 22:15:55 +02:00
|
|
|
/** helper to mark the subclass type to create.
|
|
|
|
|
* @remarks we can not specify explicit template arguments on ctor calls,
|
|
|
|
|
* so the only way is to use a dummy marker argument to pass the type.
|
|
|
|
|
* Use as `InPlaceBuffer(embedType<XYZ>, arg1, arg2, arg3)` */
|
|
|
|
|
template<typename SUB>
|
|
|
|
|
static auto embedType() { return (SUB*) nullptr; }
|
|
|
|
|
|
2009-07-18 22:07:46 +02:00
|
|
|
|
|
|
|
|
/** Abbreviation for placement new */
|
2016-02-06 22:17:48 +01:00
|
|
|
template<class TY, typename...ARGS>
|
2009-07-18 22:07:46 +02:00
|
|
|
TY&
|
2016-02-06 22:17:48 +01:00
|
|
|
create (ARGS&& ...args)
|
2009-07-18 22:07:46 +02:00
|
|
|
{
|
2016-02-06 22:17:48 +01:00
|
|
|
static_assert (siz >= sizeof(TY), "InPlaceBuffer to small");
|
|
|
|
|
|
|
|
|
|
destroy();
|
|
|
|
|
try {
|
|
|
|
|
return *new(&buf_) TY (std::forward<ARGS> (args)...);
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
placeDefault();
|
|
|
|
|
throw;
|
|
|
|
|
}
|
2009-07-18 22:07:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* === smart-ptr style access === */
|
|
|
|
|
|
|
|
|
|
BA&
|
|
|
|
|
operator* () const
|
|
|
|
|
{
|
|
|
|
|
return getObj();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BA*
|
|
|
|
|
operator-> () const
|
|
|
|
|
{
|
|
|
|
|
return &getObj();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<class SUB>
|
2013-09-21 00:22:36 +02:00
|
|
|
SUB*
|
2009-07-18 22:07:46 +02:00
|
|
|
access ()
|
|
|
|
|
{
|
|
|
|
|
BA * asBase = &getObj();
|
|
|
|
|
SUB* content = util::AccessCasted<SUB*>::access (asBase);
|
|
|
|
|
return content;
|
|
|
|
|
} // NOTE: might be null.
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-09-04 23:21:15 +02:00
|
|
|
/**
|
|
|
|
|
* handle to allow for safe _»remote implantation«_
|
|
|
|
|
* of an unknown subclass into a given opaque InPlaceBuffer,
|
|
|
|
|
* without having to disclose the concrete buffer type or size.
|
|
|
|
|
* @remarks this is especially geared towards use in APIs, allowing
|
|
|
|
|
* a not yet known implementation to implant an agent or collaboration
|
|
|
|
|
* partner into the likewise undisclosed innards of the exposed service.
|
|
|
|
|
* @warning the type BA must expose a virtual dtor, since the targeted
|
|
|
|
|
* InPlaceBuffer has to take ownership of the implanted object.
|
|
|
|
|
*/
|
|
|
|
|
template<class BA>
|
|
|
|
|
class PlantingHandle
|
|
|
|
|
{
|
|
|
|
|
void* buffer_;
|
|
|
|
|
size_t maxSiz_;
|
|
|
|
|
|
|
|
|
|
static_assert (std::has_virtual_destructor<BA>(),
|
|
|
|
|
"target interface BA must provide virtual dtor, "
|
|
|
|
|
"since InPlaceBuffer needs to take ownership.");
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
template<size_t maxSiz>
|
|
|
|
|
PlantingHandle (InPlaceBuffer<BA, maxSiz>& targetBuffer)
|
|
|
|
|
: buffer_(&targetBuffer)
|
|
|
|
|
, maxSiz_(maxSiz)
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<class SUB>
|
|
|
|
|
BA&
|
|
|
|
|
create (SUB&& implementation)
|
|
|
|
|
{
|
|
|
|
|
if (sizeof(SUB) > maxSiz_)
|
|
|
|
|
throw error::Fatal("Unable to implant implementation object of size "
|
|
|
|
|
"exceeding the pre-established storage buffer capacity."
|
|
|
|
|
,error::LUMIERA_ERROR_CAPACITY);
|
|
|
|
|
|
|
|
|
|
using Holder = InPlaceBuffer<BA, sizeof(SUB)>;
|
|
|
|
|
Holder& holder = *static_cast<Holder*> (buffer_);
|
|
|
|
|
|
|
|
|
|
return holder.create<SUB> (std::forward<SUB> (implementation));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class SUB>
|
|
|
|
|
bool
|
|
|
|
|
canCreate() const
|
|
|
|
|
{
|
|
|
|
|
return sizeof(SUB) <= maxSiz_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BA*
|
|
|
|
|
get() const
|
|
|
|
|
{
|
|
|
|
|
ENSURE (buffer_);
|
|
|
|
|
BA& bufferContent = **static_cast<InPlaceBuffer<BA>*> (buffer_);
|
|
|
|
|
return &bufferContent;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-07-18 22:07:46 +02:00
|
|
|
|
2009-07-04 03:32:15 +02:00
|
|
|
} // namespace lib
|
|
|
|
|
#endif
|