blob: 685c6387a2966c2ff9dfc931822e00ee39ce8d63 [file] [log] [blame]
Brian Silverman60e3e2a2018-08-04 23:57:12 -07001// Boost test program for base-from-member class templates -----------------//
2
3// Copyright 2001, 2003 Daryle Walker. Use, modification, and distribution are
4// subject to the Boost Software License, Version 1.0. (See accompanying file
5// LICENSE_1_0.txt or a copy at <http://www.boost.org/LICENSE_1_0.txt>.)
6
7// See <http://www.boost.org/libs/utility/> for the library's home page.
8
9// Revision History
10// 14 Jun 2003 Adjusted code for Boost.Test changes (Daryle Walker)
11// 29 Aug 2001 Initial Version (Daryle Walker)
12
13#include <boost/core/lightweight_test.hpp>
14
15#include <boost/config.hpp> // for BOOST_NO_MEMBER_TEMPLATES
16#include <boost/noncopyable.hpp> // for boost::noncopyable
17
18#include <boost/utility/base_from_member.hpp> // for boost::base_from_member
19
20#include <functional> // for std::less
21#include <iostream> // for std::cout (std::ostream, std::endl indirectly)
22#include <set> // for std::set
23#include <typeinfo> // for std::type_info
24#include <utility> // for std::pair, std::make_pair
25#include <vector> // for std::vector
26
27
28// Control if extra information is printed
29#ifndef CONTROL_EXTRA_PRINTING
30#define CONTROL_EXTRA_PRINTING 1
31#endif
32
33
34// A (sub)object can be identified by its memory location and its type.
35// Both are needed since an object can start at the same place as its
36// first base class subobject and/or contained subobject.
37typedef std::pair< void *, std::type_info const * > object_id;
38
39// Object IDs need to be printed
40std::ostream & operator <<( std::ostream &os, object_id const &oi );
41
42// A way to generate an object ID
43template < typename T >
44 object_id identify( T &obj );
45
46// A custom comparison type is needed
47struct object_id_compare
48{
49 bool operator ()( object_id const &a, object_id const &b ) const;
50
51}; // object_id_compare
52
53// A singleton of this type coordinates the acknowledgements
54// of objects being created and used.
55class object_registrar
56 : private boost::noncopyable
57{
58public:
59
60 #ifndef BOOST_NO_MEMBER_TEMPLATES
61 template < typename T >
62 void register_object( T &obj )
63 { this->register_object_imp( identify(obj) ); }
64 template < typename T, typename U >
65 void register_use( T &owner, U &owned )
66 { this->register_use_imp( identify(owner), identify(owned) ); }
67 template < typename T, typename U >
68 void unregister_use( T &owner, U &owned )
69 { this->unregister_use_imp( identify(owner), identify(owned) ); }
70 template < typename T >
71 void unregister_object( T &obj )
72 { this->unregister_object_imp( identify(obj) ); }
73 #endif
74
75 void register_object_imp( object_id obj );
76 void register_use_imp( object_id owner, object_id owned );
77 void unregister_use_imp( object_id owner, object_id owned );
78 void unregister_object_imp( object_id obj );
79
80 typedef std::set<object_id, object_id_compare> set_type;
81
82 typedef std::vector<object_id> error_record_type;
83 typedef std::vector< std::pair<object_id, object_id> > error_pair_type;
84
85 set_type db_;
86
87 error_pair_type defrauders_in_, defrauders_out_;
88 error_record_type overeager_, overkilled_;
89
90}; // object_registrar
91
92// A sample type to be used by containing types
93class base_or_member
94{
95public:
96 explicit base_or_member( int x = 1, double y = -0.25 );
97 ~base_or_member();
98
99}; // base_or_member
100
101// A sample type that uses base_or_member, used
102// as a base for the main demonstration classes
103class base_class
104{
105public:
106 explicit base_class( base_or_member &x, base_or_member *y = 0,
107 base_or_member *z = 0 );
108
109 ~base_class();
110
111private:
112 base_or_member *x_, *y_, *z_;
113
114}; // base_class
115
116// This bad class demonstrates the direct method of a base class needing
117// to be initialized by a member. This is improper since the member
118// isn't initialized until after the base class.
119class bad_class
120 : public base_class
121{
122public:
123 bad_class();
124 ~bad_class();
125
126private:
127 base_or_member x_;
128
129}; // bad_class
130
131// The first good class demonstrates the correct way to initialize a
132// base class with a member. The member is changed to another base
133// class, one that is initialized before the base that needs it.
134class good_class_1
135 : private boost::base_from_member<base_or_member>
136 , public base_class
137{
138 typedef boost::base_from_member<base_or_member> pbase_type;
139 typedef base_class base_type;
140
141public:
142 good_class_1();
143 ~good_class_1();
144
145}; // good_class_1
146
147// The second good class also demonstrates the correct way to initialize
148// base classes with other subobjects. This class uses the other helpers
149// in the library, and shows the technique of using two base subobjects
150// of the "same" type.
151class good_class_2
152 : private boost::base_from_member<base_or_member, 0>
153 , private boost::base_from_member<base_or_member, 1>
154 , private boost::base_from_member<base_or_member, 2>
155 , public base_class
156{
157 typedef boost::base_from_member<base_or_member, 0> pbase_type0;
158 typedef boost::base_from_member<base_or_member, 1> pbase_type1;
159 typedef boost::base_from_member<base_or_member, 2> pbase_type2;
160 typedef base_class base_type;
161
162public:
163 good_class_2();
164 ~good_class_2();
165
166}; // good_class_2
167
168// Declare/define the single object registrar
169object_registrar obj_reg;
170
171
172// Main functionality
173int
174main()
175{
176 BOOST_TEST( obj_reg.db_.empty() );
177 BOOST_TEST( obj_reg.defrauders_in_.empty() );
178 BOOST_TEST( obj_reg.defrauders_out_.empty() );
179 BOOST_TEST( obj_reg.overeager_.empty() );
180 BOOST_TEST( obj_reg.overkilled_.empty() );
181
182 // Make a separate block to examine pre- and post-effects
183 {
184 using std::cout;
185 using std::endl;
186
187 bad_class bc;
188 BOOST_TEST( obj_reg.db_.size() == 3 );
189 BOOST_TEST( obj_reg.defrauders_in_.size() == 1 );
190
191 good_class_1 gc1;
192 BOOST_TEST( obj_reg.db_.size() == 6 );
193 BOOST_TEST( obj_reg.defrauders_in_.size() == 1 );
194
195 good_class_2 gc2;
196 BOOST_TEST( obj_reg.db_.size() == 11 );
197 BOOST_TEST( obj_reg.defrauders_in_.size() == 1 );
198
199 BOOST_TEST( obj_reg.defrauders_out_.empty() );
200 BOOST_TEST( obj_reg.overeager_.empty() );
201 BOOST_TEST( obj_reg.overkilled_.empty() );
202
203 // Getting the addresses of the objects ensure
204 // that they're used, and not optimized away.
205 cout << "Object 'bc' is at " << &bc << '.' << endl;
206 cout << "Object 'gc1' is at " << &gc1 << '.' << endl;
207 cout << "Object 'gc2' is at " << &gc2 << '.' << endl;
208 }
209
210 BOOST_TEST( obj_reg.db_.empty() );
211 BOOST_TEST( obj_reg.defrauders_in_.size() == 1 );
212 BOOST_TEST( obj_reg.defrauders_out_.size() == 1 );
213 BOOST_TEST( obj_reg.overeager_.empty() );
214 BOOST_TEST( obj_reg.overkilled_.empty() );
215
216 return boost::report_errors();
217}
218
219
220// Print an object's ID
221std::ostream &
222operator <<
223(
224 std::ostream & os,
225 object_id const & oi
226)
227{
228 // I had an std::ostringstream to help, but I did not need it since
229 // the program never screws around with formatting. Worse, using
230 // std::ostringstream is an issue with some compilers.
231
232 return os << '[' << ( oi.second ? oi.second->name() : "NOTHING" )
233 << " at " << oi.first << ']';
234}
235
236// Get an object ID given an object
237template < typename T >
238inline
239object_id
240identify
241(
242 T & obj
243)
244{
245 return std::make_pair( static_cast<void *>(&obj), &(typeid( obj )) );
246}
247
248// Compare two object IDs
249bool
250object_id_compare::operator ()
251(
252 object_id const & a,
253 object_id const & b
254) const
255{
256 std::less<void *> vp_cmp;
257 if ( vp_cmp(a.first, b.first) )
258 {
259 return true;
260 }
261 else if ( vp_cmp(b.first, a.first) )
262 {
263 return false;
264 }
265 else
266 {
267 // object pointers are equal, compare the types
268 if ( a.second == b.second )
269 {
270 return false;
271 }
272 else if ( !a.second )
273 {
274 return true; // NULL preceeds anything else
275 }
276 else if ( !b.second )
277 {
278 return false; // NULL preceeds anything else
279 }
280 else
281 {
282 return a.second->before( *b.second ) != 0;
283 }
284 }
285}
286
287// Let an object register its existence
288void
289object_registrar::register_object_imp
290(
291 object_id obj
292)
293{
294 if ( db_.count(obj) <= 0 )
295 {
296 db_.insert( obj );
297
298 #if CONTROL_EXTRA_PRINTING
299 std::cout << "Registered " << obj << '.' << std::endl;
300 #endif
301 }
302 else
303 {
304 overeager_.push_back( obj );
305
306 #if CONTROL_EXTRA_PRINTING
307 std::cout << "Attempted to register a non-existant " << obj
308 << '.' << std::endl;
309 #endif
310 }
311}
312
313// Let an object register its use of another object
314void
315object_registrar::register_use_imp
316(
317 object_id owner,
318 object_id owned
319)
320{
321 if ( db_.count(owned) > 0 )
322 {
323 // We don't care to record usage registrations
324 }
325 else
326 {
327 defrauders_in_.push_back( std::make_pair(owner, owned) );
328
329 #if CONTROL_EXTRA_PRINTING
330 std::cout << "Attempted to own a non-existant " << owned
331 << " by " << owner << '.' << std::endl;
332 #endif
333 }
334}
335
336// Let an object un-register its use of another object
337void
338object_registrar::unregister_use_imp
339(
340 object_id owner,
341 object_id owned
342)
343{
344 if ( db_.count(owned) > 0 )
345 {
346 // We don't care to record usage un-registrations
347 }
348 else
349 {
350 defrauders_out_.push_back( std::make_pair(owner, owned) );
351
352 #if CONTROL_EXTRA_PRINTING
353 std::cout << "Attempted to disown a non-existant " << owned
354 << " by " << owner << '.' << std::endl;
355 #endif
356 }
357}
358
359// Let an object un-register its existence
360void
361object_registrar::unregister_object_imp
362(
363 object_id obj
364)
365{
366 set_type::iterator const i = db_.find( obj );
367
368 if ( i != db_.end() )
369 {
370 db_.erase( i );
371
372 #if CONTROL_EXTRA_PRINTING
373 std::cout << "Unregistered " << obj << '.' << std::endl;
374 #endif
375 }
376 else
377 {
378 overkilled_.push_back( obj );
379
380 #if CONTROL_EXTRA_PRINTING
381 std::cout << "Attempted to unregister a non-existant " << obj
382 << '.' << std::endl;
383 #endif
384 }
385}
386
387// Macros to abstract the registration of objects
388#ifndef BOOST_NO_MEMBER_TEMPLATES
389#define PRIVATE_REGISTER_BIRTH(o) obj_reg.register_object( (o) )
390#define PRIVATE_REGISTER_DEATH(o) obj_reg.unregister_object( (o) )
391#define PRIVATE_REGISTER_USE(o, w) obj_reg.register_use( (o), (w) )
392#define PRIVATE_UNREGISTER_USE(o, w) obj_reg.unregister_use( (o), (w) )
393#else
394#define PRIVATE_REGISTER_BIRTH(o) obj_reg.register_object_imp( \
395 identify((o)) )
396#define PRIVATE_REGISTER_DEATH(o) obj_reg.unregister_object_imp( \
397 identify((o)) )
398#define PRIVATE_REGISTER_USE(o, w) obj_reg.register_use_imp( identify((o)), \
399 identify((w)) )
400#define PRIVATE_UNREGISTER_USE(o, w) obj_reg.unregister_use_imp( \
401 identify((o)), identify((w)) )
402#endif
403
404// Create a base_or_member, with arguments to simulate member initializations
405base_or_member::base_or_member
406(
407 int x, // = 1
408 double y // = -0.25
409)
410{
411 PRIVATE_REGISTER_BIRTH( *this );
412
413 #if CONTROL_EXTRA_PRINTING
414 std::cout << "\tMy x-factor is " << x << " and my y-factor is " << y
415 << '.' << std::endl;
416 #endif
417}
418
419// Destroy a base_or_member
420inline
421base_or_member::~base_or_member
422(
423)
424{
425 PRIVATE_REGISTER_DEATH( *this );
426}
427
428// Create a base_class, registering any objects used
429base_class::base_class
430(
431 base_or_member & x,
432 base_or_member * y, // = 0
433 base_or_member * z // = 0
434)
435 : x_( &x ), y_( y ), z_( z )
436{
437 PRIVATE_REGISTER_BIRTH( *this );
438
439 #if CONTROL_EXTRA_PRINTING
440 std::cout << "\tMy x-factor is " << x_;
441 #endif
442
443 PRIVATE_REGISTER_USE( *this, *x_ );
444
445 if ( y_ )
446 {
447 #if CONTROL_EXTRA_PRINTING
448 std::cout << ", my y-factor is " << y_;
449 #endif
450
451 PRIVATE_REGISTER_USE( *this, *y_ );
452 }
453
454 if ( z_ )
455 {
456 #if CONTROL_EXTRA_PRINTING
457 std::cout << ", my z-factor is " << z_;
458 #endif
459
460 PRIVATE_REGISTER_USE( *this, *z_ );
461 }
462
463 #if CONTROL_EXTRA_PRINTING
464 std::cout << '.' << std::endl;
465 #endif
466}
467
468// Destroy a base_class, unregistering the objects it uses
469base_class::~base_class
470(
471)
472{
473 PRIVATE_REGISTER_DEATH( *this );
474
475 #if CONTROL_EXTRA_PRINTING
476 std::cout << "\tMy x-factor was " << x_;
477 #endif
478
479 PRIVATE_UNREGISTER_USE( *this, *x_ );
480
481 if ( y_ )
482 {
483 #if CONTROL_EXTRA_PRINTING
484 std::cout << ", my y-factor was " << y_;
485 #endif
486
487 PRIVATE_UNREGISTER_USE( *this, *y_ );
488 }
489
490 if ( z_ )
491 {
492 #if CONTROL_EXTRA_PRINTING
493 std::cout << ", my z-factor was " << z_;
494 #endif
495
496 PRIVATE_UNREGISTER_USE( *this, *z_ );
497 }
498
499 #if CONTROL_EXTRA_PRINTING
500 std::cout << '.' << std::endl;
501 #endif
502}
503
504// Create a bad_class, noting the improper construction order
505bad_class::bad_class
506(
507)
508 : x_( -7, 16.75 ), base_class( x_ ) // this order doesn't matter
509{
510 PRIVATE_REGISTER_BIRTH( *this );
511
512 #if CONTROL_EXTRA_PRINTING
513 std::cout << "\tMy factor is at " << &x_
514 << " and my base is at " << static_cast<base_class *>(this) << '.'
515 << std::endl;
516 #endif
517}
518
519// Destroy a bad_class, noting the improper destruction order
520bad_class::~bad_class
521(
522)
523{
524 PRIVATE_REGISTER_DEATH( *this );
525
526 #if CONTROL_EXTRA_PRINTING
527 std::cout << "\tMy factor was at " << &x_
528 << " and my base was at " << static_cast<base_class *>(this)
529 << '.' << std::endl;
530 #endif
531}
532
533// Create a good_class_1, noting the proper construction order
534good_class_1::good_class_1
535(
536)
537 : pbase_type( 8 ), base_type( member )
538{
539 PRIVATE_REGISTER_BIRTH( *this );
540
541 #if CONTROL_EXTRA_PRINTING
542 std::cout << "\tMy factor is at " << &member
543 << " and my base is at " << static_cast<base_class *>(this) << '.'
544 << std::endl;
545 #endif
546}
547
548// Destroy a good_class_1, noting the proper destruction order
549good_class_1::~good_class_1
550(
551)
552{
553 PRIVATE_REGISTER_DEATH( *this );
554
555 #if CONTROL_EXTRA_PRINTING
556 std::cout << "\tMy factor was at " << &member
557 << " and my base was at " << static_cast<base_class *>(this)
558 << '.' << std::endl;
559 #endif
560}
561
562// Create a good_class_2, noting the proper construction order
563good_class_2::good_class_2
564(
565)
566 : pbase_type0(), pbase_type1(-16, 0.125), pbase_type2(2, -3)
567 , base_type( pbase_type1::member, &this->pbase_type0::member,
568 &this->pbase_type2::member )
569{
570 PRIVATE_REGISTER_BIRTH( *this );
571
572 #if CONTROL_EXTRA_PRINTING
573 std::cout << "\tMy factors are at " << &this->pbase_type0::member
574 << ", " << &this->pbase_type1::member << ", "
575 << &this->pbase_type2::member << ", and my base is at "
576 << static_cast<base_class *>(this) << '.' << std::endl;
577 #endif
578}
579
580// Destroy a good_class_2, noting the proper destruction order
581good_class_2::~good_class_2
582(
583)
584{
585 PRIVATE_REGISTER_DEATH( *this );
586
587 #if CONTROL_EXTRA_PRINTING
588 std::cout << "\tMy factors were at " << &this->pbase_type0::member
589 << ", " << &this->pbase_type1::member << ", "
590 << &this->pbase_type2::member << ", and my base was at "
591 << static_cast<base_class *>(this) << '.' << std::endl;
592 #endif
593}