38 using util::isLimited;
46 using LERR_(INDEX_BOUNDS);
62 std::array<int,i> ext_;
73 setVal (getVal() -
explore(ext_).resultSum());
79 return i+ii +
explore(ext_).resultSum();
89 operator= (
Num && oNum)
99 std::swap (static_cast<Dummy&> (num1)
100 ,static_cast<Dummy&> (num2));
101 std::swap (num1.ext_, num2.ext_);
121 } // (END) test types 129 /***************************************************************//** 130 * @test use lib::Several to establish small collections of elements, 131 * possibly with sub-classing and controlled allocation. 132 * - the container is populated through a separate builder 133 * - the number of elements is flexible during population 134 * - the actual container allows random-access via base interface 135 * @see several-builder.hpp 137 class SeveralBuilder_test : public Test 147 check_ErrorHandling(); 148 check_ElementStorage(); 149 check_CustomAllocator(); 159 auto elms = makeSeveral({1,1,2,3,5,8,13}).build(); 160 CHECK (elms.size() == 7); 161 CHECK (elms.back() == 13); 162 CHECK (elms[3] == 3); 163 CHECK (join (elms,"-") == "1-1-2-3-5-8-13"_expect); 179 // prepare to verify proper invocation of all constructors / destructors 180 Dummy::checksum() = 0; 182 { // Scenario-1 : Baseclass and arbitrary subclass elements 183 SeveralBuilder<Dummy> builder; 184 CHECK (isnil (builder)); 186 builder.emplace<Num<3>>() 188 CHECK (2 == builder.size()); // use information functions... 189 CHECK (3 == builder[1].getVal()); // to peek into contents assembled thus far... 190 VERIFY_ERROR (INDEX_BOUNDS, builder[2] ); // runtime bounds check on the builder (but not on the product!) 192 CHECK (4 == builder.size()); 193 builder.fillElm(3, 5); 194 CHECK (7 == builder.size()); 196 Several<Dummy> elms = builder.build(); 197 CHECK ( isnil(builder)); 198 CHECK (not isnil(elms)); 199 CHECK (7 == elms.size()); 200 CHECK (elms[0].getVal() == (3+1)*3); // indeed a Num<3> with default-seed ≡ 3 201 CHECK (elms[0].calc(1) == 3 + 1 + (3+3+3)); // indeed called the overridden calc() operation 202 CHECK (elms[1].getVal() == (2+1)*1); // indeed a Num<2> with seed ≡ 1 203 CHECK (elms[1].calc(1) == 2 + 1 + (1+1)); // indeed the overridden calc() picking from the Array(1,1) 204 CHECK (isLimited (1, elms[2].getVal(), 100'000
'000)); // indeed a Dummy with default random seed 205 CHECK (isLimited (1, elms[3].getVal(), 100'000
'000)); // and this one too, since we filled in two instances 206 CHECK (elms[4].getVal() == 5); // followed by tree instances Dummy(5) 207 CHECK (elms[5].getVal() == 5); 208 CHECK (elms[6].getVal() == 5); 209 CHECK (elms[6].calc(1) == 5+1); // indeed invoking the base implementation of calc() 212 { // Scenario-2 : unrelated element types 213 SeveralBuilder<uint32_t> builder; 215 auto urgh = array<char,5>{"Urgh"}; 216 auto phi = (1+sqrtf(5))/2; 218 builder.append (urgh, phi, -1); // can emplace arbitrary data 219 CHECK (3 == builder.size()); 221 Several<uint32_t> elms = builder.build(); // WARNING: data accessed by wild cast to interface type 222 CHECK (3 == elms.size()); 223 CHECK (elms[0] == * reinterpret_cast<const uint32_t*> ("Urgh")); 224 CHECK (elms[1] == * reinterpret_cast<uint32_t*> (&phi)); 225 CHECK (elms[2] == uint32_t(-1)); 228 { // Scenario-3 : copy values from iterator 229 SeveralBuilder<int> builder; 231 VecI seq = getTestSeq_int<VecI> (10); 232 builder.appendAll (seq); 233 CHECK (10 == builder.size()); 235 auto elms = builder.build(); 236 CHECK (10 == elms.size()); 237 CHECK (join (elms,"-") == "0-1-2-3-4-5-6-7-8-9"_expect); 240 CHECK (0 == Dummy::checksum()); 268 check_ErrorHandling() 270 CHECK (0 == Dummy::checksum()); 272 { // Scenario-1 : Baseclass and arbitrary subclass elements 273 SeveralBuilder<Dummy> builder; 275 // The first element will _prime_ the container for a suitable usage pattern 276 builder.emplace<Num<1>>(); 277 CHECK (1 == builder.size()); 279 // Notably the first element established the _spread_ between index positions, 280 // which effectively limits the size of objects to be added. Moreover, since 281 // the element type was detected to be non-trivial, we can not correct this 282 // element spacing by shifting existing allocations (`memmove()` not possible) 283 CHECK (sizeof(Num<1>) < sizeof(Num<5>)); 284 VERIFY_FAIL ("Unable to place element of type Num<5u> (size=" 285 , builder.emplace<Num<5>>() ); 286 CHECK (1 == builder.size()); 288 // Furthermore, the first element was detected to be a subclass, 289 // and the interface type `Dummy` has a virtual destructor; 290 // all added elements must comply to this scheme, once established 291 VERIFY_FAIL ("Unable to handle (trivial-)destructor for element type long, " 292 "since this container has been primed to use virtual-baseclass-destructors." 293 , builder.emplace<long>(55) ); 294 CHECK (1 == builder.size()); 296 // the initial allocation added some reserve buffer space (for 10 elements) 297 // and we can fill that space with arbitrary subclass instances 299 CHECK (6 == builder.size()); 301 // But the initial allocation can not be increased, since that would require 302 // a re-allocation of a larger buffer, followed by copying the elements; 303 // but since the established scheme allows for _arbitrary_ subclasses, 304 // the builder does not know the exact type for safe element relocation. 305 VERIFY_FAIL ("Several-container is unable to accommodate further element of type Dummy" 306 , builder.fillElm (20) ); 307 CHECK (10 == builder.size()); 309 // in spite of all the provoked failures, 310 // all element destructors were invoked 311 CHECK (0 == Dummy::checksum()); 314 { // Scenario-2 : Baseclass and elements of a single fixed subclass 315 SeveralBuilder<Dummy, Num<5>> builder; 318 CHECK (5 == builder.size()); 320 // trigger re-alloc by moving into larger memory block 322 CHECK (19 == builder.size()); 323 CHECK (builder.size() > INITIAL_ELM_CNT); 325 // with the elements added thus far, this instance has been primed to 326 // rely on a fixed well known element type for move-growth and to use 327 // the virtual base class destructor for clean-up. It is thus not possible 328 // to add another element that is not related to this baseclass... 329 VERIFY_FAIL ("Unable to handle (trivial-)destructor for element type ShortBlocker, " 330 "since this container has been primed to use virtual-baseclass-destructors." 331 , builder.emplace<ShortBlocker>() ); 332 CHECK (19 == builder.size()); 334 CHECK (sizeof(ShortBlocker) < sizeof(Num<5>)); // it was not rejected due to size... 336 // However, a subclass /different than the defined element type/ is acceptable, 337 // but only to the condition to lock any further container growth by move-reallocation. 338 // The rationale is that we can still destroy through the virtual base destructor, 339 // but we aren't able to move
elements safely any more, since we don
't capture the type. 340 builder.emplace<Num<1>>(); 341 CHECK (20 == builder.size()); 342 CHECK (20 == builder.capacity()); 343 CHECK ( 0 == builder.capReserve()); 345 // But here comes the catch: since we choose to accept arbitrary sub-types not identified in detail, 346 // the container has lost its ability of move-reallocation; with 20 elements the current reserve 347 // is exhausted and we are now unable to add any further elements beyond that point. 348 VERIFY_FAIL ("unable to move elements of mixed unknown detail type, which are not trivially movable" 349 , builder.fillElm(5); ); 351 // the container is still sound however 352 auto elms = builder.build(); 353 CHECK (20 == elms.size()); 354 // verify that member fields were not corrupted 355 for (uint i=0; i<=18; ++i) 356 CHECK (elms[i].calc(i) == 5 + i + (5+5+5+5+5)); 357 CHECK (elms.back().calc(0) == 1 + 0 + (1)); 359 CHECK (0 == Dummy::checksum()); 362 { // Scenario-3 : arbitrary elements of trivial type 363 SeveralBuilder<uint8_t> builder; 366 CHECK ( 0 == builder.size()); 367 CHECK (16 == builder.capacity()); 368 CHECK (16 == builder.capReserve()); 370 string BFR{"starship is"}; 371 builder.appendAll (BFR); 372 CHECK (11 == builder.size()); 373 CHECK (16 == builder.capacity()); 374 CHECK ( 5 == builder.capReserve()); 376 // append element that is much larger than a char 377 // => since elements are trivial, they can be moved to accommodate 378 builder.append (int64_t(32)); 379 CHECK (12 == builder.size()); 380 CHECK (16 == builder.capacity()); // note: capacity remained nominally the same 381 CHECK ( 4 == builder.capReserve()); // while in fact the spread and thus the buffer were increased 383 // emplace a completely unrelated object type, 384 // which is also trivially destructible, but non-copyable 385 builder.emplace<ShortBlocker> ('c
'); 387 // can emplace further trivial objects, since there is still capacity left 388 builder.append (int('o
'), long('o
')); 389 CHECK (15 == builder.size()); 390 CHECK ( 1 == builder.capReserve()); 392 VERIFY_FAIL ("Unable to place element of type Num<5u>" 393 , builder.append (Num<5>{}) ); 394 CHECK (sizeof(Num<5>) > sizeof(int64_t)); 395 // not surprising: this one was too large, 396 // and due to the non-copyable element we can not adapt anymore 404 // adding data of a non-trivial type is rejected, 405 // since the container does not capture individual element types 406 // and thus does not know how to delete it 407 CHECK (sizeof(NonTrivial) <= sizeof(int64_t)); 408 VERIFY_FAIL ("Unsupported kind of destructor for element type SeveralBuilder_test::check_ErrorHandling()::NonTrivial" 409 , builder.append (NonTrivial{}) ); 410 CHECK ( 1 == builder.capReserve()); 412 // space for a single one left... 413 builder.append ('l
'); 414 CHECK (16 == builder.size()); 415 CHECK ( 0 == builder.capReserve()); 417 // and now we've run out of space, and due to the non-copyable
object, move-relocation is rejected
418 VERIFY_FAIL (
"Several-container is unable to accommodate further element of type char; " 419 "storage reserve (128 bytes ≙ 16 elms) exhausted and unable to move " 420 "elements of mixed unknown detail type, which are not trivially movable." 421 , builder.append (
'!') );
424 auto elms = builder.build();
425 CHECK (16 == elms.size());
426 CHECK (join(elms,
"·") ==
"s·t·a·r·s·h·i·p· ·i·s· ·c·o·o·l"_expect);
428 CHECK (0 == Dummy::checksum());
434 CHECK (1 == builder.size());
436 Dummy::activateCtorFailure(
true);
439 NOTREACHED (
"did not throw");
447 Dummy::checksum() -= 23;
449 CHECK (1 == builder.size());
450 Dummy::activateCtorFailure(
false);
453 auto elms = builder.
build();
454 CHECK (2 == elms.size());
455 CHECK (elms.front().calc(1) == 3 + 1 + (42+42+42));
456 CHECK (elms.back().calc(5) == 3 + 5 + (23+23+23));
459 CHECK (0 == Dummy::checksum());
478 auto loc = [](
auto& something){
return util::addrID (something); };
479 auto calcSpread = [&](
auto& several){
return loc(several[1]) - loc(several[0]); };
483 CHECK (21 == elms[0]);
484 CHECK (34 == elms[1]);
485 CHECK (55 == elms[2]);
486 CHECK (3 == elms.size());
487 CHECK (
sizeof(elms) ==
sizeof(
void*));
489 CHECK (
sizeof(
int) ==
alignof(
int));
490 size_t spread = calcSpread (elms);
491 CHECK (spread ==
sizeof(
int));
492 CHECK (loc(elms.back()) == loc(elms.front()) + 2*spread);
502 auto elms = makeSeveral<Ali>().fillElm(5).build();
503 CHECK (5 == elms.size());
504 CHECK (
sizeof(elms) ==
sizeof(
void*));
506 size_t spread = calcSpread (elms);
507 CHECK (spread ==
alignof(Ali));
508 CHECK (loc(elms.front()) %
alignof(Ali) == 0);
509 CHECK (loc(elms.back()) == loc(elms.front()) + 4*spread);
513 auto elms = makeSeveral<ShortBlocker>().fillElm(5).build();
515 auto v0 = elms[0].val;
auto p0 = loc(elms[0]);
516 auto v1 = elms[1].val;
auto p1 = loc(elms[1]);
517 auto v2 = elms[2].val;
auto p2 = loc(elms[2]);
518 auto v3 = elms[3].val;
auto p3 = loc(elms[3]);
519 auto v4 = elms[4].val;
auto p4 = loc(elms[4]);
521 CHECK (5 == elms.size());
522 auto moved = move(elms);
523 CHECK (5 == moved.size());
524 CHECK (loc(elms) != loc(moved));
525 CHECK (isnil (elms));
527 CHECK (loc(moved[0]) == p0);
528 CHECK (loc(moved[1]) == p1);
529 CHECK (loc(moved[2]) == p2);
530 CHECK (loc(moved[3]) == p3);
531 CHECK (loc(moved[4]) == p4);
533 CHECK (moved[0].val == v0);
534 CHECK (moved[1].val == v1);
535 CHECK (moved[2].val == v2);
536 CHECK (moved[3].val == v3);
537 CHECK (moved[4].val == v4);
539 CHECK (calcSpread(moved) ==
sizeof(ShortBlocker));
554 CHECK (0 == Dummy::checksum());
558 size_t expectedAlloc;
562 auto builder = makeSeveral<Dummy>()
563 .withAllocator<test::TrackAlloc>()
566 size_t elmSiz =
sizeof(
Dummy);
567 size_t buffSiz = elmSiz * builder.capacity();
568 size_t headerSiz =
sizeof(ArrayBucket<Dummy>);
569 expectedAlloc = headerSiz + buffSiz;
576 elms = builder.
build();
578 CHECK (elms.size() == 55);
583 auto others = move(elms);
584 CHECK (elms.size() == 0);
585 CHECK (others.size() == 55);
591 CHECK (others.size() == 0);
593 CHECK (0 == Dummy::checksum());
604 CHECK (allotted == 0);
606 auto builder = makeSeveral<Dummy>()
611 size_t buffSiz =
sizeof(
Dummy) * builder.capacity();
612 size_t headerSiz =
sizeof(ArrayBucket<Dummy>);
613 expectedAlloc = headerSiz + buffSiz;
614 CHECK (4 == builder.size());
615 CHECK (4 == builder.capacity());
616 CHECK (1 == clu.numExtents());
617 CHECK (expectedAlloc == clu.
numBytes());
620 CHECK (8 == builder.capacity());
622 buffSiz =
sizeof(
Dummy) * builder.capacity();
623 expectedAlloc = headerSiz + buffSiz;
624 CHECK (1 == clu.numExtents());
625 CHECK (expectedAlloc == clu.
numBytes());
629 CHECK (1 == clu.numExtents());
633 CHECK (5 == builder.size());
634 CHECK (9 <= builder.capacity());
635 CHECK (2 == clu.numExtents());
636 buffSiz =
sizeof(
Dummy) * builder.capacity();
637 expectedAlloc = headerSiz + buffSiz;
645 CHECK (5 == builder.size());
646 CHECK (5 == builder.capacity());
650 elms = builder.
build();
651 CHECK (5 == elms.size());
652 CHECK (23 == elms.back().getVal());
653 CHECK (55 == extraDummy.getVal());
655 CHECK (5 == elms.size());
656 CHECK (23 == elms.back().getVal());
657 CHECK (2 == clu.numExtents());
660 CHECK (Dummy::checksum() > 0);
662 CHECK (Dummy::checksum() == 55);
663 CHECK (2 == clu.numExtents());
666 CHECK (Dummy::checksum() == 0);
IterQueue< T > elements(T const &elm)
convenience free function to build an iterable sequence
SeveralBuilder && reserve(size_t cntElm=1, size_t elmSiz=reqSiz< TY >())
ensure up-front that a desired capacity is allocated
Several< I > build()
Terminal Builder: complete and lock the collection contents.
static HashVal checksum(Literal pool=GLOBAL)
get Checksum for specific mem-pool
auto explore(IT &&srcSeq)
start building a IterExplorer by suitably wrapping the given iterable source.
SeveralBuilder && emplace(ARGS &&...args)
create a new content element within the managed storage
void check_CustomAllocator()
Memory management for the low-level model (render nodes network).
A non-copyable struct with 16bit alignment.
Any copy and copy construction prohibited.
int rani(uint bound=_iBOUND())
static size_t numBytes(Literal pool=GLOBAL)
calculate currently allotted Bytes for mem-pool
Implementation namespace for support and library code.
SeveralBuilder< I, E > makeSeveral()
Entrance Point: start building a lib::Several instance.
friend void swap(Num &num1, Num &num2)
Abstraction: Fixed array of elements.
SeveralBuilder && fillElm(size_t cntNew, ARGS &&...args)
emplace a number of elements of the defined element type E
Simplistic test class runner.
some bits of unit test helper code to fabricate collections with test data
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Builder to create and populate a lib::Several<I>.
unittest helper code: test dummy objects to track instances.
Unittest helper code: a custom allocator to track memory usage.
Instance tracking sub-dummy.
A collection of frequently used helper functions to support unit testing.
A Dummy object for tests.
Num(Num &&oNum) noexcept
allow for move construction
A pile of objects sharing common allocation and lifecycle.
static size_t numAlloc(Literal pool=GLOBAL)
get active allocation count for mem-pool
static size_t use_count(Literal pool=GLOBAL)
determine number of active front-end handles
SeveralBuilder && shrinkFit()
discard excess reserve capacity.
Building tree expanding and backtracking evaluations within hierarchical scopes.
void check_ElementStorage()
long calc(int ii) override
a dummy API operation
SeveralBuilder && append(VAL &&val, VALS &&...vals)
append copies of one or several arbitrary elements
static size_t constexpr max_size()
Maximum individual allocation size that can be handled.
Builder to create and populate instances of the lib::Several container.
#define VERIFY_FAIL(FAILURE_MSG, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises a std::exception, which additionally contains some FAI...