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) Lumiera.org
5  2015, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 * *****************************************************/
22 
29 #include "lib/test/run.hpp"
30 #include "lib/format-string.hpp"
31 #include "lib/test/test-helper.hpp"
33 #include "lib/format-string.hpp"
34 #include "lib/format-cout.hpp"
35 #include "lib/util.hpp"
36 
37 #include <string>
38 #include <type_traits>
39 
40 using util::_Fmt;
41 using util::isnil;
42 using std::string;
43 
44 using LERR_(LOGIC);
45 using LERR_(WRONG_TYPE);
46 
47 
48 namespace lib {
49 namespace meta {
50 namespace test {
51 
52  namespace { // Test fixture...
53 
54  int _CheckSum_ = 0;
55 
56  class Interface;
57 
58 
68  {
69  public:
70  virtual void copyInto (void* ) const { /* NOP */ };
71  virtual void moveInto (void* ) { /* NOP */ };
72  virtual void copyInto (Interface&) const { /* NOP */ };
73  virtual void moveInto (Interface&) { /* NOP */ };
74  };
75 
76 
80  class Interface
81  : public CopyInterface
82  {
83  public:
84  virtual ~Interface() { }
85  virtual operator string() const =0;
86  virtual bool empty() const =0;
87  };
88 
92  template<uint i>
93  class Sub
94  : public Interface
95  {
96  static_assert (0 < i, "invalid construction");
97 
98  char storage_[i];
99 
100  char&
101  access()
102  {
103  return storage_[i-1];
104  }
105  char const&
106  access() const
107  {
108  return storage_[i-1];
109  }
110 
111  public: /* == Implementation of the Interface == */
112 
113  virtual operator string() const override
114  {
115  return _Fmt("Sub|%s|%d|-%s")
116  % util::typeStr(this)
117  % i
118  % access();
119  }
120 
121  virtual bool
122  empty() const
123  {
124  return not access();
125  }
126 
127 
128  public: /* == full set of copy and assignment operations == */
129 
130  ~Sub()
131  {
132  _CheckSum_ -= access();
133  }
134  Sub()
135  {
136  access() = 'A' + rand() % 23;
137  _CheckSum_ += access();
138  }
139  Sub (Sub const& osub)
140  {
141  access() = osub.access();
142  _CheckSum_ += access();
143  }
144  Sub (Sub&& rsub)
145  {
146  access() = 0;
147  std::swap(access(),rsub.access());
148  }
149  Sub&
150  operator= (Sub const& osub)
151  {
152  if (!util::isSameObject (*this, osub))
153  {
154  _CheckSum_ -= access();
155  access() = osub.access();
156  _CheckSum_ += access();
157  }
158  return *this;
159  }
160  Sub&
161  operator= (Sub&& rsub)
162  {
163  _CheckSum_ -= access();
164  access() = 0;
165  std::swap(access(),rsub.access());
166  return *this;
167  }
168  };
169 
170 
171  /* == create various flavours of copyable / noncopyable objects == */
172 
173  template<char c>
174  class Regular
175  : public Sub<c>
176  { };
177 
178  template<char c>
180  : public Sub<c>
181  {
182  void operator= (UnAssignable const&); // private and unimplemented
183  public:
184  UnAssignable() = default;
185  UnAssignable(UnAssignable&&) = default;
186  UnAssignable(UnAssignable const&) = default;
187  };
188 
189  template<char c>
191  : public UnAssignable<c>
192  {
193  public:
194  OnlyMovable (OnlyMovable const&) = delete;
195  OnlyMovable() = default;
196  OnlyMovable(OnlyMovable&&) = default;
197  };
198 
199  template<char c>
201  : public OnlyMovable<c>
202  {
203  public:
204  Noncopyable (Noncopyable&&) = delete;
205  Noncopyable () = default;
206  };
207 
208 
209 
210  /* == concrete implementation subclass with virtual copy support == */
211 
212  template<class IMP>
213  class Opaque //-----Interface| CRTP-Impl | direct Baseclass
214  : public CopySupport<IMP>::template Policy<Interface, Opaque<IMP>, IMP>
215  {
216  public:
217  static Opaque&
218  downcast (Interface& bas)
219  {
220  Opaque* impl = dynamic_cast<Opaque*> (&bas);
221 
222  if (!impl)
223  throw error::Logic{"virtual copy works only on instances "
224  "of the same concrete implementation class"
225  , LERR_(WRONG_TYPE)};
226  else
227  return *impl;
228  }
229  };
230 
231  // == Test subject(s)==========================
232  using RegularImpl = Opaque<Regular<'a'>>;
233  using ClonableImpl = Opaque<UnAssignable<'b'>>;
234  using MovableImpl = Opaque<OnlyMovable<'c'>>;
235  using ImobileImpl = Opaque<Noncopyable<'d'>>;
236 
237  }//(End)Test fixture
238 
239 
240 
241 
242 
243 
244 
245  /**********************************************************************************************/
251  class VirtualCopySupport_test : public Test
252  {
253  virtual void
254  run (Arg)
255  {
256  CHECK(0 == _CheckSum_);
257 
258  verify_TestFixture();
259 
260  CHECK(0 == _CheckSum_);
261 
262  verify_fullVirtualCopySupport();
263  verify_noAssignementSupport();
264  verify_onlyMovableSupport();
265  verify_disabledCopySupport();
266 
267  CHECK(0 == _CheckSum_);
268  }
269 
270 
283  void
285  {
286  /* == full copy, move and assignment == */
287  Regular<'A'> a;
288  Regular<'A'> aa(a);
289  Regular<'A'> a1;
290 
291  cout << a <<endl
292  << aa <<endl
293  << a1 <<endl;
294 
295  a1 = a;
296 
297  CHECK (string(a) == string(aa));
298  CHECK (string(a) == string(a1));
299  CHECK (!isnil(a1));
300 
301  a = std::move(a1);
302 
303  CHECK (isnil(a1));
304  CHECK (string(a) == string(aa));
305 
306 
307  /* == interface vs. concrete class == */
308  Regular<'B'> b;
309  Interface& ii = b;
310 
311  string prevID(b);
312  ii = a; // Copy operation of Base-Interface is NOP
313  CHECK (string(b) == prevID);
314 
315 
316  /* == assignment inhibited == */
317  UnAssignable<'C'> c;
318  UnAssignable<'C'> cc(c);
319 
320  CHECK (string(c) == string(cc));
321 
322  prevID = cc;
323  UnAssignable<'C'> ccc(std::move(cc));
324 
325  cout << cc <<endl
326  << ccc <<endl;
327 
328  CHECK (string(ccc) == prevID);
329  CHECK (string(cc) != prevID);
330  CHECK (!isnil(ccc));
331  CHECK (isnil (cc)); // killed by moving away
332 
333  // c = cc; // does not compile
334 
335 
336  /* == only move construction allowed == */
337  OnlyMovable<'D'> d;
338  OnlyMovable<'D'> dd (std::move(d));
339 
340  cout << d <<endl
341  << dd <<endl;
342 
343  CHECK (string(dd) != string(d));
344  CHECK (!isnil(dd));
345  CHECK (isnil(d));
346 
347  // OnlyMovable<'D'> d1 (dd); // does not compile
348  // d = dd; // does not compile;
349 
350 
351  /* == all copy/assignment inhibited == */
352  Noncopyable<'E'> e;
353  // Noncopyable<'E'> ee (e); // does not compile
354  // Noncopyable<'E'> eee (std::move (e)); // does not compile
355  // e = Noncopyable<'E'>(); // does not compile
356 
357  CHECK (!isnil (e));
358  }
359 
360 
361 
362 
363 
364 
365  void
366  verify_fullVirtualCopySupport()
367  {
368  RegularImpl a,aa,aaa;
369  Interface& i(a);
370  Interface& ii(aa);
371  Interface& iii(aaa);
372 
373  char storage[sizeof(RegularImpl)];
374  Interface& iiii (*reinterpret_cast<Interface*> (&storage));
375 
376  string prevID = a;
377  CHECK (!isnil (a));
378 
379  i.moveInto(&storage);
380  CHECK (string(iiii) == prevID);
381  CHECK (!isnil(iiii));
382  CHECK ( isnil(i));
383 
384  ii.copyInto(i);
385  CHECK (!isnil(i));
386  CHECK (!isnil(ii));
387  CHECK (string(i) == string(ii));
388 
389  prevID = iii;
390  iii.moveInto(ii);
391  CHECK (!isnil(ii));
392  CHECK ( isnil(iii));
393  CHECK (string(ii) == prevID);
394 
395  // Verify that type mismatch in assignment is detected...
396  Opaque<Regular<'!'>> divergent;
397  Interface& evil(divergent);
398  VERIFY_ERROR (WRONG_TYPE, evil.copyInto(i));
399  VERIFY_ERROR (WRONG_TYPE, evil.moveInto(i));
400 
401 
402  cout << "==fullVirtualCopySupport=="<<endl
403  << i <<endl
404  << ii <<endl
405  << iii <<endl
406  << iiii <<endl;
407 
408  //need to clean-up the placement-new instance explicitly
409  iiii.~Interface();
410  }
411 
412 
413  void
414  verify_noAssignementSupport()
415  {
416  ClonableImpl b,bb,bbb;
417  Interface& i(b);
418  Interface& ii(bb);
419  Interface& iii(bbb);
420 
421  char storage[sizeof(ClonableImpl)];
422  Interface& iiii (*reinterpret_cast<Interface*> (&storage));
423 
424  string prevID = b;
425  CHECK (!isnil (b));
426 
427  i.moveInto(&storage);
428  CHECK (string(iiii) == prevID);
429  CHECK (!isnil(iiii));
430  CHECK ( isnil(i));
431 
432  iiii.~Interface(); //clean-up previously placed instance
433 
434  prevID = ii;
435  ii.copyInto(&storage);
436  CHECK (!isnil(ii));
437  CHECK (!isnil(iiii));
438  CHECK ( isnil(i));
439  CHECK (string(iiii) == prevID);
440  CHECK (string(ii) == prevID);
441 
442  prevID = iii;
443  VERIFY_ERROR (LOGIC, iii.copyInto(ii));
444  VERIFY_ERROR (LOGIC, iii.moveInto(ii));
445  CHECK (string(iii) == prevID);
446  CHECK (!isnil(iii));
447 
448  cout << "==noAssignementSupport=="<<endl
449  << i <<endl
450  << ii <<endl
451  << iii <<endl
452  << iiii <<endl;
453 
454  //clean-up placement-new instance
455  iiii.~Interface();
456  }
457 
458 
459  void
460  verify_onlyMovableSupport()
461  {
462  MovableImpl c,cc;
463  Interface& i(c);
464  Interface& ii(cc);
465 
466  char storage[sizeof(MovableImpl)];
467  Interface& iiii (*reinterpret_cast<Interface*> (&storage));
468 
469  string prevID = i;
470  CHECK (!isnil (i));
471 
472  i.moveInto(&storage);
473  CHECK (string(iiii) == prevID);
474  CHECK (!isnil(iiii));
475  CHECK ( isnil(i));
476 
477  prevID = ii;
478  VERIFY_ERROR (LOGIC, ii.copyInto(&storage));
479  VERIFY_ERROR (LOGIC, ii.copyInto(i));
480  VERIFY_ERROR (LOGIC, ii.moveInto(i));
481  CHECK (string(ii) == prevID);
482  CHECK (!isnil(ii));
483  CHECK ( isnil(i));
484 
485  cout << "==onlyMovableSupport=="<<endl
486  << i <<endl
487  << ii <<endl
488  << iiii <<endl;
489 
490  //clean-up placement-new instance
491  iiii.~Interface();
492  }
493 
494 
495  void
496  verify_disabledCopySupport()
497  {
498  ImobileImpl d,dd;
499  Interface& i(d);
500  Interface& ii(dd);
501  char storage[sizeof(ImobileImpl)];
502 
503  CHECK (!isnil (i));
504 
505  string prevID = ii;
506  VERIFY_ERROR (LOGIC, ii.copyInto(&storage));
507  VERIFY_ERROR (LOGIC, ii.moveInto(&storage));
508  VERIFY_ERROR (LOGIC, ii.copyInto(i));
509  VERIFY_ERROR (LOGIC, ii.moveInto(i));
510  CHECK (string(ii) == prevID);
511  CHECK (!isnil(ii));
512  CHECK (!isnil (i));
513 
514  cout << "==disabledCopySupport=="<<endl
515  << i <<endl
516  << ii <<endl;
517 
518  //no clean-up,
519  //since we never created anything in the storage buffer
520  }
521  };
522 
523 
525  LAUNCHER (VirtualCopySupport_test, "unit common");
526 
527 
528 
529 }}} // 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:49
Front-end for printf-style string template interpolation.
#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:199
Simple 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.