1 // 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.
37 typedef std::pair< void *, std::type_info const * > object_id;
38
39 // Object IDs need to be printed
40 std::ostream & operator <<( std::ostream &os, object_id const &oi );
41
42 // A way to generate an object ID
43 template < typename T >
44 object_id identify( T &obj );
45
46 // A custom comparison type is needed
47 struct 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.
55 class object_registrar
56 : private boost::noncopyable
57 {
58 public:
59
60 #ifndef BOOST_NO_MEMBER_TEMPLATES
61 template < typename T >
register_object(T & obj)62 void register_object( T &obj )
63 { this->register_object_imp( identify(obj) ); }
64 template < typename T, typename U >
register_use(T & owner,U & owned)65 void register_use( T &owner, U &owned )
66 { this->register_use_imp( identify(owner), identify(owned) ); }
67 template < typename T, typename U >
unregister_use(T & owner,U & owned)68 void unregister_use( T &owner, U &owned )
69 { this->unregister_use_imp( identify(owner), identify(owned) ); }
70 template < typename T >
unregister_object(T & obj)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
93 class base_or_member
94 {
95 public:
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
103 class base_class
104 {
105 public:
106 explicit base_class( base_or_member &x, base_or_member *y = 0,
107 base_or_member *z = 0 );
108
109 ~base_class();
110
111 private:
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.
119 class bad_class
120 : public base_class
121 {
122 public:
123 bad_class();
124 ~bad_class();
125
126 private:
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.
134 class 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
141 public:
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.
151 class 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
162 public:
163 good_class_2();
164 ~good_class_2();
165
166 }; // good_class_2
167
168 // Declare/define the single object registrar
169 object_registrar obj_reg;
170
171
172 // Main functionality
173 int
main()174 main()
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
221 std::ostream &
operator <<(std::ostream & os,object_id const & oi)222 operator <<
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
237 template < typename T >
238 inline
239 object_id
identify(T & obj)240 identify
241 (
242 T & obj
243 )
244 {
245 return std::make_pair( static_cast<void *>(&obj), &(typeid( obj )) );
246 }
247
248 // Compare two object IDs
249 bool
operator ()(object_id const & a,object_id const & b) const250 object_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
288 void
register_object_imp(object_id obj)289 object_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
314 void
register_use_imp(object_id owner,object_id owned)315 object_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
337 void
unregister_use_imp(object_id owner,object_id owned)338 object_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
360 void
unregister_object_imp(object_id obj)361 object_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
base_or_member(int x,double y)405 base_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
420 inline
~base_or_member()421 base_or_member::~base_or_member
422 (
423 )
424 {
425 PRIVATE_REGISTER_DEATH( *this );
426 }
427
428 // Create a base_class, registering any objects used
base_class(base_or_member & x,base_or_member * y,base_or_member * z)429 base_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
~base_class()469 base_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
bad_class()505 bad_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
~bad_class()520 bad_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
good_class_1()534 good_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
~good_class_1()549 good_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
good_class_2()563 good_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
~good_class_2()581 good_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 }
594