From e63fa6d64643688b8cfa75fa3519b6e2cfe2951c Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 1 Jan 2012 06:20:42 +0100 Subject: [PATCH] Test-driven brainstorming: draft a ScopedCollection #877 --- src/lib/opaque-holder.hpp | 4 +- src/lib/scoped-collection.hpp | 161 +++++++++ src/lib/test/testdummy.hpp | 14 +- tests/40components.tests | 7 +- .../proc/play/diagnostic-output-slot.hpp | 2 + tests/lib/diagnostic-context-test.cpp | 2 +- tests/lib/scoped-collection-test.cpp | 326 ++++++++++++++++++ tests/lib/scoped-holder-test.cpp | 12 +- tests/lib/scoped-holder-transfer-test.cpp | 2 +- 9 files changed, 516 insertions(+), 14 deletions(-) create mode 100644 src/lib/scoped-collection.hpp create mode 100644 tests/lib/scoped-collection-test.cpp diff --git a/src/lib/opaque-holder.hpp b/src/lib/opaque-holder.hpp index d19ecfcc8..7ffb2463b 100644 --- a/src/lib/opaque-holder.hpp +++ b/src/lib/opaque-holder.hpp @@ -253,6 +253,8 @@ namespace lib { template 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); } diff --git a/src/lib/scoped-collection.hpp b/src/lib/scoped-collection.hpp new file mode 100644 index 000000000..8a2fb81e0 --- /dev/null +++ b/src/lib/scoped-collection.hpp @@ -0,0 +1,161 @@ +/* + SCOPED-COLLECTION.hpp - managing a fixed collection of noncopyable polymorphic objects + + Copyright (C) Lumiera.org + 2012, Hermann Vosseler + + 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 +//#include +#include + + +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 ScopedCollection + : boost::noncopyable + { +// typedef std::vector _Vec; +// typedef typename _Vec::iterator VIter; +// +// typedef RangeIter RIter; +// typedef PtrDerefIter 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 diff --git a/src/lib/test/testdummy.hpp b/src/lib/test/testdummy.hpp index 24d467c1f..27d4e79c7 100644 --- a/src/lib/test/testdummy.hpp +++ b/src/lib/test/testdummy.hpp @@ -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) diff --git a/tests/40components.tests b/tests/40components.tests index 92ead61e2..d22d69d07 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -617,7 +617,12 @@ out: ^\.throw some exceptions... END -TEST "ScopedPtrVect_test" ScopedPtrVect_test <timeOf (frameNr); } diff --git a/tests/lib/diagnostic-context-test.cpp b/tests/lib/diagnostic-context-test.cpp index 001c92cef..008c37af5 100644 --- a/tests/lib/diagnostic-context-test.cpp +++ b/tests/lib/diagnostic-context-test.cpp @@ -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 diff --git a/tests/lib/scoped-collection-test.cpp b/tests/lib/scoped-collection-test.cpp new file mode 100644 index 000000000..f712c239e --- /dev/null +++ b/tests/lib/scoped-collection-test.cpp @@ -0,0 +1,326 @@ +/* + ScopedCollection(Test) - holding and owning a fixed collection of noncopyable objects + + Copyright (C) Lumiera.org + 2012, Hermann Vosseler + + 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 + + +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 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(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 (r1); + CHECK (2 == coll.size()); + + int sum = Dummy::checksum(); + + // trigger the bomb + VERIFY_ERROR (SUBVERSIVE, coll.appendNew(r1,r1) ); + + CHECK ( 2 == coll.size()); // the other objects survived + CHECK (sum == Dummy::checksum()); + + Dummy& d2 = coll.appendNew (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 (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 + diff --git a/tests/lib/scoped-holder-test.cpp b/tests/lib/scoped-holder-test.cpp index 40b2637e7..3815d86a3 100644 --- a/tests/lib/scoped-holder-test.cpp +++ b/tests/lib/scoped-holder-test.cpp @@ -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)); diff --git a/tests/lib/scoped-holder-transfer-test.cpp b/tests/lib/scoped-holder-transfer-test.cpp index 36b1bdd2c..add399caa 100644 --- a/tests/lib/scoped-holder-transfer-test.cpp +++ b/tests/lib/scoped-holder-transfer-test.cpp @@ -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()); }