2011-04-20 19:11:08 +02:00
|
|
|
/*
|
|
|
|
|
PolymorphicValue(Test) - verify handling of opaque polymorphic values
|
|
|
|
|
|
|
|
|
|
Copyright (C) Lumiera.org
|
|
|
|
|
2011, 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-foreach.hpp"
|
|
|
|
|
|
2011-04-21 19:35:09 +02:00
|
|
|
#include "lib/polymorphic-value.hpp"
|
2011-04-20 19:11:08 +02:00
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
namespace test{
|
|
|
|
|
|
|
|
|
|
using ::Test;
|
|
|
|
|
using util::for_each;
|
2011-04-21 19:35:09 +02:00
|
|
|
using util::unConst;
|
2011-04-20 19:11:08 +02:00
|
|
|
using util::isSameObject;
|
|
|
|
|
using lumiera::error::LUMIERA_ERROR_ASSERTION;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace { // test dummy hierarchy
|
|
|
|
|
// Note: largely varying space requirements
|
|
|
|
|
// correct function depending on concrete class
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct Interface
|
|
|
|
|
{
|
|
|
|
|
virtual ~Interface() {}
|
|
|
|
|
virtual long apiFunc() =0;
|
|
|
|
|
|
|
|
|
|
virtual long& localSum() =0;
|
|
|
|
|
|
|
|
|
|
bool
|
2011-04-21 19:35:09 +02:00
|
|
|
operator== (Interface const& o) const
|
2011-04-20 19:11:08 +02:00
|
|
|
{
|
2011-04-21 19:35:09 +02:00
|
|
|
return unConst(this)->localSum()
|
|
|
|
|
== unConst(o).localSum();
|
2011-04-20 19:11:08 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const uint MAX_RAND = 1000;
|
2011-04-24 20:28:36 +02:00
|
|
|
const uint MAX_ELM = 111;
|
|
|
|
|
const uint MAX_SIZ = sizeof(long[MAX_ELM]);
|
2011-04-20 19:11:08 +02:00
|
|
|
|
2011-04-24 20:28:36 +02:00
|
|
|
/* Checksums to verify proper ctor-dtor calls and copy operations */
|
2011-04-20 19:11:08 +02:00
|
|
|
long _checkSum = 0;
|
|
|
|
|
long _callSum = 0;
|
|
|
|
|
uint _created = 0;
|
|
|
|
|
|
|
|
|
|
|
2011-04-24 20:28:36 +02:00
|
|
|
/**
|
|
|
|
|
* Template to generate concrete implementation classes.
|
|
|
|
|
* @note the generated classes vary largely in size, and
|
|
|
|
|
* moreover the actual place to store the checksum
|
|
|
|
|
* also depends on that size parameter.
|
|
|
|
|
*/
|
|
|
|
|
template<uint ii, class BASE=Interface>
|
|
|
|
|
struct Imp : BASE
|
2011-04-20 19:11:08 +02:00
|
|
|
{
|
|
|
|
|
long localData_[ii];
|
|
|
|
|
|
2011-04-22 03:29:12 +02:00
|
|
|
~Imp()
|
|
|
|
|
{
|
|
|
|
|
mark (-1 * localSum());
|
|
|
|
|
CHECK (0 == localSum());
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-20 19:11:08 +02:00
|
|
|
Imp()
|
|
|
|
|
{
|
|
|
|
|
REQUIRE (0 < ii);
|
|
|
|
|
localSum() = 0;
|
2011-04-22 03:29:12 +02:00
|
|
|
mark(ii);
|
2011-04-20 19:11:08 +02:00
|
|
|
++ _created;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-22 03:29:12 +02:00
|
|
|
Imp (Imp const& o)
|
2011-04-20 19:11:08 +02:00
|
|
|
{
|
2011-04-22 03:29:12 +02:00
|
|
|
++ _created;
|
|
|
|
|
copyData (o);
|
|
|
|
|
_checkSum += localSum();
|
|
|
|
|
}// adjust, because we've gotten two identical instances
|
2011-04-20 19:11:08 +02:00
|
|
|
|
2011-04-22 03:29:12 +02:00
|
|
|
Imp&
|
|
|
|
|
operator= (Imp const& o)
|
|
|
|
|
{
|
|
|
|
|
_checkSum -= localSum();
|
|
|
|
|
copyData (o);
|
|
|
|
|
_checkSum += localSum();
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
2011-04-20 19:11:08 +02:00
|
|
|
|
2011-04-22 03:29:12 +02:00
|
|
|
private:
|
|
|
|
|
virtual long
|
2011-04-20 19:11:08 +02:00
|
|
|
apiFunc()
|
|
|
|
|
{
|
2011-10-23 22:06:32 +02:00
|
|
|
long rr = ii * (1 + rand() % MAX_RAND);
|
2011-04-22 03:29:12 +02:00
|
|
|
mark (rr);
|
2011-04-20 19:11:08 +02:00
|
|
|
_callSum += rr;
|
|
|
|
|
return rr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long&
|
|
|
|
|
localSum()
|
|
|
|
|
{
|
2011-04-21 19:35:09 +02:00
|
|
|
return localData_[ii-1];
|
2011-04-20 19:11:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2011-04-22 03:29:12 +02:00
|
|
|
mark (long markerValue)
|
2011-04-20 19:11:08 +02:00
|
|
|
{
|
|
|
|
|
localSum() += markerValue;
|
|
|
|
|
_checkSum += markerValue;
|
|
|
|
|
}
|
2011-04-22 03:29:12 +02:00
|
|
|
|
|
|
|
|
void
|
|
|
|
|
copyData (Imp const& o)
|
|
|
|
|
{
|
|
|
|
|
for (uint i=0; i<ii; ++i)
|
|
|
|
|
localData_[i] = o.localData_[i];
|
|
|
|
|
}
|
2011-04-20 19:11:08 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** maximum additional storage maybe wasted
|
|
|
|
|
* due to alignment of the contained object
|
|
|
|
|
* within the embedded Holder/Buffer
|
|
|
|
|
*/
|
|
|
|
|
const size_t _ALIGN_ = sizeof(size_t);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef PolymorphicValue<Interface, MAX_SIZ> PolyVal;
|
2011-04-21 19:35:09 +02:00
|
|
|
typedef std::vector<PolyVal> TestList;
|
2011-04-20 19:11:08 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-04-24 20:28:36 +02:00
|
|
|
|
2012-12-02 23:03:37 +01:00
|
|
|
|
|
|
|
|
|
2011-04-20 19:11:08 +02:00
|
|
|
/**********************************************************************************
|
|
|
|
|
* @test build a bunch of PolymorphicValue objects. Handle them like copyable
|
|
|
|
|
* value objects, without knowing the exact implementation type; moreover
|
|
|
|
|
* execute implementation internals only disclosed to the concrete subtype.
|
|
|
|
|
* Verify correctness through checksums.
|
|
|
|
|
*/
|
|
|
|
|
class PolymorphicValue_test : public Test
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
|
run (Arg)
|
|
|
|
|
{
|
|
|
|
|
_checkSum = 0;
|
|
|
|
|
_callSum = 0;
|
|
|
|
|
_created = 0;
|
|
|
|
|
|
2011-04-24 20:28:36 +02:00
|
|
|
verifyBasics();
|
|
|
|
|
|
2011-04-20 19:11:08 +02:00
|
|
|
{
|
|
|
|
|
TestList objs = createOpaqueValues ();
|
|
|
|
|
for_each (objs, operate);
|
|
|
|
|
}
|
2011-04-21 19:35:09 +02:00
|
|
|
CHECK (0 == _checkSum); // all dead
|
2011-04-20 19:11:08 +02:00
|
|
|
|
|
|
|
|
verifyOverrunProtection();
|
2011-04-25 04:49:05 +02:00
|
|
|
verifyCopySupportDetectionMetafunctions();
|
2011-04-20 19:11:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TestList
|
|
|
|
|
createOpaqueValues ()
|
|
|
|
|
{
|
|
|
|
|
TestList list;
|
|
|
|
|
list.push_back (PolyVal::build<Imp<1> > () );
|
|
|
|
|
list.push_back (PolyVal::build<Imp<11> > () );
|
|
|
|
|
list.push_back (PolyVal::build<Imp<111> >() );
|
|
|
|
|
list.push_back (PolyVal::build<Imp<23> > () );
|
|
|
|
|
list.push_back (PolyVal::build<Imp<5> > () );
|
|
|
|
|
return list;
|
|
|
|
|
} //note: copy
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
operate (PolyVal& elm)
|
|
|
|
|
{
|
|
|
|
|
PolyVal myLocalVal(elm);
|
|
|
|
|
CHECK (elm == myLocalVal);
|
|
|
|
|
|
|
|
|
|
long prevSum = _callSum;
|
2011-04-24 20:28:36 +02:00
|
|
|
long randVal = myLocalVal->apiFunc();
|
2011-04-20 19:11:08 +02:00
|
|
|
CHECK (prevSum + randVal == _callSum);
|
|
|
|
|
CHECK (elm != myLocalVal);
|
|
|
|
|
|
|
|
|
|
elm = myLocalVal;
|
|
|
|
|
CHECK (elm == myLocalVal);
|
|
|
|
|
CHECK (!isSameObject (elm, myLocalVal));
|
|
|
|
|
|
2011-04-23 20:19:29 +02:00
|
|
|
CHECK (sizeof(myLocalVal) <= MAX_SIZ + polyvalue::Trait<Interface>::ADMIN_OVERHEAD + _ALIGN_);
|
2011-04-20 19:11:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-04-22 03:29:12 +02:00
|
|
|
void
|
|
|
|
|
verifyBasics()
|
|
|
|
|
{
|
2011-04-24 20:28:36 +02:00
|
|
|
typedef Imp<MAX_ELM> MaximumSizedImp;
|
|
|
|
|
|
|
|
|
|
// Standard case: no copy support by client objects
|
|
|
|
|
verifyCreation_and_Copy<PolyVal, MaximumSizedImp>();
|
|
|
|
|
|
|
|
|
|
// Special case: client objects expose extension point for copy support
|
|
|
|
|
typedef polyvalue::CopySupport<Interface> CopySupportAPI; // Copy support API declared as sub-interface
|
|
|
|
|
typedef Imp<MAX_ELM,CopySupportAPI> CopySupportingImp; // insert this sub-interface between public API and Implementation
|
|
|
|
|
typedef PolymorphicValue<Interface, MAX_SIZ, CopySupportAPI> OptimalPolyVal; // Make the Holder use this special attachment point
|
|
|
|
|
CHECK (sizeof(OptimalPolyVal) < sizeof(PolyVal)); // results in smaller Holder and less implementation overhead
|
|
|
|
|
|
|
|
|
|
verifyCreation_and_Copy<OptimalPolyVal, CopySupportingImp>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<class PV,class IMP>
|
|
|
|
|
void
|
|
|
|
|
verifyCreation_and_Copy()
|
|
|
|
|
{
|
|
|
|
|
typedef PV Holder;
|
|
|
|
|
typedef IMP ImpType;
|
|
|
|
|
typedef typename PV::Interface Api;
|
|
|
|
|
|
2011-04-22 03:29:12 +02:00
|
|
|
long prevSum = _checkSum;
|
|
|
|
|
uint prevCnt = _created;
|
|
|
|
|
|
2011-04-24 20:28:36 +02:00
|
|
|
Holder val = Holder::template build<ImpType>();
|
2011-04-22 03:29:12 +02:00
|
|
|
CHECK (prevSum+111 == _checkSum); // We got one primary ctor call
|
|
|
|
|
CHECK (prevCnt+1 <= _created); // Note: usually, the compiler optimises
|
|
|
|
|
CHECK (prevCnt+2 >= _created); // and skips the spurious copy-operation
|
2011-04-24 20:28:36 +02:00
|
|
|
CHECK (sizeof(Holder) >= sizeof(ImpType));
|
|
|
|
|
Api& embedded = val;
|
2011-04-22 03:29:12 +02:00
|
|
|
CHECK (isSameObject(embedded,val));
|
2011-04-24 20:28:36 +02:00
|
|
|
CHECK (INSTANCEOF(ImpType, &embedded));
|
|
|
|
|
|
|
|
|
|
prevCnt = _created;
|
|
|
|
|
Holder val2(val); // invoke copy ctor without knowing the implementation type
|
|
|
|
|
embedded.apiFunc();
|
|
|
|
|
CHECK (val != val2); // invoking the API function had an sideeffect on the state
|
|
|
|
|
val = val2; // assignment of copy back to the original...
|
|
|
|
|
CHECK (val == val2); // cancels the side effect
|
|
|
|
|
|
|
|
|
|
CHECK (prevCnt+1 == _created); // one new embedded instance was created by copy ctor
|
2011-04-22 03:29:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-04-20 19:11:08 +02:00
|
|
|
void
|
|
|
|
|
verifyOverrunProtection()
|
|
|
|
|
{
|
2011-04-24 20:28:36 +02:00
|
|
|
typedef Imp<MAX_ELM+1> OversizedImp;
|
|
|
|
|
CHECK (MAX_SIZ < sizeof(OversizedImp));
|
2011-04-20 19:11:08 +02:00
|
|
|
#if false ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT
|
2011-04-24 20:28:36 +02:00
|
|
|
VERIFY_ERROR (ASSERTION, PolyVal::build<OversizedImp>() );
|
2011-04-20 19:11:08 +02:00
|
|
|
#endif ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT
|
|
|
|
|
}
|
2011-04-25 04:49:05 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @Test internally, PolymorphicValue uses some metafunctions
|
|
|
|
|
* to pick a suitable code path, based on the presence of helper functions
|
|
|
|
|
* on the API of the embedded objects. Default is no support by these objects,
|
|
|
|
|
* which then requires to use a more expensive implementation. Sometimes it's
|
|
|
|
|
* desirable to support \em cloning only (copy ctor), but no assignment after
|
|
|
|
|
* the fact. In this special case, a support API with only a \cloneInto member
|
|
|
|
|
* can be implemented, causing the PolymorphicValue container to raise an
|
|
|
|
|
* exception in case the copy operator is invoked.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
verifyCopySupportDetectionMetafunctions()
|
|
|
|
|
{
|
|
|
|
|
typedef polyvalue::CopySupport<Interface> CopySupportAPI;
|
|
|
|
|
typedef polyvalue::CloneValueSupport<Interface> CloneOnlyAPI;
|
|
|
|
|
|
|
|
|
|
CHECK ( !polyvalue::exposes_CloneFunction<Interface>::value );
|
|
|
|
|
CHECK ( polyvalue::exposes_CloneFunction<CopySupportAPI>::value );
|
|
|
|
|
CHECK ( polyvalue::exposes_CloneFunction<CloneOnlyAPI>::value );
|
|
|
|
|
|
|
|
|
|
CHECK ( polyvalue::allow_Clone_but_no_Copy<CloneOnlyAPI>::value );
|
|
|
|
|
CHECK ( !polyvalue::allow_Clone_but_no_Copy<CopySupportAPI>::value );
|
|
|
|
|
}
|
2011-04-20 19:11:08 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LAUNCHER (PolymorphicValue_test, "unit common");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}} // namespace lib::test
|