Lumiera  0.pre.03
»edit your freedom«
virtual-copy-support-test.cpp
Go to the documentation of this file.
1 /*
2  VirtualCopySupport(Test) - copy and clone type-erased objects
3 
4  Copyright (C)
5  2015, Hermann Vosseler <Ichthyostega@web.de>
6 
7   **Lumiera** is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2 of the License, or (at your
10   option) any later version. See the file COPYING for further details.
11 
12 * *****************************************************************/
13 
20 #include "lib/test/run.hpp"
21 #include "lib/format-string.hpp"
22 #include "lib/test/test-helper.hpp"
24 #include "lib/format-string.hpp"
25 #include "lib/format-cout.hpp"
26 #include "lib/util.hpp"
27 
28 #include <string>
29 #include <type_traits>
30 
31 using util::_Fmt;
32 using util::isnil;
33 using std::string;
34 
35 using LERR_(LOGIC);
36 using LERR_(WRONG_TYPE);
37 
38 
39 namespace lib {
40 namespace meta {
41 namespace test {
42 
43  namespace { // Test fixture...
44 
45  int _CheckSum_ = 0;
46 
47  class Interface;
48 
49 
59  {
60  public:
61  virtual void copyInto (void* ) const { /* NOP */ };
62  virtual void moveInto (void* ) { /* NOP */ };
63  virtual void copyInto (Interface&) const { /* NOP */ };
64  virtual void moveInto (Interface&) { /* NOP */ };
65  };
66 
67 
71  class Interface
72  : public CopyInterface
73  {
74  public:
75  virtual ~Interface() { }
76  virtual operator string() const =0;
77  virtual bool empty() const =0;
78  };
79 
83  template<uint i>
84  class Sub
85  : public Interface
86  {
87  static_assert (0 < i, "invalid construction");
88 
89  char storage_[i];
90 
91  char&
92  access()
93  {
94  return storage_[i-1];
95  }
96  char const&
97  access() const
98  {
99  return storage_[i-1];
100  }
101 
102  public: /* == Implementation of the Interface == */
103 
104  virtual operator string() const override
105  {
106  return _Fmt("Sub|%s|%d|-%s")
107  % util::typeStr(this)
108  % i
109  % access();
110  }
111 
112  virtual bool
113  empty() const
114  {
115  return not access();
116  }
117 
118 
119  public: /* == full set of copy and assignment operations == */
120 
121  ~Sub()
122  {
123  _CheckSum_ -= access();
124  }
125  Sub()
126  {
127  access() = 'A' + rani(23);
128  _CheckSum_ += access();
129  }
130  Sub (Sub const& osub)
131  {
132  access() = osub.access();
133  _CheckSum_ += access();
134  }
135  Sub (Sub&& rsub)
136  {
137  access() = 0;
138  std::swap(access(),rsub.access());
139  }
140  Sub&
141  operator= (Sub const& osub)
142  {
143  if (!util::isSameObject (*this, osub))
144  {
145  _CheckSum_ -= access();
146  access() = osub.access();
147  _CheckSum_ += access();
148  }
149  return *this;
150  }
151  Sub&
152  operator= (Sub&& rsub)
153  {
154  _CheckSum_ -= access();
155  access() = 0;
156  std::swap(access(),rsub.access());
157  return *this;
158  }
159  };
160 
161 
162  /* == create various flavours of copyable / noncopyable objects == */
163 
164  template<char c>
165  class Regular
166  : public Sub<c>
167  { };
168 
169  template<char c>
171  : public Sub<c>
172  {
173  void operator= (UnAssignable const&); // private and unimplemented
174  public:
175  UnAssignable() = default;
176  UnAssignable(UnAssignable&&) = default;
177  UnAssignable(UnAssignable const&) = default;
178  };
179 
180  template<char c>
182  : public UnAssignable<c>
183  {
184  public:
185  OnlyMovable (OnlyMovable const&) = delete;
186  OnlyMovable() = default;
187  OnlyMovable(OnlyMovable&&) = default;
188  };
189 
190  template<char c>
192  : public OnlyMovable<c>
193  {
194  public:
195  Noncopyable (Noncopyable&&) = delete;
196  Noncopyable () = default;
197  };
198 
199 
200 
201  /* == concrete implementation subclass with virtual copy support == */
202 
203  template<class IMP>
204  class Opaque //-----Interface| CRTP-Impl | direct Baseclass
205  : public CopySupport<IMP>::template Policy<Interface, Opaque<IMP>, IMP>
206  {
207  public:
208  static Opaque&
209  downcast (Interface& bas)
210  {
211  Opaque* impl = dynamic_cast<Opaque*> (&bas);
212 
213  if (!impl)
214  throw error::Logic{"virtual copy works only on instances "
215  "of the same concrete implementation class"
216  , LERR_(WRONG_TYPE)};
217  else
218  return *impl;
219  }
220  };
221 
222  // == Test subject(s)==========================
223  using RegularImpl = Opaque<Regular<'a'>>;
224  using ClonableImpl = Opaque<UnAssignable<'b'>>;
225  using MovableImpl = Opaque<OnlyMovable<'c'>>;
226  using ImobileImpl = Opaque<Noncopyable<'d'>>;
227 
228  }//(End)Test fixture
229 
230 
231 
232 
233 
234 
235 
236  /**********************************************************************************************/
242  class VirtualCopySupport_test : public Test
243  {
244  virtual void
245  run (Arg)
246  {
247  seedRand();
248  CHECK(0 == _CheckSum_);
249 
250  verify_TestFixture();
251 
252  CHECK(0 == _CheckSum_);
253 
254  verify_fullVirtualCopySupport();
255  verify_noAssignementSupport();
256  verify_onlyMovableSupport();
257  verify_disabledCopySupport();
258 
259  CHECK(0 == _CheckSum_);
260  }
261 
262 
275  void
277  {
278  /* == full copy, move and assignment == */
279  Regular<'A'> a;
280  Regular<'A'> aa(a);
281  Regular<'A'> a1;
282 
283  cout << a <<endl
284  << aa <<endl
285  << a1 <<endl;
286 
287  a1 = a;
288 
289  CHECK (string(a) == string(aa));
290  CHECK (string(a) == string(a1));
291  CHECK (!isnil(a1));
292 
293  a = std::move(a1);
294 
295  CHECK (isnil(a1));
296  CHECK (string(a) == string(aa));
297 
298 
299  /* == interface vs. concrete class == */
300  Regular<'B'> b;
301  Interface& ii = b;
302 
303  string prevID(b);
304  ii = a; // Copy operation of Base-Interface is NOP
305  CHECK (string(b) == prevID);
306 
307 
308  /* == assignment inhibited == */
309  UnAssignable<'C'> c;
310  UnAssignable<'C'> cc(c);
311 
312  CHECK (string(c) == string(cc));
313 
314  prevID = cc;
315  UnAssignable<'C'> ccc(std::move(cc));
316 
317  cout << cc <<endl
318  << ccc <<endl;
319 
320  CHECK (string(ccc) == prevID);
321  CHECK (string(cc) != prevID);
322  CHECK (!isnil(ccc));
323  CHECK (isnil (cc)); // killed by moving away
324 
325  // c = cc; // does not compile
326 
327 
328  /* == only move construction allowed == */
329  OnlyMovable<'D'> d;
330  OnlyMovable<'D'> dd (std::move(d));
331 
332  cout << d <<endl
333  << dd <<endl;
334 
335  CHECK (string(dd) != string(d));
336  CHECK (!isnil(dd));
337  CHECK (isnil(d));
338 
339  // OnlyMovable<'D'> d1 (dd); // does not compile
340  // d = dd; // does not compile;
341 
342 
343  /* == all copy/assignment inhibited == */
344  Noncopyable<'E'> e;
345  // Noncopyable<'E'> ee (e); // does not compile
346  // Noncopyable<'E'> eee (std::move (e)); // does not compile
347  // e = Noncopyable<'E'>(); // does not compile
348 
349  CHECK (!isnil (e));
350  }
351 
352 
353 
354 
355 
356 
357  void
358  verify_fullVirtualCopySupport()
359  {
360  RegularImpl a,aa,aaa;
361  Interface& i(a);
362  Interface& ii(aa);
363  Interface& iii(aaa);
364 
365  char storage[sizeof(RegularImpl)];
366  Interface& iiii (*reinterpret_cast<Interface*> (&storage));
367 
368  string prevID = a;
369  CHECK (!isnil (a));
370 
371  i.moveInto(&storage);
372  CHECK (string(iiii) == prevID);
373  CHECK (!isnil(iiii));
374  CHECK ( isnil(i));
375 
376  ii.copyInto(i);
377  CHECK (!isnil(i));
378  CHECK (!isnil(ii));
379  CHECK (string(i) == string(ii));
380 
381  prevID = iii;
382  iii.moveInto(ii);
383  CHECK (!isnil(ii));
384  CHECK ( isnil(iii));
385  CHECK (string(ii) == prevID);
386 
387  // Verify that type mismatch in assignment is detected...
388  Opaque<Regular<'!'>> divergent;
389  Interface& evil(divergent);
390  VERIFY_ERROR (WRONG_TYPE, evil.copyInto(i));
391  VERIFY_ERROR (WRONG_TYPE, evil.moveInto(i));
392 
393 
394  cout << "==fullVirtualCopySupport=="<<endl
395  << i <<endl
396  << ii <<endl
397  << iii <<endl
398  << iiii <<endl;
399 
400  //need to clean-up the placement-new instance explicitly
401  iiii.~Interface();
402  }
403 
404 
405  void
406  verify_noAssignementSupport()
407  {
408  ClonableImpl b,bb,bbb;
409  Interface& i(b);
410  Interface& ii(bb);
411  Interface& iii(bbb);
412 
413  char storage[sizeof(ClonableImpl)];
414  Interface& iiii (*reinterpret_cast<Interface*> (&storage));
415 
416  string prevID = b;
417  CHECK (!isnil (b));
418 
419  i.moveInto(&storage);
420  CHECK (string(iiii) == prevID);
421  CHECK (!isnil(iiii));
422  CHECK ( isnil(i));
423 
424  iiii.~Interface(); //clean-up previously placed instance
425 
426  prevID = ii;
427  ii.copyInto(&storage);
428  CHECK (!isnil(ii));
429  CHECK (!isnil(iiii));
430  CHECK ( isnil(i));
431  CHECK (string(iiii) == prevID);
432  CHECK (string(ii) == prevID);
433 
434  prevID = iii;
435  VERIFY_ERROR (LOGIC, iii.copyInto(ii));
436  VERIFY_ERROR (LOGIC, iii.moveInto(ii));
437  CHECK (string(iii) == prevID);
438  CHECK (!isnil(iii));
439 
440  cout << "==noAssignementSupport=="<<endl
441  << i <<endl
442  << ii <<endl
443  << iii <<endl
444  << iiii <<endl;
445 
446  //clean-up placement-new instance
447  iiii.~Interface();
448  }
449 
450 
451  void
452  verify_onlyMovableSupport()
453  {
454  MovableImpl c,cc;
455  Interface& i(c);
456  Interface& ii(cc);
457 
458  char storage[sizeof(MovableImpl)];
459  Interface& iiii (*reinterpret_cast<Interface*> (&storage));
460 
461  string prevID = i;
462  CHECK (!isnil (i));
463 
464  i.moveInto(&storage);
465  CHECK (string(iiii) == prevID);
466  CHECK (!isnil(iiii));
467  CHECK ( isnil(i));
468 
469  prevID = ii;
470  VERIFY_ERROR (LOGIC, ii.copyInto(&storage));
471  VERIFY_ERROR (LOGIC, ii.copyInto(i));
472  VERIFY_ERROR (LOGIC, ii.moveInto(i));
473  CHECK (string(ii) == prevID);
474  CHECK (!isnil(ii));
475  CHECK ( isnil(i));
476 
477  cout << "==onlyMovableSupport=="<<endl
478  << i <<endl
479  << ii <<endl
480  << iiii <<endl;
481 
482  //clean-up placement-new instance
483  iiii.~Interface();
484  }
485 
486 
487  void
488  verify_disabledCopySupport()
489  {
490  ImobileImpl d,dd;
491  Interface& i(d);
492  Interface& ii(dd);
493  char storage[sizeof(ImobileImpl)];
494 
495  CHECK (!isnil (i));
496 
497  string prevID = ii;
498  VERIFY_ERROR (LOGIC, ii.copyInto(&storage));
499  VERIFY_ERROR (LOGIC, ii.moveInto(&storage));
500  VERIFY_ERROR (LOGIC, ii.copyInto(i));
501  VERIFY_ERROR (LOGIC, ii.moveInto(i));
502  CHECK (string(ii) == prevID);
503  CHECK (!isnil(ii));
504  CHECK (!isnil (i));
505 
506  cout << "==disabledCopySupport=="<<endl
507  << i <<endl
508  << ii <<endl;
509 
510  //no clean-up,
511  //since we never created anything in the storage buffer
512  }
513  };
514 
515 
517  LAUNCHER (VirtualCopySupport_test, "unit common");
518 
519 
520 
521 }}} // namespace lib::meta::test
Automatically use custom string conversion in C++ stream output.
Policy to pick a suitable implementation of "virtual copy operations".
Definition: run.hpp:40
Front-end for printf-style string template interpolation.
int rani(uint bound=_iBOUND())
Definition: random.hpp:135
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises an exception.
A front-end for using printf-style formatting.
Implementation namespace for support and library code.
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:190
Simplistic test class runner.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
A collection of frequently used helper functions to support unit testing.
Helper for building »virtual copy« operations.