1 // Boost test program for base-from-member class templates -----------------//
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>.)
7 // See <http://www.boost.org/libs/utility/> for the library's home page.
10 // 14 Jun 2003 Adjusted code for Boost.Test changes (Daryle Walker)
11 // 29 Aug 2001 Initial Version (Daryle Walker)
13 #include <boost/test/minimal.hpp> // for BOOST_CHECK, main
15 #include <boost/config.hpp> // for BOOST_NO_MEMBER_TEMPLATES
16 #include <boost/cstdlib.hpp> // for boost::exit_success
17 #include <boost/noncopyable.hpp> // for boost::noncopyable
19 #include <boost/utility/base_from_member.hpp> // for boost::base_from_member
21 #include <functional> // for std::binary_function, std::less
22 #include <iostream> // for std::cout (std::ostream, std::endl indirectly)
23 #include <set> // for std::set
24 #include <typeinfo> // for std::type_info
25 #include <utility> // for std::pair, std::make_pair
26 #include <vector> // for std::vector
29 // Control if extra information is printed
30 #ifndef CONTROL_EXTRA_PRINTING
31 #define CONTROL_EXTRA_PRINTING 1
35 // A (sub)object can be identified by its memory location and its type.
36 // Both are needed since an object can start at the same place as its
37 // first base class subobject and/or contained subobject.
38 typedef std::pair
< void *, std::type_info
const * > object_id
;
40 // Object IDs need to be printed
41 std::ostream
& operator <<( std::ostream
&os
, object_id
const &oi
);
43 // A way to generate an object ID
44 template < typename T
>
45 object_id
identify( T
&obj
);
47 // A custom comparison type is needed
48 struct object_id_compare
49 : std::binary_function
<object_id
, object_id
, bool>
51 bool operator ()( object_id
const &a
, object_id
const &b
) const;
53 }; // object_id_compare
55 // A singleton of this type coordinates the acknowledgements
56 // of objects being created and used.
57 class object_registrar
58 : private boost::noncopyable
62 #ifndef BOOST_NO_MEMBER_TEMPLATES
63 template < typename T
>
64 void register_object( T
&obj
)
65 { this->register_object_imp( identify(obj
) ); }
66 template < typename T
, typename U
>
67 void register_use( T
&owner
, U
&owned
)
68 { this->register_use_imp( identify(owner
), identify(owned
) ); }
69 template < typename T
, typename U
>
70 void unregister_use( T
&owner
, U
&owned
)
71 { this->unregister_use_imp( identify(owner
), identify(owned
) ); }
72 template < typename T
>
73 void unregister_object( T
&obj
)
74 { this->unregister_object_imp( identify(obj
) ); }
77 void register_object_imp( object_id obj
);
78 void register_use_imp( object_id owner
, object_id owned
);
79 void unregister_use_imp( object_id owner
, object_id owned
);
80 void unregister_object_imp( object_id obj
);
82 typedef std::set
<object_id
, object_id_compare
> set_type
;
84 typedef std::vector
<object_id
> error_record_type
;
85 typedef std::vector
< std::pair
<object_id
, object_id
> > error_pair_type
;
89 error_pair_type defrauders_in_
, defrauders_out_
;
90 error_record_type overeager_
, overkilled_
;
92 }; // object_registrar
94 // A sample type to be used by containing types
98 explicit base_or_member( int x
= 1, double y
= -0.25 );
103 // A sample type that uses base_or_member, used
104 // as a base for the main demonstration classes
108 explicit base_class( base_or_member
&x
, base_or_member
*y
= 0,
109 base_or_member
*z
= 0 );
114 base_or_member
*x_
, *y_
, *z_
;
118 // This bad class demonstrates the direct method of a base class needing
119 // to be initialized by a member. This is improper since the member
120 // isn't initialized until after the base class.
133 // The first good class demonstrates the correct way to initialize a
134 // base class with a member. The member is changed to another base
135 // class, one that is initialized before the base that needs it.
137 : private boost::base_from_member
<base_or_member
>
140 typedef boost::base_from_member
<base_or_member
> pbase_type
;
141 typedef base_class base_type
;
149 // The second good class also demonstrates the correct way to initialize
150 // base classes with other subobjects. This class uses the other helpers
151 // in the library, and shows the technique of using two base subobjects
152 // of the "same" type.
154 : private boost::base_from_member
<base_or_member
, 0>
155 , private boost::base_from_member
<base_or_member
, 1>
156 , private boost::base_from_member
<base_or_member
, 2>
159 typedef boost::base_from_member
<base_or_member
, 0> pbase_type0
;
160 typedef boost::base_from_member
<base_or_member
, 1> pbase_type1
;
161 typedef boost::base_from_member
<base_or_member
, 2> pbase_type2
;
162 typedef base_class base_type
;
170 // Declare/define the single object registrar
171 object_registrar obj_reg
;
174 // Main functionality
176 test_main( int , char * [] )
178 BOOST_CHECK( obj_reg
.db_
.empty() );
179 BOOST_CHECK( obj_reg
.defrauders_in_
.empty() );
180 BOOST_CHECK( obj_reg
.defrauders_out_
.empty() );
181 BOOST_CHECK( obj_reg
.overeager_
.empty() );
182 BOOST_CHECK( obj_reg
.overkilled_
.empty() );
184 // Make a separate block to examine pre- and post-effects
190 BOOST_CHECK( obj_reg
.db_
.size() == 3 );
191 BOOST_CHECK( obj_reg
.defrauders_in_
.size() == 1 );
194 BOOST_CHECK( obj_reg
.db_
.size() == 6 );
195 BOOST_CHECK( obj_reg
.defrauders_in_
.size() == 1 );
198 BOOST_CHECK( obj_reg
.db_
.size() == 11 );
199 BOOST_CHECK( obj_reg
.defrauders_in_
.size() == 1 );
201 BOOST_CHECK( obj_reg
.defrauders_out_
.empty() );
202 BOOST_CHECK( obj_reg
.overeager_
.empty() );
203 BOOST_CHECK( obj_reg
.overkilled_
.empty() );
205 // Getting the addresses of the objects ensure
206 // that they're used, and not optimized away.
207 cout
<< "Object 'bc' is at " << &bc
<< '.' << endl
;
208 cout
<< "Object 'gc1' is at " << &gc1
<< '.' << endl
;
209 cout
<< "Object 'gc2' is at " << &gc2
<< '.' << endl
;
212 BOOST_CHECK( obj_reg
.db_
.empty() );
213 BOOST_CHECK( obj_reg
.defrauders_in_
.size() == 1 );
214 BOOST_CHECK( obj_reg
.defrauders_out_
.size() == 1 );
215 BOOST_CHECK( obj_reg
.overeager_
.empty() );
216 BOOST_CHECK( obj_reg
.overkilled_
.empty() );
218 return boost::exit_success
;
222 // Print an object's ID
230 // I had an std::ostringstream to help, but I did not need it since
231 // the program never screws around with formatting. Worse, using
232 // std::ostringstream is an issue with some compilers.
234 return os
<< '[' << ( oi
.second
? oi
.second
->name() : "NOTHING" )
235 << " at " << oi
.first
<< ']';
238 // Get an object ID given an object
239 template < typename T
>
247 return std::make_pair( static_cast<void *>(&obj
), &(typeid( obj
)) );
250 // Compare two object IDs
252 object_id_compare::operator ()
258 std::less
<void *> vp_cmp
;
259 if ( vp_cmp(a
.first
, b
.first
) )
263 else if ( vp_cmp(b
.first
, a
.first
) )
269 // object pointers are equal, compare the types
270 if ( a
.second
== b
.second
)
274 else if ( !a
.second
)
276 return true; // NULL preceeds anything else
278 else if ( !b
.second
)
280 return false; // NULL preceeds anything else
284 return a
.second
->before( *b
.second
) != 0;
289 // Let an object register its existence
291 object_registrar::register_object_imp
296 if ( db_
.count(obj
) <= 0 )
300 #if CONTROL_EXTRA_PRINTING
301 std::cout
<< "Registered " << obj
<< '.' << std::endl
;
306 overeager_
.push_back( obj
);
308 #if CONTROL_EXTRA_PRINTING
309 std::cout
<< "Attempted to register a non-existant " << obj
315 // Let an object register its use of another object
317 object_registrar::register_use_imp
323 if ( db_
.count(owned
) > 0 )
325 // We don't care to record usage registrations
329 defrauders_in_
.push_back( std::make_pair(owner
, owned
) );
331 #if CONTROL_EXTRA_PRINTING
332 std::cout
<< "Attempted to own a non-existant " << owned
333 << " by " << owner
<< '.' << std::endl
;
338 // Let an object un-register its use of another object
340 object_registrar::unregister_use_imp
346 if ( db_
.count(owned
) > 0 )
348 // We don't care to record usage un-registrations
352 defrauders_out_
.push_back( std::make_pair(owner
, owned
) );
354 #if CONTROL_EXTRA_PRINTING
355 std::cout
<< "Attempted to disown a non-existant " << owned
356 << " by " << owner
<< '.' << std::endl
;
361 // Let an object un-register its existence
363 object_registrar::unregister_object_imp
368 set_type::iterator
const i
= db_
.find( obj
);
370 if ( i
!= db_
.end() )
374 #if CONTROL_EXTRA_PRINTING
375 std::cout
<< "Unregistered " << obj
<< '.' << std::endl
;
380 overkilled_
.push_back( obj
);
382 #if CONTROL_EXTRA_PRINTING
383 std::cout
<< "Attempted to unregister a non-existant " << obj
389 // Macros to abstract the registration of objects
390 #ifndef BOOST_NO_MEMBER_TEMPLATES
391 #define PRIVATE_REGISTER_BIRTH(o) obj_reg.register_object( (o) )
392 #define PRIVATE_REGISTER_DEATH(o) obj_reg.unregister_object( (o) )
393 #define PRIVATE_REGISTER_USE(o, w) obj_reg.register_use( (o), (w) )
394 #define PRIVATE_UNREGISTER_USE(o, w) obj_reg.unregister_use( (o), (w) )
396 #define PRIVATE_REGISTER_BIRTH(o) obj_reg.register_object_imp( \
398 #define PRIVATE_REGISTER_DEATH(o) obj_reg.unregister_object_imp( \
400 #define PRIVATE_REGISTER_USE(o, w) obj_reg.register_use_imp( identify((o)), \
402 #define PRIVATE_UNREGISTER_USE(o, w) obj_reg.unregister_use_imp( \
403 identify((o)), identify((w)) )
406 // Create a base_or_member, with arguments to simulate member initializations
407 base_or_member::base_or_member
413 PRIVATE_REGISTER_BIRTH( *this );
415 #if CONTROL_EXTRA_PRINTING
416 std::cout
<< "\tMy x-factor is " << x
<< " and my y-factor is " << y
421 // Destroy a base_or_member
423 base_or_member::~base_or_member
427 PRIVATE_REGISTER_DEATH( *this );
430 // Create a base_class, registering any objects used
431 base_class::base_class
434 base_or_member
* y
, // = 0
435 base_or_member
* z
// = 0
437 : x_( &x
), y_( y
), z_( z
)
439 PRIVATE_REGISTER_BIRTH( *this );
441 #if CONTROL_EXTRA_PRINTING
442 std::cout
<< "\tMy x-factor is " << x_
;
445 PRIVATE_REGISTER_USE( *this, *x_
);
449 #if CONTROL_EXTRA_PRINTING
450 std::cout
<< ", my y-factor is " << y_
;
453 PRIVATE_REGISTER_USE( *this, *y_
);
458 #if CONTROL_EXTRA_PRINTING
459 std::cout
<< ", my z-factor is " << z_
;
462 PRIVATE_REGISTER_USE( *this, *z_
);
465 #if CONTROL_EXTRA_PRINTING
466 std::cout
<< '.' << std::endl
;
470 // Destroy a base_class, unregistering the objects it uses
471 base_class::~base_class
475 PRIVATE_REGISTER_DEATH( *this );
477 #if CONTROL_EXTRA_PRINTING
478 std::cout
<< "\tMy x-factor was " << x_
;
481 PRIVATE_UNREGISTER_USE( *this, *x_
);
485 #if CONTROL_EXTRA_PRINTING
486 std::cout
<< ", my y-factor was " << y_
;
489 PRIVATE_UNREGISTER_USE( *this, *y_
);
494 #if CONTROL_EXTRA_PRINTING
495 std::cout
<< ", my z-factor was " << z_
;
498 PRIVATE_UNREGISTER_USE( *this, *z_
);
501 #if CONTROL_EXTRA_PRINTING
502 std::cout
<< '.' << std::endl
;
506 // Create a bad_class, noting the improper construction order
510 : x_( -7, 16.75 ), base_class( x_
) // this order doesn't matter
512 PRIVATE_REGISTER_BIRTH( *this );
514 #if CONTROL_EXTRA_PRINTING
515 std::cout
<< "\tMy factor is at " << &x_
516 << " and my base is at " << static_cast<base_class
*>(this) << '.'
521 // Destroy a bad_class, noting the improper destruction order
522 bad_class::~bad_class
526 PRIVATE_REGISTER_DEATH( *this );
528 #if CONTROL_EXTRA_PRINTING
529 std::cout
<< "\tMy factor was at " << &x_
530 << " and my base was at " << static_cast<base_class
*>(this)
535 // Create a good_class_1, noting the proper construction order
536 good_class_1::good_class_1
539 : pbase_type( 8 ), base_type( member
)
541 PRIVATE_REGISTER_BIRTH( *this );
543 #if CONTROL_EXTRA_PRINTING
544 std::cout
<< "\tMy factor is at " << &member
545 << " and my base is at " << static_cast<base_class
*>(this) << '.'
550 // Destroy a good_class_1, noting the proper destruction order
551 good_class_1::~good_class_1
555 PRIVATE_REGISTER_DEATH( *this );
557 #if CONTROL_EXTRA_PRINTING
558 std::cout
<< "\tMy factor was at " << &member
559 << " and my base was at " << static_cast<base_class
*>(this)
564 // Create a good_class_2, noting the proper construction order
565 good_class_2::good_class_2
568 : pbase_type0(), pbase_type1(-16, 0.125), pbase_type2(2, -3)
569 , base_type( pbase_type1::member
, &this->pbase_type0::member
,
570 &this->pbase_type2::member
)
572 PRIVATE_REGISTER_BIRTH( *this );
574 #if CONTROL_EXTRA_PRINTING
575 std::cout
<< "\tMy factors are at " << &this->pbase_type0::member
576 << ", " << &this->pbase_type1::member
<< ", "
577 << &this->pbase_type2::member
<< ", and my base is at "
578 << static_cast<base_class
*>(this) << '.' << std::endl
;
582 // Destroy a good_class_2, noting the proper destruction order
583 good_class_2::~good_class_2
587 PRIVATE_REGISTER_DEATH( *this );
589 #if CONTROL_EXTRA_PRINTING
590 std::cout
<< "\tMy factors were at " << &this->pbase_type0::member
591 << ", " << &this->pbase_type1::member
<< ", "
592 << &this->pbase_type2::member
<< ", and my base was at "
593 << static_cast<base_class
*>(this) << '.' << std::endl
;