create a lib header based on this solution
This commit is contained in:
parent
32637dd958
commit
6a1c33e5a1
8 changed files with 599 additions and 216 deletions
|
|
@ -39,7 +39,15 @@
|
|||
** ScopedHolder implements a similar concept for in-place storage of
|
||||
** noncopyable objects within STL containers.
|
||||
**
|
||||
** While the added copy operations (secured with the "empty" requirement)
|
||||
** are enough to use those holders within fixed sized STL containers,
|
||||
** supporting dynamic growth (like in std::vector#resize() ) additionally
|
||||
** requires a facility to transfer the lifecycle management control between
|
||||
** holder instances. This is the purpose of the \c transfer_control
|
||||
** friend function.
|
||||
**
|
||||
** @see scopedholdertest.cpp
|
||||
** @see scopedholdertransfer.hpp use in std::vector
|
||||
** @see AllocationCluster usage example
|
||||
*/
|
||||
|
||||
|
|
@ -69,6 +77,13 @@ namespace lib {
|
|||
{
|
||||
typedef boost::scoped_ptr<B> _Parent;
|
||||
|
||||
static B* must_be_null (_Parent const& ptr)
|
||||
{
|
||||
if (ptr)
|
||||
throw lumiera::error::Logic("ScopedPtrHolder protocol violation: "
|
||||
"attempt to copy from non-null.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
ScopedPtrHolder ()
|
||||
|
|
@ -97,14 +112,13 @@ namespace lib {
|
|||
return *this;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
static B* must_be_null (_Parent const& ptr)
|
||||
friend void
|
||||
transfer_control (ScopedPtrHolder& from, ScopedPtrHolder& to)
|
||||
{
|
||||
if (ptr)
|
||||
throw lumiera::error::Logic("ScopedPtrHolder protocol violation: "
|
||||
"attempt to copy from non-null.");
|
||||
return 0;
|
||||
if (!from) return;
|
||||
TRACE (test, "transfer_control<ScopedPtrHolder>... from=%x to=%x",&from, &to);
|
||||
must_be_null (to);
|
||||
to.swap(from);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -133,11 +147,22 @@ namespace lib {
|
|||
typedef ScopedHolder<TY> _ThisType;
|
||||
|
||||
|
||||
static char must_be_empty (_ThisType const& ref)
|
||||
{
|
||||
if (ref)
|
||||
throw lumiera::error::Logic("ScopedHolder protocol violation: "
|
||||
"copy operation after having invoked create().");
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
ScopedHolder()
|
||||
: created_(0)
|
||||
{ }
|
||||
|
||||
~ScopedHolder() { clear(); }
|
||||
|
||||
|
||||
TY&
|
||||
create ()
|
||||
{
|
||||
|
|
@ -147,7 +172,8 @@ namespace lib {
|
|||
return *obj;
|
||||
}
|
||||
|
||||
~ScopedHolder()
|
||||
void
|
||||
clear ()
|
||||
{
|
||||
if (created_)
|
||||
get()->~TY();
|
||||
|
|
@ -198,16 +224,28 @@ namespace lib {
|
|||
bool operator! () const { return !created_; }
|
||||
|
||||
|
||||
private:
|
||||
static char must_be_empty (_ThisType const& ref)
|
||||
friend void
|
||||
transfer_control (ScopedHolder& from, ScopedHolder& to)
|
||||
{
|
||||
if (ref)
|
||||
throw lumiera::error::Logic("ScopedHolder protocol violation: "
|
||||
"copy operation after having invoked create().");
|
||||
return 0;
|
||||
if (!from) return;
|
||||
TRACE (test, "transfer_control<ScopedHolder>... from=%x to=%x",&from, &to);
|
||||
must_be_empty (to);
|
||||
to.create();
|
||||
try
|
||||
{
|
||||
transfer_control(*from,*to); // note: assumed to have no side-effect in case it throws
|
||||
from.clear();
|
||||
return;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
to.clear();
|
||||
WARN (test, "transfer_control operation aborted.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
137
src/lib/scopedholdertransfer.hpp
Normal file
137
src/lib/scopedholdertransfer.hpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
SCOPEDHOLDERVECTOR.hpp - using ScopedHolder within a STL vector
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef LIB_SCOPEDHOLDERVECTOR_H
|
||||
#define LIB_SCOPEDHOLDERVECTOR_H
|
||||
|
||||
#include "common/error.hpp"
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Addendum to scopedholder.hpp for transferring the lifecycle
|
||||
* management to another instance. Using these wrappers within
|
||||
* STL vector and similar containers may result in the need to
|
||||
* do a re-allocation in response to a request to grow.
|
||||
* Obviously such a feature needs support by the objects being
|
||||
* wrapped, which should provide an operation for transferring
|
||||
* lifecycle management in a controlled fashion. This behaviour
|
||||
* is similar to std::auto_ptr, but because we use a separate
|
||||
* dedicated operation, we avoid some of the dangers pertaining
|
||||
* the use of the latter: just taking the "value" can't kill
|
||||
* the managed object.
|
||||
* \par
|
||||
* To implement this feature we need
|
||||
* - a custom allocator to be used by the vector. By default
|
||||
* it is built as a thin proxy round std::allocator.
|
||||
* - the \em noncopyable type to be managed within the vector
|
||||
* needs to provide a custom extension point: when the
|
||||
* allocator detects the need to transfer control between
|
||||
* two instances, it will invoke a free function named
|
||||
* <tt> transfer_control(TY& from, TY& to)</tt> intended
|
||||
* to be found by ADL. Note: in case this function throws,
|
||||
* it <i>must not have any side effects</i>.
|
||||
* - besides, the \em noncopyable type needs to provide an
|
||||
* <tt>operator bool()</tt> yielding true iff currently
|
||||
* containing an managed object. This is similar to
|
||||
* boost::scoped_ptr or even the behaviour of a plain
|
||||
* old raw pointer, which is equivalent to \c true
|
||||
* when the pointer isn'T \c NULL
|
||||
*
|
||||
*/
|
||||
template<class TY, class PAR = std::allocator<TY> >
|
||||
class Allocator_TransferNoncopyable
|
||||
{
|
||||
typedef Allocator_TransferNoncopyable<TY,PAR> _ThisType;
|
||||
|
||||
PAR par_;
|
||||
|
||||
|
||||
public:
|
||||
typedef typename PAR::size_type size_type;
|
||||
typedef typename PAR::difference_type difference_type;
|
||||
typedef typename PAR::pointer pointer;
|
||||
typedef typename PAR::const_pointer const_pointer;
|
||||
typedef typename PAR::reference reference;
|
||||
typedef typename PAR::const_reference const_reference;
|
||||
typedef typename PAR::value_type value_type;
|
||||
|
||||
template<typename XX>
|
||||
struct rebind
|
||||
{ typedef Allocator_TransferNoncopyable<XX, PAR> other; };
|
||||
|
||||
Allocator_TransferNoncopyable() { }
|
||||
|
||||
Allocator_TransferNoncopyable(const _ThisType& allo)
|
||||
: par_(allo.par_) { }
|
||||
Allocator_TransferNoncopyable(const PAR& allo)
|
||||
: par_(allo) { }
|
||||
|
||||
template<typename X>
|
||||
Allocator_TransferNoncopyable(const std::allocator<X>&) { }
|
||||
|
||||
~Allocator_TransferNoncopyable() { }
|
||||
|
||||
|
||||
//------------proxying-the-parent-allocator------------------------------------
|
||||
|
||||
size_type max_size() const { return par_.max_size(); }
|
||||
pointer address(reference r) const { return par_.address(r); }
|
||||
const_pointer address(const_reference cr) const { return par_.address(cr); }
|
||||
pointer allocate(size_type n, const void *p=0){ return par_.allocate(n,p); }
|
||||
void deallocate(pointer p, size_type n) { return par_.deallocate(p,n); }
|
||||
void destroy(pointer p) { return par_.destroy(p); }
|
||||
|
||||
|
||||
void
|
||||
construct (pointer p, const TY& ref)
|
||||
{
|
||||
new(p) TY();
|
||||
ASSERT (p);
|
||||
ASSERT (!(*p), "protocol violation: target already manages another object.");
|
||||
if (ref)
|
||||
transfer_control (const_cast<TY&>(ref), *p);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TY1, typename TY2, class ALO>
|
||||
inline bool
|
||||
operator== (Allocator_TransferNoncopyable<TY1,ALO> const&, Allocator_TransferNoncopyable<TY2, ALO> const&)
|
||||
{ return true; }
|
||||
|
||||
template<typename TY1, typename TY2, class ALO>
|
||||
inline bool
|
||||
operator!= (Allocator_TransferNoncopyable<TY1,ALO> const&, Allocator_TransferNoncopyable<TY2,ALO> const&)
|
||||
{ return false; }
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace lib
|
||||
#endif
|
||||
|
|
@ -9,6 +9,6 @@ Import('env','artifacts','core')
|
|||
# build the ubiquitous Hello World application (note: C source)
|
||||
artifacts['tools'] = [ env.Program('#$BINDIR/hello-world','hello.c')
|
||||
+ env.Program('#$BINDIR/luidgen', ['luidgen.c']+core)
|
||||
+ env.Program('#$BINDIR/try', ['try.cpp']+core) #### to try out some feature...
|
||||
+ env.Program('#$BINDIR/try', 'try.cpp') #### to try out some feature...
|
||||
]
|
||||
|
||||
|
|
|
|||
173
src/tool/try.cpp
173
src/tool/try.cpp
|
|
@ -14,20 +14,16 @@
|
|||
// 10/8 - abusing the STL containers to hold noncopyable values
|
||||
|
||||
|
||||
//#include <nobug.h>
|
||||
#include "proc/nobugcfg.hpp"
|
||||
#include <nobug.h>
|
||||
//#include "proc/nobugcfg.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <typeinfo>
|
||||
//#include <typeinfo>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/scopedholder.hpp"
|
||||
//#include <boost/noncopyable.hpp>
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::vector;
|
||||
using boost::format;
|
||||
|
||||
|
||||
|
|
@ -37,150 +33,6 @@ using boost::format;
|
|||
}
|
||||
|
||||
|
||||
namespace funny {
|
||||
|
||||
|
||||
class Mrpf
|
||||
: boost::noncopyable
|
||||
{
|
||||
long secret_;
|
||||
|
||||
typedef Mrpf _ThisType;
|
||||
|
||||
public:
|
||||
Mrpf()
|
||||
: secret_(0)
|
||||
{
|
||||
cout << "Mrpf() this=" << this <<"\n";
|
||||
}
|
||||
~Mrpf()
|
||||
{
|
||||
checksum -= secret_;
|
||||
cout << "~Mrpf() this=" << this <<" skeret="<<secret_<<"\n";
|
||||
}
|
||||
|
||||
Mrpf (const Mrpf& o)
|
||||
: secret_(0)
|
||||
{
|
||||
cout << "Mrpf( ref ) this=" <<this << " o="<<&o<<"\n";
|
||||
ASSERT (!o.secret_);
|
||||
}
|
||||
|
||||
Mrpf&
|
||||
operator= (Mrpf const& ref)
|
||||
{
|
||||
cout << "Mrpf=ref this=" <<this << " ref="<<&ref<<"\n";
|
||||
ASSERT (!(*this));
|
||||
ASSERT (!ref);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
setup (long x=0)
|
||||
{
|
||||
secret_ = (x? x : (rand() % 10000));
|
||||
checksum += secret_;
|
||||
cout << "this="<<this<<" ....setup-->"<<secret_<<"\n";
|
||||
}
|
||||
|
||||
long getIt() const { return secret_; }
|
||||
|
||||
|
||||
typedef long _ThisType::*unspecified_bool_type;
|
||||
|
||||
/** implicit conversion to "bool" */
|
||||
operator unspecified_bool_type() const // never throws
|
||||
{
|
||||
return secret_? &_ThisType::secret_ : 0;
|
||||
}
|
||||
|
||||
bool operator! () const { return 0==secret_; }
|
||||
|
||||
|
||||
protected:
|
||||
friend void transfer_control (Mrpf& from, Mrpf& to);
|
||||
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
transfer_control (Mrpf& from, Mrpf& to)
|
||||
{
|
||||
ASSERT (!to.secret_);
|
||||
cout << "transfer... from="<<&from<<" to="<<&to<<"\n";
|
||||
to.secret_ = from.secret_;
|
||||
from.secret_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<class TY, class PAR = std::allocator<TY> >
|
||||
class Allocator_TransferNoncopyable
|
||||
{
|
||||
typedef Allocator_TransferNoncopyable<TY,PAR> _ThisType;
|
||||
|
||||
PAR par_;
|
||||
|
||||
|
||||
public:
|
||||
typedef typename PAR::size_type size_type;
|
||||
typedef typename PAR::difference_type difference_type;
|
||||
typedef typename PAR::pointer pointer;
|
||||
typedef typename PAR::const_pointer const_pointer;
|
||||
typedef typename PAR::reference reference;
|
||||
typedef typename PAR::const_reference const_reference;
|
||||
typedef typename PAR::value_type value_type;
|
||||
|
||||
template<typename _Tp1>
|
||||
struct rebind
|
||||
{ typedef Allocator_TransferNoncopyable<_Tp1, PAR> other; };
|
||||
|
||||
Allocator_TransferNoncopyable() { }
|
||||
|
||||
Allocator_TransferNoncopyable(const _ThisType& __a)
|
||||
: par_(__a.par_) { }
|
||||
Allocator_TransferNoncopyable(const PAR& __a)
|
||||
: par_(__a) { }
|
||||
|
||||
template<typename X>
|
||||
Allocator_TransferNoncopyable(const std::allocator<X>&) { }
|
||||
|
||||
~Allocator_TransferNoncopyable() { }
|
||||
|
||||
|
||||
|
||||
size_type max_size() const { return par_.max_size(); }
|
||||
pointer address(reference r) const { return par_.address(r); }
|
||||
const_pointer address(const_reference cr) const { return par_.address(cr); }
|
||||
pointer allocate(size_type n, const void *p=0){ return par_.allocate(n,p); }
|
||||
void deallocate(pointer p, size_type n) { return par_.deallocate(p,n); }
|
||||
void destroy(pointer p) { return par_.destroy(p); }
|
||||
|
||||
|
||||
void
|
||||
construct (pointer p, const TY& ref)
|
||||
{
|
||||
cout << "Allo::construct( p="<<p<<", ref="<<&ref<<" )\n";
|
||||
new(p) TY();
|
||||
ASSERT (p);
|
||||
ASSERT (!(*p));
|
||||
if (ref)
|
||||
transfer_control (const_cast<TY&>(ref), *p);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename _T1, typename _T2>
|
||||
inline bool
|
||||
operator== (Allocator_TransferNoncopyable<_T1> const&, Allocator_TransferNoncopyable<_T2> const&)
|
||||
{ return true; }
|
||||
|
||||
template<typename _T1, typename _T2>
|
||||
inline bool
|
||||
operator!= (Allocator_TransferNoncopyable<_T1> const&, Allocator_TransferNoncopyable<_T2> const&)
|
||||
{ return false; }
|
||||
|
||||
|
||||
|
||||
typedef Allocator_TransferNoncopyable<funny::Mrpf> Allo;
|
||||
|
||||
int
|
||||
main (int argc, char* argv[])
|
||||
|
|
@ -188,22 +40,7 @@ main (int argc, char* argv[])
|
|||
|
||||
NOBUG_INIT;
|
||||
|
||||
vector<funny::Mrpf, Allo> mrmpf;
|
||||
|
||||
mrmpf.reserve(2);
|
||||
mrmpf.push_back(funny::Mrpf());
|
||||
mrmpf[0].setup(33);
|
||||
|
||||
cout << ".....resize:\n";
|
||||
|
||||
mrmpf.resize(5);
|
||||
|
||||
|
||||
cout << "Summmmmmmmmmmmmmmmmmmm="<<checksum<<"\n";
|
||||
mrmpf.clear();
|
||||
cout << "Summmmmmmmmmmmmmmmmmm="<<checksum<<"\n";
|
||||
|
||||
cout << "\ngulp\n";
|
||||
cout << "\n.gulp.\n";
|
||||
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include "common/util.hpp"
|
||||
|
||||
#include "lib/scopedholder.hpp"
|
||||
#include "testdummy.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <iostream>
|
||||
|
|
@ -40,39 +41,9 @@ namespace lib {
|
|||
|
||||
using std::map;
|
||||
using std::cout;
|
||||
|
||||
namespace { // yet another test dummy
|
||||
|
||||
long checksum = 0;
|
||||
bool magic = false;
|
||||
|
||||
class Dummy
|
||||
: boost::noncopyable
|
||||
{
|
||||
int val_;
|
||||
|
||||
public:
|
||||
Dummy ()
|
||||
: val_(1 + (rand() % 100000000))
|
||||
{
|
||||
checksum += val_;
|
||||
if (magic)
|
||||
throw val_;
|
||||
}
|
||||
|
||||
~Dummy()
|
||||
{
|
||||
checksum -= val_;
|
||||
}
|
||||
|
||||
long add (int i) { return val_+i; }
|
||||
};
|
||||
|
||||
|
||||
typedef ScopedHolder<Dummy> HolderD;
|
||||
typedef ScopedPtrHolder<Dummy> PtrHolderD;
|
||||
|
||||
}
|
||||
typedef ScopedHolder<Dummy> HolderD;
|
||||
typedef ScopedPtrHolder<Dummy> PtrHolderD;
|
||||
|
||||
|
||||
/**********************************************************************************
|
||||
|
|
|
|||
164
tests/common/scopedholdertransfertest.cpp
Normal file
164
tests/common/scopedholdertransfertest.cpp
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
ScopedHolderTransfer(Test) - managing noncopyable objects within a growing vector
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
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.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
|
||||
|
||||
#include "common/test/run.hpp"
|
||||
#include "common/util.hpp"
|
||||
|
||||
#include "lib/scopedholder.hpp"
|
||||
#include "lib/scopedholdertransfer.hpp"
|
||||
#include "testdummy.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test {
|
||||
|
||||
using ::Test;
|
||||
using util::isnil;
|
||||
|
||||
using std::map;
|
||||
using std::cout;
|
||||
|
||||
|
||||
typedef ScopedHolder<Dummy> HolderD;
|
||||
typedef ScopedPtrHolder<Dummy> PtrHolderD;
|
||||
|
||||
|
||||
|
||||
/**********************************************************************************
|
||||
* @test ScopedHolder and ScopedPtrHolder are initially empty and copyable.
|
||||
* After taking ownership, they prohibit copy operations, manage the
|
||||
* lifecycle of the contained object and provide smart-ptr like access.
|
||||
* A series of identical tests is conducted both with the ScopedPtrHolder
|
||||
* (the contained objects are heap allocated but managed by the holder)
|
||||
* and with the ScopedHolder (objects placed inline)
|
||||
*/
|
||||
class ScopedHolderTransfer_test : public Test
|
||||
{
|
||||
|
||||
virtual void
|
||||
run (Arg arg)
|
||||
{
|
||||
|
||||
cout << "checking ScopedHolder<Dummy>...\n";
|
||||
buildVector<HolderD>();
|
||||
growVector<HolderD>();
|
||||
checkErrorHandling<HolderD>();
|
||||
|
||||
cout << "checking ScopedPtrHolder<Dummy>...\n";
|
||||
buildVector<PtrHolderD>();
|
||||
growVector<PtrHolderD>();
|
||||
checkErrorHandling<PtrHolderD>();
|
||||
}
|
||||
|
||||
void create_contained_object (HolderD& holder) { holder.create(); }
|
||||
void create_contained_object (PtrHolderD& holder) { holder.reset(new Dummy()); }
|
||||
|
||||
|
||||
template<class HO>
|
||||
void
|
||||
buildVector()
|
||||
{
|
||||
ASSERT (0==checksum);
|
||||
{
|
||||
UNIMPLEMENTED ("create constant sized vector holding noncopyables");
|
||||
HO holder;
|
||||
ASSERT (!holder);
|
||||
ASSERT (0==checksum);
|
||||
|
||||
create_contained_object (holder);
|
||||
ASSERT (holder);
|
||||
ASSERT (false!=holder);
|
||||
ASSERT (holder!=false);
|
||||
|
||||
ASSERT (0!=checksum);
|
||||
ASSERT ( &(*holder));
|
||||
ASSERT (holder->add(2) == checksum+2);
|
||||
|
||||
Dummy *rawP = holder.get();
|
||||
ASSERT (rawP);
|
||||
ASSERT (holder);
|
||||
ASSERT (rawP == &(*holder));
|
||||
ASSERT (rawP->add(-5) == holder->add(-5));
|
||||
|
||||
TRACE (test, "holder at %x", &holder);
|
||||
TRACE (test, "object at %x", holder.get() );
|
||||
TRACE (test, "size(object) = %d", sizeof(*holder));
|
||||
TRACE (test, "size(holder) = %d", sizeof(holder));
|
||||
}
|
||||
ASSERT (0==checksum);
|
||||
}
|
||||
|
||||
|
||||
template<class HO>
|
||||
void
|
||||
growVector()
|
||||
{
|
||||
ASSERT (0==checksum);
|
||||
{
|
||||
UNIMPLEMENTED ("check growing a vector holding noncopyables");
|
||||
}
|
||||
ASSERT (0==checksum);
|
||||
}
|
||||
|
||||
|
||||
template<class HO>
|
||||
void
|
||||
checkErrorHandling()
|
||||
{
|
||||
UNIMPLEMENTED ("throw an error while growing");
|
||||
ASSERT (0==checksum);
|
||||
{
|
||||
HO holder;
|
||||
|
||||
magic = true;
|
||||
try
|
||||
{
|
||||
create_contained_object (holder);
|
||||
NOTREACHED ;
|
||||
}
|
||||
catch (int val)
|
||||
{
|
||||
ASSERT (0!=checksum);
|
||||
checksum -= val;
|
||||
ASSERT (0==checksum);
|
||||
}
|
||||
ASSERT (!holder); /* because the exception happens in ctor
|
||||
object doesn't count as "created" */
|
||||
magic = false;
|
||||
}
|
||||
ASSERT (0==checksum);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LAUNCHER (ScopedHolderTransfer_test, "unit common");
|
||||
|
||||
|
||||
}// namespace test
|
||||
|
||||
} // namespace lib
|
||||
|
||||
73
tests/common/testdummy.hpp
Normal file
73
tests/common/testdummy.hpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
TESTDUMMY.hpp - yet another test dummy for tracking ctor/dtor calls
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
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.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test {
|
||||
|
||||
namespace { // yet another test dummy
|
||||
|
||||
long checksum = 0;
|
||||
bool magic = false;
|
||||
|
||||
class Dummy
|
||||
: boost::noncopyable
|
||||
{
|
||||
int val_;
|
||||
|
||||
public:
|
||||
Dummy ()
|
||||
: val_(1 + (rand() % 100000000))
|
||||
{
|
||||
checksum += val_;
|
||||
if (magic)
|
||||
throw val_;
|
||||
}
|
||||
|
||||
~Dummy()
|
||||
{
|
||||
checksum -= val_;
|
||||
}
|
||||
|
||||
long add (int i) { return val_+i; }
|
||||
|
||||
protected:
|
||||
int getVal() const { return val_; }
|
||||
|
||||
void
|
||||
setVal (int newVal)
|
||||
{
|
||||
checksum += newVal - val_;
|
||||
val_ = newVal;
|
||||
}
|
||||
};
|
||||
|
||||
}// anonymous test dummy
|
||||
|
||||
}// namespace test
|
||||
|
||||
} // namespace lib
|
||||
|
||||
163
tests/common/vectortransfertest.cpp
Normal file
163
tests/common/vectortransfertest.cpp
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
VectorTransfer(Test) - intercept object copying when a STL vector grows
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
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.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
|
||||
|
||||
#include "common/test/run.hpp"
|
||||
|
||||
#include "lib/scopedholdertransfer.hpp"
|
||||
#include "testdummy.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test {
|
||||
|
||||
using ::Test;
|
||||
using std::vector;
|
||||
using std::cout;
|
||||
|
||||
namespace { // extending the Dummy for our special purpose....
|
||||
|
||||
class TransDummy
|
||||
: public Dummy
|
||||
{
|
||||
public:
|
||||
TransDummy()
|
||||
{
|
||||
TRACE (test, "CTOR TransDummy() --> this=%x", this);
|
||||
setVal(0); // we use val_==0 to mark the "empty" state
|
||||
}
|
||||
|
||||
~TransDummy()
|
||||
{
|
||||
TRACE (test, "DTOR ~TransDummy() this=%x", this);
|
||||
}
|
||||
|
||||
/* to make Dummy usable within vector, we need to provide
|
||||
* \em special copy operations, an operator bool() and
|
||||
* a transfer_control friend function to be used by
|
||||
* our special allocator.
|
||||
*/
|
||||
|
||||
TransDummy (const TransDummy& o)
|
||||
{
|
||||
TRACE (test, "COPY-ctor TransDummy( ref=%x ) --> this=%x", &o,this);
|
||||
ASSERT (!o, "protocol violation: real copy operations inhibited");
|
||||
}
|
||||
|
||||
TransDummy&
|
||||
operator= (TransDummy const& ref)
|
||||
{
|
||||
TRACE (test, "COPY target=%x <-- source=%x", this,&ref);
|
||||
ASSERT (!(*this));
|
||||
ASSERT (!ref, "protocol violation: real copy operations inhibited");
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
setup (int x=0)
|
||||
{
|
||||
setVal (x? x : (rand() % 10000));
|
||||
TRACE (test, "CREATE val=%d ---> this=%x", getVal(),this);
|
||||
}
|
||||
|
||||
|
||||
// define implicit conversion to "bool" the naive way...
|
||||
operator bool() const
|
||||
{
|
||||
return 0!=getVal();
|
||||
}
|
||||
|
||||
|
||||
friend void transfer_control (TransDummy& from, TransDummy& to);
|
||||
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
transfer_control (TransDummy& from, TransDummy& to)
|
||||
{
|
||||
TRACE (test, "TRANSFER target=%x <-- source=%x", &to,&from);
|
||||
ASSERT (!to, "protocol violation: target already manages another object");
|
||||
to.setVal (from.getVal());
|
||||
from.setVal(0);
|
||||
}
|
||||
|
||||
typedef Allocator_TransferNoncopyable<TransDummy> Allo;
|
||||
typedef vector<TransDummy, Allo> TransDummyVector;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**********************************************************************************
|
||||
* @test ScopedHolder and ScopedPtrHolder are initially empty and copyable.
|
||||
* After taking ownership, they prohibit copy operations, manage the
|
||||
* lifecycle of the contained object and provide smart-ptr like access.
|
||||
* A series of identical tests is conducted both with the ScopedPtrHolder
|
||||
* (the contained objects are heap allocated but managed by the holder)
|
||||
* and with the ScopedHolder (objects placed inline)
|
||||
*/
|
||||
class VectorTransfer_test : public Test
|
||||
{
|
||||
|
||||
virtual void
|
||||
run (Arg arg)
|
||||
{
|
||||
cout << "\n..setup table space for 2 elements\n";
|
||||
TransDummyVector table;
|
||||
table.reserve(2);
|
||||
ASSERT (0==checksum);
|
||||
|
||||
cout << "\n..install one element at index[0]\n";
|
||||
table.push_back(TransDummy());
|
||||
ASSERT (0==checksum);
|
||||
|
||||
table[0].setup(); // switches into "managed" state
|
||||
ASSERT (0 < checksum);
|
||||
int theSum = checksum;
|
||||
|
||||
cout << "\n..*** resize table to 5 elements\n";
|
||||
table.resize(5);
|
||||
ASSERT (theSum==checksum);
|
||||
|
||||
cout << "\n..install another element\n";
|
||||
table[3].setup(375);
|
||||
ASSERT (theSum+375==checksum);
|
||||
|
||||
cout << "\n..kill all elements....\n";
|
||||
table.clear();
|
||||
ASSERT (0==checksum);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LAUNCHER (VectorTransfer_test, "unit common");
|
||||
|
||||
|
||||
}// namespace test
|
||||
|
||||
} // namespace lib
|
||||
|
||||
Loading…
Reference in a new issue