Test-driven brainstorming: draft a ScopedCollection #877

This commit is contained in:
Fischlurch 2012-01-01 06:20:42 +01:00
parent f047069284
commit e63fa6d646
9 changed files with 516 additions and 14 deletions

View file

@ -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);
}

View 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

View file

@ -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)

View file

@ -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

View file

@ -102,6 +102,8 @@ namespace play {
TimeValue
deadlineFor (FrameID frameNr)
{
// a real world implementation
// would offset by a latency here
return frameGrid_->timeOf (frameNr);
}

View file

@ -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

View 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

View file

@ -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));

View file

@ -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());
}