Lumiera  0.pre.03
»edit your freedom«
several-builder-test.cpp
Go to the documentation of this file.
1 /*
2  SeveralBuilder(Test) - building a limited fixed collection of elements
3 
4  Copyright (C)
5  2008, Hermann Vosseler <Ichthyostega@web.de>
6  2024, Hermann Vosseler <Ichthyostega@web.de>
7 
8   **Lumiera** is free software; you can redistribute it and/or modify it
9   under the terms of the GNU General Public License as published by the
10   Free Software Foundation; either version 2 of the License, or (at your
11   option) any later version. See the file COPYING for further details.
12 
13 * *****************************************************************/
14 
20 #include "lib/test/run.hpp"
23 #include "lib/test/test-coll.hpp"
24 #include "lib/test/test-helper.hpp"
26 #include "lib/iter-explorer.hpp"
27 #include "lib/format-util.hpp"
28 #include "lib/util.hpp"
29 
30 #include "lib/several-builder.hpp"
31 
32 #include <array>
33 
34 using ::test::Test;
35 using std::array;
36 
37 using lib::explore;
38 using util::isLimited;
39 using util::isnil;
40 using util::join;
41 
42 
43 namespace lib {
44 namespace test{
45 
46  using LERR_(INDEX_BOUNDS);
47 
48  namespace { // invocation tracking diagnostic subclass...
49 
58  template<uint i>
59  class Num
60  : public test::Dummy
61  {
62  std::array<int,i> ext_;
63 
64  public:
65  Num (uint seed=i)
66  : Dummy(seed)
67  {
68  ext_.fill(seed);
69  setVal ((i+1)*seed);
70  }
71  ~Num()
72  {
73  setVal (getVal() - explore(ext_).resultSum());
74  }
75 
76  long
77  calc (int ii) override
78  {
79  return i+ii + explore(ext_).resultSum();
80  }
81 
83  Num (Num && oNum) noexcept
84  : Num(0)
85  {
86  swap (*this, oNum);
87  }
88  Num&
89  operator= (Num && oNum)
90  {
91  if (&oNum != this)
92  swap (*this, oNum);
93  return *this;
94  }
95 
96  friend void
97  swap (Num& num1, Num& num2)
98  {
99  std::swap (static_cast<Dummy&> (num1)
100  ,static_cast<Dummy&> (num2));
101  std::swap (num1.ext_, num2.ext_);
102  }
103  };
104 
105 
113  {
114  int16_t val;
115 
116  ShortBlocker (short r = 1 + rani(1'000))
117  : val(r)
118  { };
119  };
120 
121  } // (END) test types
122 
123 
124 
125 
126 
127 
128 
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
136  */
137  class SeveralBuilder_test : public Test
138  {
139 
140  virtual void
141  run (Arg)
142  {
143  seedRand();
144 
145  simpleUsage();
146  check_Builder();
147  check_ErrorHandling();
148  check_ElementStorage();
149  check_CustomAllocator();
150  }
151 
152 
153 
156  void
157  simpleUsage()
158  {
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);
164  }
165 
166 
176  void
177  check_Builder()
178  {
179  // prepare to verify proper invocation of all constructors / destructors
180  Dummy::checksum() = 0;
181 
182  { // Scenario-1 : Baseclass and arbitrary subclass elements
183  SeveralBuilder<Dummy> builder;
184  CHECK (isnil (builder));
185 
186  builder.emplace<Num<3>>()
187  .emplace<Num<2>>(1);
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!)
191  builder.fillElm(2);
192  CHECK (4 == builder.size());
193  builder.fillElm(3, 5);
194  CHECK (7 == builder.size());
195 
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()
210  }
211 
212  { // Scenario-2 : unrelated element types
213  SeveralBuilder<uint32_t> builder;
214 
215  auto urgh = array<char,5>{"Urgh"};
216  auto phi = (1+sqrtf(5))/2;
217 
218  builder.append (urgh, phi, -1); // can emplace arbitrary data
219  CHECK (3 == builder.size());
220 
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));
226  }
227 
228  { // Scenario-3 : copy values from iterator
229  SeveralBuilder<int> builder;
230 
231  VecI seq = getTestSeq_int<VecI> (10);
232  builder.appendAll (seq);
233  CHECK (10 == builder.size());
234 
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);
238  }
239 
240  CHECK (0 == Dummy::checksum());
241  }
242 
243 
267  void
268  check_ErrorHandling()
269  {
270  CHECK (0 == Dummy::checksum());
271 
272  { // Scenario-1 : Baseclass and arbitrary subclass elements
273  SeveralBuilder<Dummy> builder;
274 
275  // The first element will _prime_ the container for a suitable usage pattern
276  builder.emplace<Num<1>>();
277  CHECK (1 == builder.size());
278 
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());
287 
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());
295 
296  // the initial allocation added some reserve buffer space (for 10 elements)
297  // and we can fill that space with arbitrary subclass instances
298  builder.fillElm (5);
299  CHECK (6 == builder.size());
300 
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());
308  }
309  // in spite of all the provoked failures,
310  // all element destructors were invoked
311  CHECK (0 == Dummy::checksum());
312 
313 
314  { // Scenario-2 : Baseclass and elements of a single fixed subclass
315  SeveralBuilder<Dummy, Num<5>> builder;
316 
317  builder.fillElm(5);
318  CHECK (5 == builder.size());
319 
320  // trigger re-alloc by moving into larger memory block
321  builder.fillElm(14);
322  CHECK (19 == builder.size());
323  CHECK (builder.size() > INITIAL_ELM_CNT);
324 
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());
333 
334  CHECK (sizeof(ShortBlocker) < sizeof(Num<5>)); // it was not rejected due to size...
335 
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());
344 
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); );
350 
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));
358  }
359  CHECK (0 == Dummy::checksum());
360 
361 
362  { // Scenario-3 : arbitrary elements of trivial type
363  SeveralBuilder<uint8_t> builder;
364 
365  builder.reserve(16);
366  CHECK ( 0 == builder.size());
367  CHECK (16 == builder.capacity());
368  CHECK (16 == builder.capReserve());
369 
370  string BFR{"starship is"};
371  builder.appendAll (BFR);
372  CHECK (11 == builder.size());
373  CHECK (16 == builder.capacity());
374  CHECK ( 5 == builder.capReserve());
375 
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
382 
383  // emplace a completely unrelated object type,
384  // which is also trivially destructible, but non-copyable
385  builder.emplace<ShortBlocker> ('c');
386 
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());
391 
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
397 
398  class NonTrivial
399  {
400  public:
401  ~NonTrivial() { }
402  };
403 
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());
411 
412  // space for a single one left...
413  builder.append ('l');
414  CHECK (16 == builder.size());
415  CHECK ( 0 == builder.capReserve());
416 
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 ('!') );
422 
423  // yet the container is still fine....
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);
427  }
428  CHECK (0 == Dummy::checksum());
429 
430  { // Scenario-4 : exception from element constructor
431  SeveralBuilder<Dummy> builder;
432 
433  builder.emplace<Num<3>>(42);
434  CHECK (1 == builder.size());
435 
436  Dummy::activateCtorFailure(true);
437  try {
438  builder.emplace<Num<3>>(23);
439  NOTREACHED ("did not throw");
440  }
441  catch(...)
442  {
443  // Exception emanated from Dummy(baseclass) ctor;
444  // at that point, the local val was set to the seed (≙23).
445  // When a constructor fails in C++, the destructor is not called,
446  // thus we have to compensate here to balance the checksum
447  Dummy::checksum() -= 23;
448  }
449  CHECK (1 == builder.size());
450  Dummy::activateCtorFailure(false);
451  builder.emplace<Num<3>>(23);
452 
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));
457  }
458  // all other destructors properly invoked...
459  CHECK (0 == Dummy::checksum());
460  }
461 
462 
463 
475  void
477  {
478  auto loc = [](auto& something){ return util::addrID (something); };
479  auto calcSpread = [&](auto& several){ return loc(several[1]) - loc(several[0]); };
480 
481  { // Scenario-1 : tightly packed values
482  Several<int> elms = makeSeveral({21,34,55}).build();
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*));
488 
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);
493  }
494 
495  { // Scenario-2 : alignment
496  struct Ali
497  {
498  alignas(64)
499  char charm = 'u';
500  };
501 
502  auto elms = makeSeveral<Ali>().fillElm(5).build();
503  CHECK (5 == elms.size());
504  CHECK (sizeof(elms) == sizeof(void*));
505 
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);
510  }
511 
512  { // Scenario-3 : noncopyable objects
513  auto elms = makeSeveral<ShortBlocker>().fillElm(5).build();
514 
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]);
520 
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));
526 
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);
532 
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);
538 
539  CHECK (calcSpread(moved) == sizeof(ShortBlocker));
540  }
541  }
542 
543 
544 
550  void
552  {
553  // Setup-1: use the TrackingAllocator
554  CHECK (0 == Dummy::checksum());
555  CHECK (0 == TrackingAllocator::checksum());
556 
557  Several<Dummy> elms;
558  size_t expectedAlloc;
559  CHECK (0 == TrackingAllocator::numAlloc());
560  CHECK (0 == TrackingAllocator::use_count());
561  {
562  auto builder = makeSeveral<Dummy>()
563  .withAllocator<test::TrackAlloc>()
564  .fillElm(55);
565 
566  size_t elmSiz = sizeof(Dummy);
567  size_t buffSiz = elmSiz * builder.capacity();
568  size_t headerSiz = sizeof(ArrayBucket<Dummy>);
569  expectedAlloc = headerSiz + buffSiz;
570 
571  CHECK (TrackingAllocator::numBytes() == expectedAlloc);
572  CHECK (TrackingAllocator::numAlloc() == 1);
573  CHECK (TrackingAllocator::use_count()== 2); // one instance in the builder, one in the deleter
574  CHECK (TrackingAllocator::checksum() > 0);
575 
576  elms = builder.build();
577  }
578  CHECK (elms.size() == 55);
579  CHECK (TrackingAllocator::numBytes() == expectedAlloc);
580  CHECK (TrackingAllocator::numAlloc() == 1);
581  CHECK (TrackingAllocator::use_count()== 1); // only one allocator instance in the deleter left
582 
583  auto others = move(elms);
584  CHECK (elms.size() == 0);
585  CHECK (others.size() == 55);
586  CHECK (TrackingAllocator::numBytes() == expectedAlloc);
587  CHECK (TrackingAllocator::numAlloc() == 1);
588  CHECK (TrackingAllocator::use_count()== 1);
589 
590  others = move(Several<Dummy>{}); // automatically triggers de-allocation
591  CHECK (others.size() == 0);
592 
593  CHECK (0 == Dummy::checksum());
594  CHECK (0 == TrackingAllocator::numBytes());
595  CHECK (0 == TrackingAllocator::numAlloc());
596  CHECK (0 == TrackingAllocator::use_count());
597  CHECK (0 == TrackingAllocator::checksum());
598 
599 
600 
601  {// Setup-2: use an AllocationCLuster instance
602  AllocationCluster clu;
603  size_t allotted = clu.numBytes();
604  CHECK (allotted == 0);
605  {
606  auto builder = makeSeveral<Dummy>()
607  .withAllocator(clu)
608  .reserve(4) // use a limited pre-reservation
609  .fillElm(4); // fill all the allocated space with 4 new elements
610 
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()); // AllocationCluster has only opened one extent thus far
617  CHECK (expectedAlloc == clu.numBytes()); // and the allocated space matches the demand precisely
618 
619  builder.append (Dummy{23}); // now request to add just one further element
620  CHECK (8 == builder.capacity()); // ...which causes the builder to double up the reserve capacity
621 
622  buffSiz = sizeof(Dummy) * builder.capacity();
623  expectedAlloc = headerSiz + buffSiz;
624  CHECK (1 == clu.numExtents()); // However, AllocationCluster was able to adjust allocation in-place
625  CHECK (expectedAlloc == clu.numBytes()); // and thus the new increased buffer is still placed in the first extent
626 
627  // perform another unrelated allocation
628  Dummy& extraDummy = clu.create<Dummy>(55);
629  CHECK (1 == clu.numExtents());
630  CHECK (clu.numBytes() > expectedAlloc+sizeof(Dummy)); // but now we've used some further space behind that point
631 
632  builder.reserve(9); // ...which means that the AllocationCluster can no longer adjust dynamically
633  CHECK (5 == builder.size()); // .....because this is only possible on the latest allocation opened
634  CHECK (9 <= builder.capacity()); // And while we still got the increased capacity as desired,
635  CHECK (2 == clu.numExtents()); // this was only possible by wasting space and copying into a new extent
636  buffSiz = sizeof(Dummy) * builder.capacity();
637  expectedAlloc = headerSiz + buffSiz;
638  CHECK (expectedAlloc <= AllocationCluster::max_size());
639  CHECK (clu.numBytes() == AllocationCluster::max_size()
640  + expectedAlloc);
641 
642  allotted = clu.numBytes();
643  // request to throw away excess reserve
644  builder.shrinkFit();
645  CHECK (5 == builder.size());
646  CHECK (5 == builder.capacity());
647  CHECK (allotted > clu.numBytes()); // dynamic adjustment was possible, since it is the latest allocation
648  allotted = clu.numBytes();
649 
650  elms = builder.build(); // Note: assigning to the existing front-end (which is storage agnostic)
651  CHECK (5 == elms.size());
652  CHECK (23 == elms.back().getVal());
653  CHECK (55 == extraDummy.getVal());
654  } // Now the Builder and the ExtraDummy is gone...
655  CHECK (5 == elms.size()); // while all created elements are still there, sitting in the Allocationcluster
656  CHECK (23 == elms.back().getVal());
657  CHECK (2 == clu.numExtents());
658  CHECK (clu.numBytes() == allotted);
659 
660  CHECK (Dummy::checksum() > 0);
661  elms = move(Several<Dummy>{});
662  CHECK (Dummy::checksum() == 55); // all elements within Several were cleaned-up...
663  CHECK (2 == clu.numExtents()); // but the base allocation itself lives as long as the AllocationCluster
664  CHECK (clu.numBytes() == allotted);
665  }// AllocationCluster goes out of scope...
666  CHECK (Dummy::checksum() == 0); // now the (already unreachable) extraDummy was cleaned up
667  } // WARNING: contents in Several would now be dangling (if we haden't killed them)
668  };
669 
670 
671 
673  LAUNCHER (SeveralBuilder_test, "unit common");
674 
675 
676 
677 }} // namespace lib::test
IterQueue< T > elements(T const &elm)
convenience free function to build an iterable sequence
Definition: iter-stack.hpp:292
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
Definition: run.hpp:40
Memory management for the low-level model (render nodes network).
Any copy and copy construction prohibited.
Definition: nocopy.hpp:37
int rani(uint bound=_iBOUND())
Definition: random.hpp:135
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.
Abstraction: Fixed array of elements.
Definition: several.hpp:156
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.
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
Collection of small helpers and convenience shortcuts for diagnostics & formatting.
SeveralBuilder && shrinkFit()
discard excess reserve capacity.
Building tree expanding and backtracking evaluations within hierarchical scopes.
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...