issue resolved, tests pass, finally (whew)
This commit is contained in:
parent
c3b8d39507
commit
2462dee5ca
4 changed files with 148 additions and 134 deletions
|
|
@ -101,7 +101,11 @@ namespace typelist{
|
|||
typedef lib::InPlaceAnyHolder< sizeof(FunVoid) // same size for all function objects
|
||||
, lib::InPlaceAnyHolder_unrelatedTypes // no common base class!
|
||||
> FunHolder;
|
||||
typedef lib::InPlaceAnyHolder< sizeof(void*)
|
||||
, lib::InPlaceAnyHolder_unrelatedTypes
|
||||
> FunPtrHolder;
|
||||
|
||||
|
||||
/**
|
||||
* Policy for FunErasure: store an embedded tr1::function
|
||||
* Using this policy allows to store arbitrary complex functor objects
|
||||
|
|
@ -111,7 +115,6 @@ namespace typelist{
|
|||
class StoreFunction
|
||||
: public FunHolder
|
||||
{
|
||||
|
||||
public:
|
||||
template<typename SIG>
|
||||
StoreFunction (SIG& fun)
|
||||
|
|
@ -139,59 +142,26 @@ namespace typelist{
|
|||
* The price to pay is vtable access.
|
||||
*/
|
||||
class StoreFunPtr
|
||||
: public lib::BoolCheckable<StoreFunPtr>
|
||||
: public FunPtrHolder
|
||||
{
|
||||
/** Helper: type erasure */
|
||||
struct Holder
|
||||
{
|
||||
void *fP_;
|
||||
virtual ~Holder() {}
|
||||
};
|
||||
|
||||
/** storing and retrieving concrete function ptr */
|
||||
template<typename SIG>
|
||||
struct FunctionHolder : Holder
|
||||
{
|
||||
FunctionHolder (SIG *fun)
|
||||
{
|
||||
REQUIRE (fun);
|
||||
fP_ = reinterpret_cast<void*> (fun);
|
||||
}
|
||||
SIG&
|
||||
get()
|
||||
{
|
||||
return *reinterpret_cast<SIG*> (fP_);
|
||||
}
|
||||
};
|
||||
|
||||
/** embedded container holding the pointer */
|
||||
Holder holder_;
|
||||
|
||||
public:
|
||||
template<typename SIG>
|
||||
StoreFunPtr (SIG& fun)
|
||||
{
|
||||
new(&holder_) FunctionHolder<SIG> (&fun);
|
||||
}
|
||||
: FunPtrHolder(&fun)
|
||||
{ }
|
||||
|
||||
template<typename SIG>
|
||||
StoreFunPtr (SIG *fun)
|
||||
{
|
||||
new(&holder_) FunctionHolder<SIG> (fun);
|
||||
}
|
||||
: FunPtrHolder(fun)
|
||||
{ }
|
||||
|
||||
template<typename SIG>
|
||||
SIG&
|
||||
getFun ()
|
||||
{
|
||||
REQUIRE (INSTANCEOF (FunctionHolder<SIG>, &holder_));
|
||||
return static_cast<FunctionHolder<SIG>&> (holder_).get();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
isValid() const
|
||||
{
|
||||
return holder_.fP_;
|
||||
SIG *fun = get<SIG*>();
|
||||
REQUIRE (fun);
|
||||
return *fun;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -29,11 +29,21 @@
|
|||
** an inline buffer holding an object of the concrete subclass. Typically,
|
||||
** this situation arises when dealing with functor objects.
|
||||
**
|
||||
** This template helps building custom objects and wrappers based on this
|
||||
** pattern: it provides an buffer for the target objects 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 can be empty; but re-accessing the concrete
|
||||
** These templates help with building custom objects and wrappers based on
|
||||
** this pattern: InPlaceAnyHolder provides an buffer for the target objects
|
||||
** 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
|
||||
** object requires knowledge of the actual type, similar to boost::any
|
||||
** (but contrary to OpaqueHolder the latter uses heap storage).
|
||||
**
|
||||
|
|
@ -87,14 +97,15 @@ namespace lib {
|
|||
|
||||
}
|
||||
|
||||
/* ==== Policy classes for OpaqueHolder ==== */
|
||||
|
||||
|
||||
/* ==== Policy classes controlling re-Access ==== */
|
||||
|
||||
/**
|
||||
* Standard policy for accessing the contents via
|
||||
* a common base class interface. Using this policy
|
||||
* causes a virtual get() function to be used, where
|
||||
* the actual Buff-subclasses will define overrides
|
||||
* with covariant return types.
|
||||
* causes static or dynamic casts or direct conversion
|
||||
* to be employed as appropriate.
|
||||
*/
|
||||
template<class BA>
|
||||
struct InPlaceAnyHolder_useCommonBase
|
||||
|
|
@ -134,18 +145,15 @@ namespace lib {
|
|||
};
|
||||
|
||||
/**
|
||||
* Alternative policy for accessing the contents via
|
||||
* Alternative policy for accessing the contents without
|
||||
* 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
|
||||
* a common base class. (E.g. tr1::function objects).
|
||||
* In this case, the BA template parameter of OpaqueHolder
|
||||
* can be considered just a nominal placeholder.
|
||||
* @note using this policy renders the smart-ptr style
|
||||
* access largely useless and might even cause
|
||||
* segmentation errors, when trying to access
|
||||
* the contents of the buffer this way through
|
||||
* a brute force reinterpret_cast.
|
||||
* 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.
|
||||
*/
|
||||
struct InPlaceAnyHolder_unrelatedTypes
|
||||
{
|
||||
|
|
@ -166,26 +174,28 @@ namespace lib {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////TODO
|
||||
/////////////////////////////: separieren in einen InPlaceAnyHolder
|
||||
/////////////////////////////: und als dessen Kind den OpaqueHolder
|
||||
/////////////////////////////: AccessPolicy unter die konkrete Buff-Klasse einschieben
|
||||
/////////////////////////////: EmptyBuffer als abgeleitet.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Inline buffer holding and owning an object while concealing the
|
||||
* 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.
|
||||
* The whole compound is copyable if and only if the contained object
|
||||
* is copyable.
|
||||
* 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().
|
||||
* Moreover \code !empty() && isValid() \endcode may be tested
|
||||
* as by \bool conversion of the Holder object. The whole compound
|
||||
* is copyable if and only if the contained object is copyable.
|
||||
*
|
||||
* For using OpaqueHolder, several \b assumptions need to be fulfilled
|
||||
* - any instance placed into OpaqueHolder is below the specified maximum size
|
||||
* - the caller cares for thread safety. No concurrent get calls while in mutation!
|
||||
* @note assertion failure when trying to place an instance not
|
||||
* fitting into given size.
|
||||
* @note \em not threadsafe!
|
||||
*/
|
||||
template
|
||||
< size_t siz ///< maximum storage required for the targets to be held inline
|
||||
|
|
@ -204,30 +214,14 @@ namespace lib {
|
|||
void* ptr() { return &content_; }
|
||||
|
||||
virtual ~Buffer() {}
|
||||
virtual bool isValid() const =0;
|
||||
virtual bool empty() const =0;
|
||||
virtual BaseP getBase() const =0;
|
||||
|
||||
virtual void clone (void* targetStorage) const =0;
|
||||
};
|
||||
|
||||
template<typename TY>
|
||||
struct AbstractBuff : Buffer
|
||||
{
|
||||
TY&
|
||||
get() const ///< core operation: target is contained within the inline buffer
|
||||
{
|
||||
return *reinterpret_cast<TY*> (unConst(this)->ptr());
|
||||
}
|
||||
|
||||
BaseP
|
||||
getBase() const
|
||||
{
|
||||
return AccessPolicy::convert2base (get());
|
||||
}
|
||||
virtual bool isValid() const =0;
|
||||
virtual bool empty() const =0;
|
||||
virtual BaseP getBase() const =0;
|
||||
|
||||
virtual void clone (void* targetStorage) const =0;
|
||||
};
|
||||
|
||||
/** special case: no stored object */
|
||||
struct EmptyBuff : Buffer
|
||||
{
|
||||
virtual bool isValid() const { return false; }
|
||||
|
|
@ -244,15 +238,20 @@ namespace lib {
|
|||
{
|
||||
new(targetStorage) EmptyBuff();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** concrete subclass managing a specific kind of contained object.
|
||||
* @note invariant: content_ always contains a valid SUB object */
|
||||
* @note invariant: #content_ always contains a valid SUB object */
|
||||
template<typename SUB>
|
||||
struct Buff : AbstractBuff<SUB>
|
||||
struct Buff : Buffer
|
||||
{
|
||||
SUB&
|
||||
get() const ///< core operation: target is contained within the inline buffer
|
||||
{
|
||||
return *reinterpret_cast<SUB*> (unConst(this)->ptr());
|
||||
}
|
||||
|
||||
~Buff()
|
||||
{
|
||||
get().~SUB();
|
||||
|
|
@ -269,20 +268,27 @@ namespace lib {
|
|||
{
|
||||
new(Buffer::ptr()) SUB (oBuff.get());
|
||||
}
|
||||
|
||||
|
||||
Buff&
|
||||
operator= (Buff const& ref) ///< not used currently
|
||||
operator= (Buff const& ref) ///< currently not used
|
||||
{
|
||||
if (&ref != this)
|
||||
get() = ref.get();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* == virtual access functions == */
|
||||
|
||||
virtual void
|
||||
clone (void* targetStorage) const
|
||||
{
|
||||
new(targetStorage) Buff(this->get());
|
||||
new(targetStorage) Buff(get());
|
||||
}
|
||||
|
||||
virtual BaseP
|
||||
getBase() const
|
||||
{
|
||||
return AccessPolicy::convert2base (get());
|
||||
}
|
||||
|
||||
virtual bool
|
||||
|
|
@ -299,6 +305,7 @@ namespace lib {
|
|||
};
|
||||
|
||||
|
||||
|
||||
enum{ BUFFSIZE = sizeof(Buffer) };
|
||||
|
||||
/** embedded buffer actually holding the concrete Buff object,
|
||||
|
|
@ -310,8 +317,9 @@ namespace lib {
|
|||
|
||||
|
||||
|
||||
/* === internal interface for managing the storage === */
|
||||
protected:
|
||||
|
||||
protected: /* === internal interface for managing the storage === */
|
||||
|
||||
Buffer&
|
||||
buff()
|
||||
{
|
||||
|
|
@ -324,23 +332,27 @@ namespace lib {
|
|||
}
|
||||
|
||||
|
||||
void killBuffer()
|
||||
{
|
||||
void
|
||||
killBuffer()
|
||||
{
|
||||
buff().~Buffer();
|
||||
}
|
||||
|
||||
void make_emptyBuff()
|
||||
void
|
||||
make_emptyBuff()
|
||||
{
|
||||
new(&storage_) EmptyBuff();
|
||||
}
|
||||
|
||||
template<class SUB>
|
||||
void place_inBuff (SUB const& obj)
|
||||
void
|
||||
place_inBuff (SUB const& obj)
|
||||
{
|
||||
new(&storage_) Buff<SUB> (obj);
|
||||
}
|
||||
|
||||
void clone_inBuff (InPlaceAnyHolder const& ref)
|
||||
void
|
||||
clone_inBuff (InPlaceAnyHolder const& ref)
|
||||
{
|
||||
ref.buff().clone (storage_);
|
||||
}
|
||||
|
|
@ -348,6 +360,7 @@ namespace lib {
|
|||
|
||||
|
||||
public:
|
||||
|
||||
~InPlaceAnyHolder()
|
||||
{
|
||||
killBuffer();
|
||||
|
|
@ -420,8 +433,21 @@ namespace lib {
|
|||
|
||||
|
||||
|
||||
/* === re-accessing the concrete contained object === */
|
||||
|
||||
/** re-accessing the concrete contained object.
|
||||
* When the specified type is exactly the same
|
||||
* as used when storing the object, we can directly
|
||||
* re-access the buffer. Otherwise, a conversion might
|
||||
* be possible going through the Base type, depending
|
||||
* on the actual types involved and the AccessPolicy.
|
||||
* But, as we don't "know" the actual type of the object
|
||||
* in storage, a \em static upcast to any type \em between
|
||||
* the concrete object type and the base type has to be
|
||||
* ruled out for safety reasons. When the contained object
|
||||
* has RTTI, a \em dynamic cast can be performed in this
|
||||
* situation. You might consider using visitor.hpp instead
|
||||
* if this imposes a serious limitation.
|
||||
* @throws lumiera::error::Logic when conversion/access fails
|
||||
*/
|
||||
template<class SUB>
|
||||
SUB& get() const
|
||||
{
|
||||
|
|
@ -463,13 +489,16 @@ namespace lib {
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Inline buffer holding and owning an object while concealing the
|
||||
* 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.
|
||||
* The whole compound is copyable if and only if the contained object
|
||||
* is copyable.
|
||||
* The whole compound is copyable if and only if the contained
|
||||
* object is copyable.
|
||||
*
|
||||
* \par using OpaqueHolder
|
||||
* OpaqueHolder instances are copyable value objects. They are created
|
||||
|
|
|
|||
|
|
@ -211,15 +211,16 @@ namespace test {
|
|||
// fabricate an unbound functor...
|
||||
|
||||
typedef typename BuildEmptyFunctor<HOL>::Type NoFunc;
|
||||
NoFunc noFunction;
|
||||
NoFunc noFunction = NoFunc();
|
||||
HOL emptyHolder (noFunction);
|
||||
ASSERT (!emptyHolder);
|
||||
|
||||
ASSERT ( h1 );
|
||||
ASSERT ( h2 );
|
||||
ASSERT ( h3 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ namespace test{
|
|||
using util::isnil;
|
||||
using util::for_each;
|
||||
using util::isSameObject;
|
||||
using lumiera::error::LUMIERA_ERROR_INVALID;
|
||||
using lumiera::error::LUMIERA_ERROR_ASSERTION;
|
||||
|
||||
using std::vector;
|
||||
|
|
@ -88,13 +89,13 @@ namespace test{
|
|||
return myVal_ % 2;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** maximum additional storage maybe wasted
|
||||
* due to alignment of the contained object
|
||||
* within OpaqueHolder's buffer
|
||||
*/
|
||||
const size_t ALLIGNMENT = sizeof(size_t);
|
||||
|
||||
|
||||
/** maximum additional storage maybe wasted
|
||||
* due to alignment of the contained object
|
||||
* within OpaqueHolder's buffer
|
||||
*/
|
||||
const size_t ALLIGNMENT = sizeof(size_t);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -104,14 +105,14 @@ namespace test{
|
|||
|
||||
|
||||
/**********************************************************************************
|
||||
* @test use the OpaqueHolder inline buffer to handle a family of classes
|
||||
* @test use the OpaqueHolder inline buffer to handle objects of a family of types
|
||||
* through a common interface, without being forced to use heap storage
|
||||
* or a custom allocator.
|
||||
*
|
||||
* @todo this test doesn't cover automatic conversions and conversions using
|
||||
* RTTI from the target objects, while OpaqueHolder.tempate get() would
|
||||
* allow for such conversions. This is similar to Ticket #141, and actually
|
||||
* based on the same code as variant.hpp (access-casted.hpp)
|
||||
*
|
||||
* @todo this test doesn't cover automatic conversions and conversions using RTTI
|
||||
* from the target objects, while \code OpaqueHolder.template get() \endcode
|
||||
* would allow for such conversions. This is similar to Ticket #141, and
|
||||
* actually based on the same code as variant.hpp (access-casted.hpp)
|
||||
*/
|
||||
class OpaqueHolder_test : public Test
|
||||
{
|
||||
|
|
@ -150,6 +151,9 @@ namespace test{
|
|||
}
|
||||
|
||||
|
||||
/** @test cover the basic situations of object handling,
|
||||
* especially copy operations and re-assignments
|
||||
*/
|
||||
void
|
||||
checkHandling (TestList& objs)
|
||||
{
|
||||
|
|
@ -164,25 +168,28 @@ namespace test{
|
|||
typedef DD<3> D3;
|
||||
typedef DD<5> D5;
|
||||
D3 d3 (oo.get<D3>() );
|
||||
ASSERT (3 == oo->getIt());
|
||||
ASSERT (3 == oo->getIt()); // re-access through Base interface
|
||||
ASSERT (!isSameObject (d3, *oo));
|
||||
VERIFY_ERROR (WRONG_TYPE, oo.get<D5>() );
|
||||
|
||||
oo = D5(); // direct assignment of target into Buffer
|
||||
// direct assignment of target into Buffer
|
||||
oo = D5();
|
||||
ASSERT (oo);
|
||||
ASSERT (5 == oo->getIt());
|
||||
VERIFY_ERROR (WRONG_TYPE, oo.get<D3>() );
|
||||
|
||||
D5 &rd5 (oo.get<D5>()); // can get a direct reference to contained object
|
||||
// can get a direct reference to contained object
|
||||
D5 &rd5 (oo.get<D5>());
|
||||
ASSERT (isSameObject (rd5, *oo));
|
||||
|
||||
ASSERT (!isnil(oo));
|
||||
oo = objs[3]; // copy construction also works on non-empty object
|
||||
ASSERT (7 == oo->getIt());
|
||||
|
||||
ASSERT (7 == rd5.getIt()); // WARNING: direct ref has been messed up through the backdoor!
|
||||
// WARNING: direct ref has been messed up through the backdoor!
|
||||
ASSERT (7 == rd5.getIt());
|
||||
ASSERT (isSameObject (rd5, *oo));
|
||||
|
||||
|
||||
uint cnt_before = _create_count;
|
||||
|
||||
oo.clear();
|
||||
|
|
@ -190,7 +197,9 @@ namespace test{
|
|||
oo = D5(); // direct assignment also works on empty object
|
||||
ASSERT (oo);
|
||||
ASSERT (5 == oo->getIt());
|
||||
ASSERT (_create_count == 2 + cnt_before); // one within buff and one for the anonymous temporary D5()
|
||||
ASSERT (_create_count == 2 + cnt_before);
|
||||
// one within buff and one for the anonymous temporary D5()
|
||||
|
||||
|
||||
// verify that self-assignment is properly detected...
|
||||
cnt_before = _create_count;
|
||||
|
|
@ -206,6 +215,11 @@ namespace test{
|
|||
oo.clear();
|
||||
ASSERT (!oo);
|
||||
ASSERT (isnil(oo));
|
||||
VERIFY_ERROR (INVALID, oo.get<D5>() );
|
||||
#if false ////////////////////////////////////////////////////////TODO: restore throwing ASSERT
|
||||
VERIFY_ERROR (ASSERTION, oo->getIt() );
|
||||
#endif////////////////////////////////////////////////////////////
|
||||
// can't access empty holder...
|
||||
|
||||
Opaque o1 (oo);
|
||||
ASSERT (!o1);
|
||||
|
|
|
|||
Loading…
Reference in a new issue