Test-driven brainstorming: draft a ScopedCollection #877
This commit is contained in:
parent
f047069284
commit
e63fa6d646
9 changed files with 516 additions and 14 deletions
|
|
@ -253,6 +253,8 @@ namespace lib {
|
|||
template<typename SUB>
|
||||
struct Buff : Buffer
|
||||
{
|
||||
BOOST_STATIC_ASSERT (siz >= sizeof(SUB));
|
||||
|
||||
SUB&
|
||||
get() const ///< core operation: target is contained within the inline buffer
|
||||
{
|
||||
|
|
@ -267,8 +269,6 @@ namespace lib {
|
|||
explicit
|
||||
Buff (SUB const& obj)
|
||||
{
|
||||
BOOST_STATIC_ASSERT (siz >= sizeof(SUB));
|
||||
|
||||
new(Buffer::ptr()) SUB (obj);
|
||||
}
|
||||
|
||||
|
|
|
|||
161
src/lib/scoped-collection.hpp
Normal file
161
src/lib/scoped-collection.hpp
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
SCOPED-COLLECTION.hpp - managing a fixed collection of noncopyable polymorphic objects
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2012, 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.
|
||||
|
||||
*/
|
||||
|
||||
/** @file scoped-collection.hpp
|
||||
** Managing a collection of noncopyable polymorphic objects in compact storage.
|
||||
** This helper supports the frequently encountered situation where a service
|
||||
** implementation internally manages a collection of implementation related
|
||||
** sub-components with reference semantics. Typically, those objects are
|
||||
** being used polymorphically, and often they are also added step by step.
|
||||
** The storage holding all those child objects is allocated in one chunk
|
||||
** and never adjusted.
|
||||
**
|
||||
** - TODO: retro-fit with RefArray interface
|
||||
**
|
||||
** @see ScopedCollection_test
|
||||
** @see scoped-ptrvect.hpp quite similar, but using individual heap pointers
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LIB_SCOPED_COLLECTION_H
|
||||
#define LIB_SCOPED_COLLECTION_H
|
||||
|
||||
|
||||
//#include "include/logging.h"
|
||||
//#include "lib/iter-adapter.hpp"
|
||||
#include "lib/error.hpp"
|
||||
//#include "lib/util.hpp"
|
||||
|
||||
//#include <vector>
|
||||
//#include <algorithm>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A fixed collection of noncopyable polymorphic objects.
|
||||
* All child objects reside in a common chunk of storage
|
||||
* and are owned and managed by this collection holder.
|
||||
* Array style access and iteration.
|
||||
*/
|
||||
template<class T>
|
||||
class ScopedCollection
|
||||
: boost::noncopyable
|
||||
{
|
||||
// typedef std::vector<T*> _Vec;
|
||||
// typedef typename _Vec::iterator VIter;
|
||||
//
|
||||
// typedef RangeIter<VIter> RIter;
|
||||
// typedef PtrDerefIter<RIter> IterType;
|
||||
//
|
||||
// typedef typename IterType::ConstIterType ConstIterType;
|
||||
// typedef typename IterType::WrappedConstIterType RcIter;
|
||||
|
||||
|
||||
public:
|
||||
typedef size_t size_type;
|
||||
// typedef T & reference;
|
||||
// typedef T const& const_reference;
|
||||
|
||||
|
||||
|
||||
ScopedCollection ()
|
||||
{ }
|
||||
|
||||
explicit
|
||||
ScopedCollection (size_type capacity)
|
||||
{
|
||||
UNIMPLEMENTED ("prepare scoped collection storage");
|
||||
}
|
||||
|
||||
~ScopedCollection ()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void
|
||||
clear()
|
||||
{
|
||||
UNIMPLEMENTED ("drop all content objects");
|
||||
}
|
||||
|
||||
|
||||
/* === Element access and iteration === */
|
||||
|
||||
T&
|
||||
operator[] (size_type i)
|
||||
{
|
||||
return *get(i);
|
||||
}
|
||||
|
||||
// typedef IterType iterator;
|
||||
// typedef ConstIterType const_iterator;
|
||||
//
|
||||
// iterator begin() { return iterator (allPtrs()); }
|
||||
// iterator end() { return iterator ( RIter() ); }
|
||||
// const_iterator begin() const { return const_iterator::build_by_cast (allPtrs()); }
|
||||
// const_iterator end() const { return const_iterator::nil(); }
|
||||
|
||||
|
||||
|
||||
|
||||
/* ====== proxied vector functions ==================== */
|
||||
|
||||
// size_type size () const { return _Vec::size(); }
|
||||
// size_type max_size () const { return _Vec::max_size(); }
|
||||
// size_type capacity () const { return _Vec::capacity(); }
|
||||
// bool empty () const { return _Vec::empty(); }
|
||||
|
||||
|
||||
private:
|
||||
/** @internal element access, including range and null check */
|
||||
T*
|
||||
get (size_type i)
|
||||
{
|
||||
UNIMPLEMENTED("raw element access");
|
||||
}
|
||||
|
||||
// /** @internal access sequence of all managed pointers */
|
||||
// RIter
|
||||
// allPtrs ()
|
||||
// {
|
||||
// return RIter (_Vec::begin(), _Vec::end());
|
||||
// }
|
||||
// RIter
|
||||
// allPtrs () const
|
||||
// {
|
||||
// _Vec& elements = util::unConst(*this);
|
||||
// return RIter (elements.begin(), elements.end());
|
||||
// }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace lib
|
||||
#endif
|
||||
|
|
@ -48,14 +48,22 @@ namespace test{
|
|||
: val_(v)
|
||||
{ init(); }
|
||||
|
||||
~Dummy()
|
||||
virtual ~Dummy()
|
||||
{
|
||||
checksum() -= val_;
|
||||
}
|
||||
|
||||
long add (int i) { return val_+i; }
|
||||
virtual long
|
||||
acc (int i) ///< dummy API operation
|
||||
{
|
||||
return val_+i;
|
||||
}
|
||||
|
||||
int getVal() const { return val_; }
|
||||
int
|
||||
getVal() const
|
||||
{
|
||||
return val_;
|
||||
}
|
||||
|
||||
void
|
||||
setVal (int newVal)
|
||||
|
|
|
|||
|
|
@ -617,7 +617,12 @@ out: ^\.throw some exceptions...
|
|||
END
|
||||
|
||||
|
||||
TEST "ScopedPtrVect_test" ScopedPtrVect_test <<END
|
||||
PLANNED "Managed Collection (I)" ScopedCollection_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Managed Collection (II)" ScopedPtrVect_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,8 @@ namespace play {
|
|||
TimeValue
|
||||
deadlineFor (FrameID frameNr)
|
||||
{
|
||||
// a real world implementation
|
||||
// would offset by a latency here
|
||||
return frameGrid_->timeOf (frameNr);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ namespace test{
|
|||
|
||||
namespace { // private test setup...
|
||||
|
||||
const uint NUM_THREADS = 50;
|
||||
const uint NUM_THREADS = 40;
|
||||
const uint MAX_RAND = 1000*1000;
|
||||
|
||||
inline bool
|
||||
|
|
|
|||
326
tests/lib/scoped-collection-test.cpp
Normal file
326
tests/lib/scoped-collection-test.cpp
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
ScopedCollection(Test) - holding and owning a fixed collection of noncopyable objects
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2012, 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 "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include "lib/scoped-collection.hpp"
|
||||
#include "lib/test/testdummy.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test{
|
||||
|
||||
namespace error = lumiera::error;
|
||||
|
||||
|
||||
namespace { // our explosive special Dummy
|
||||
|
||||
LUMIERA_ERROR_DEFINE(SUBVERSIVE, "undercover action");
|
||||
|
||||
class SubDummy
|
||||
: public Dummy
|
||||
{
|
||||
int trigger_;
|
||||
|
||||
/** special variant of the dummy API operation:
|
||||
* @param i when zero, the trigger value will be revealed
|
||||
*/
|
||||
virtual long
|
||||
acc (int i)
|
||||
{
|
||||
if (!i)
|
||||
return getVal() + trigger_;
|
||||
else
|
||||
return Dummy::acc(i);
|
||||
}
|
||||
|
||||
public:
|
||||
SubDummy (int id, int trigger)
|
||||
: Dummy(id)
|
||||
, trigger_(trigger)
|
||||
{
|
||||
if (trigger == getVal())
|
||||
throw new error::Fatal ("Subversive Bomb", LUMIERA_ERROR_SUBVERSIVE);
|
||||
}
|
||||
};
|
||||
|
||||
}//(End) subversive test data
|
||||
|
||||
|
||||
|
||||
|
||||
using util::isnil;
|
||||
//using std::tr1::placeholders::_1; /////////////////////////////TODO
|
||||
using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
|
||||
|
||||
typedef ScopedCollection<Dummy> CollD;
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* @test ScopedCollection manages a fixed set of objects, but these
|
||||
* child objects are noncopyable, may be polymorphic, an can
|
||||
* be created either all at once or chunk wise. The API is
|
||||
* similar to a vector and allows for element access
|
||||
* and iteration.
|
||||
*/
|
||||
class ScopedCollection_test : public Test
|
||||
{
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
simpleUsage();
|
||||
building_RAII_Style();
|
||||
building_StackStyle();
|
||||
iterating();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
simpleUsage()
|
||||
{
|
||||
CHECK (0 == Dummy::checksum());
|
||||
{
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877
|
||||
CollD container(5);
|
||||
CHECK (isnil (container));
|
||||
CHECK (0 == container.size());
|
||||
CHECK (0 == Dummy::checksum());
|
||||
|
||||
container.populate();
|
||||
CHECK (!isnil (container));
|
||||
CHECK (5 == container.size());
|
||||
CHECK (0 != Dummy::checksum());
|
||||
|
||||
holder.clear();
|
||||
CHECK (isnil (container));
|
||||
CHECK (0 == container.size());
|
||||
CHECK (0 == Dummy::checksum());
|
||||
|
||||
container.populate();
|
||||
CHECK (Dummy::checksum() == container[0].getVal()
|
||||
+ container[1].getVal()
|
||||
+ container[2].getVal()
|
||||
+ container[3].getVal()
|
||||
+ container[4].getVal());
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877
|
||||
}
|
||||
CHECK (0 == Dummy::checksum());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
iterating()
|
||||
{
|
||||
CHECK (0 == Dummy::checksum());
|
||||
{
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877
|
||||
CollD coll(50);
|
||||
for (int i=0; i<coll.capacity(); ++i)
|
||||
coll.appendNew<Dummy>(i);
|
||||
|
||||
int check=0;
|
||||
CollD::iterator ii = coll.begin();
|
||||
while (ii)
|
||||
{
|
||||
CHECK (check == ii->getVal());
|
||||
CHECK (check == ii->acc(+5) - 5);
|
||||
++check;
|
||||
++ii;
|
||||
}
|
||||
|
||||
|
||||
// Test the const iterator
|
||||
check = 0;
|
||||
CollD::const_iterator cii = coll.begin();
|
||||
while (cii)
|
||||
{
|
||||
CHECK (check == cii->getVal());
|
||||
++check;
|
||||
++cii;
|
||||
}
|
||||
|
||||
|
||||
// Verify correct behaviour of iteration end
|
||||
CHECK (! (coll.end()));
|
||||
CHECK (isnil (coll.end()));
|
||||
|
||||
VERIFY_ERROR (ITER_EXHAUST, *coll.end() );
|
||||
VERIFY_ERROR (ITER_EXHAUST, ++coll.end() );
|
||||
|
||||
CHECK (ii == coll.end());
|
||||
CHECK (cii == coll.end());
|
||||
VERIFY_ERROR (ITER_EXHAUST, ++ii );
|
||||
VERIFY_ERROR (ITER_EXHAUST, ++cii );
|
||||
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877
|
||||
}
|
||||
CHECK (0 == Dummy::checksum());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
building_RAII_Style()
|
||||
{
|
||||
UNIMPLEMENTED ("building all at once");
|
||||
CHECK (0 == Dummy::checksum());
|
||||
{
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877
|
||||
/** Functor to populate the Collection */
|
||||
class Populator
|
||||
{
|
||||
uint i_;
|
||||
int off_;
|
||||
|
||||
public:
|
||||
Populator (int baseOffset)
|
||||
: i_(0)
|
||||
, off_(baseOffset)
|
||||
{ }
|
||||
|
||||
void
|
||||
operator() (int specialOffset, void* storage)
|
||||
{
|
||||
switch (i_ % 2)
|
||||
{
|
||||
case 0:
|
||||
new(storage) Dummy(i_+off_);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
new(storage) SubDummy(i_+off_, specialOffset);
|
||||
break;
|
||||
} }
|
||||
};
|
||||
|
||||
|
||||
int rr = rand() % 100;
|
||||
int trigger = 101;
|
||||
|
||||
CollD coll (6, Populator(rr), trigger, _1 );
|
||||
|
||||
CHECK (!isnil (coll));
|
||||
CHECK (6 == coll.size());
|
||||
CHECK (0 != Dummy::checksum());
|
||||
|
||||
CHECK (coll[0].acc(0) == 0 + rr);
|
||||
CHECK (coll[1].acc(0) == 1 + rr + trigger);
|
||||
CHECK (coll[2].acc(0) == 2 + rr);
|
||||
CHECK (coll[3].acc(0) == 3 + rr + trigger);
|
||||
CHECK (coll[4].acc(0) == 4 + rr);
|
||||
CHECK (coll[5].acc(0) == 5 + rr + trigger);
|
||||
|
||||
coll.clear();
|
||||
CHECK (0 == Dummy::checksum());
|
||||
|
||||
// Verify Error handling while in creation:
|
||||
// SubDummy explodes on equal ctor parameters
|
||||
// which here happens for i==7
|
||||
VERIFY_ERROR (SUBVERSIVE, CollD(10, Populator(0), 7, _1 ) );
|
||||
|
||||
// any already created object was properly destroyed
|
||||
CHECK (0 == Dummy::checksum());
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877
|
||||
}
|
||||
CHECK (0 == Dummy::checksum());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
building_StackStyle()
|
||||
{
|
||||
UNIMPLEMENTED ("pushing new objects successively");
|
||||
CHECK (0 == Dummy::checksum());
|
||||
{
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877
|
||||
|
||||
int rr = rand() % 100;
|
||||
|
||||
CollD coll(3);
|
||||
CHECK (0 == coll.size());
|
||||
CHECK (0 == Dummy::checksum());
|
||||
|
||||
Dummy& d0 = coll.appendNew();
|
||||
CHECK (1 == coll.size());
|
||||
|
||||
Dummy& d1 = coll.appendNew<Dummy> (r1);
|
||||
CHECK (2 == coll.size());
|
||||
|
||||
int sum = Dummy::checksum();
|
||||
|
||||
// trigger the bomb
|
||||
VERIFY_ERROR (SUBVERSIVE, coll.appendNew<SubSummy>(r1,r1) );
|
||||
|
||||
CHECK ( 2 == coll.size()); // the other objects survived
|
||||
CHECK (sum == Dummy::checksum());
|
||||
|
||||
Dummy& d2 = coll.appendNew<SubDummy> (r1, r1+1);
|
||||
CHECK (3 == coll.size());
|
||||
|
||||
CHECK (sum + r1 == Dummy::checksum());
|
||||
|
||||
VERIFY_ERROR (CAPACITY, coll.appendNew());
|
||||
VERIFY_ERROR (CAPACITY, coll.appendNew());
|
||||
VERIFY_ERROR (CAPACITY, coll.appendNew());
|
||||
|
||||
CHECK (3 == coll.size());
|
||||
CHECK (sum + r1 == Dummy::checksum());
|
||||
|
||||
|
||||
CHECK (d0.acc(11) == coll[0].getVal() + 11 );
|
||||
CHECK (d1.acc(22) == r1 + 22);
|
||||
CHECK (d2.acc(33) == r1 + 33);
|
||||
CHECK (d2.acc(0) == r1 + (r1+1) ); // SubDummy's special implementation of the acc()-function
|
||||
// returns the trigger value, when the argument is zero
|
||||
|
||||
coll.clear();
|
||||
coll.appendNew<SubDummy> (11,22);
|
||||
|
||||
CHECK (3 == coll.size());
|
||||
CHECK (11 == Dummy::checksum());
|
||||
|
||||
// NOTE DANGEROUS:
|
||||
// The previously obtained references just point into the object storage.
|
||||
// Thus we're now accessing a different object, even a different type!
|
||||
CHECK (d0.acc(0) == 11 + 22);
|
||||
|
||||
// The others even point into obsoleted storage holding zombie objects
|
||||
CHECK (d1.acc(44) == r1 + 44);
|
||||
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #877
|
||||
}
|
||||
CHECK (0 == Dummy::checksum());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
LAUNCHER (ScopedCollection_test, "unit common");
|
||||
|
||||
|
||||
}} // namespace lib::test
|
||||
|
||||
|
|
@ -96,15 +96,15 @@ namespace test{
|
|||
CHECK (false!=holder);
|
||||
CHECK (holder!=false);
|
||||
|
||||
CHECK (0 != Dummy::checksum());
|
||||
CHECK (0 < Dummy::checksum());
|
||||
CHECK ( &(*holder));
|
||||
CHECK (holder->add(2) == 2 + Dummy::checksum());
|
||||
CHECK (holder->acc(2) == 2 + Dummy::checksum());
|
||||
|
||||
Dummy *rawP = holder.get();
|
||||
CHECK (rawP);
|
||||
CHECK (holder);
|
||||
CHECK (rawP == &(*holder));
|
||||
CHECK (rawP->add(-5) == holder->add(-5));
|
||||
CHECK (rawP->acc(-5) == holder->acc(-5));
|
||||
|
||||
TRACE (test, "holder at %p", &holder);
|
||||
TRACE (test, "object at %p", holder.get() );
|
||||
|
|
@ -127,7 +127,7 @@ namespace test{
|
|||
try
|
||||
{
|
||||
create_contained_object (holder);
|
||||
NOTREACHED ();
|
||||
NOTREACHED ("expect failure in ctor");
|
||||
}
|
||||
catch (int val)
|
||||
{
|
||||
|
|
@ -221,13 +221,13 @@ namespace test{
|
|||
{
|
||||
create_contained_object (maph[i]);
|
||||
CHECK (maph[i]);
|
||||
CHECK (0 < maph[i]->add(12));
|
||||
CHECK (0 < maph[i]->acc(12));
|
||||
}
|
||||
CHECK (100==maph.size());
|
||||
CHECK (0 != Dummy::checksum());
|
||||
|
||||
|
||||
long value55 = maph[55]->add(0);
|
||||
long value55 = maph[55]->acc(0);
|
||||
long currSum = Dummy::checksum();
|
||||
|
||||
CHECK (1 == maph.erase(55));
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ namespace test {
|
|||
CHECK (rawP);
|
||||
CHECK (table[5]);
|
||||
CHECK (rawP == &(*table[5]));
|
||||
CHECK (rawP->add(-555) == table[5]->add(-555));
|
||||
CHECK (rawP->acc(-555) == table[5]->acc(-555));
|
||||
}
|
||||
CHECK (0 == Dummy::checksum());
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue