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::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
50 bool operator ()( object_id
const &a
, object_id
const &b
) const;
52 }; // object_id_compare
54 // A singleton of this type coordinates the acknowledgements
55 // of objects being created and used.
56 class object_registrar
57 : private boost::noncopyable
61 #ifndef BOOST_NO_MEMBER_TEMPLATES
62 template < typename T
>
63 void register_object( T
&obj
)
64 { this->register_object_imp( identify(obj
) ); }
65 template < typename T
, typename U
>
66 void register_use( T
&owner
, U
&owned
)
67 { this->register_use_imp( identify(owner
), identify(owned
) ); }
68 template < typename T
, typename U
>
69 void unregister_use( T
&owner
, U
&owned
)
70 { this->unregister_use_imp( identify(owner
), identify(owned
) ); }
71 template < typename T
>
72 void unregister_object( T
&obj
)
73 { this->unregister_object_imp( identify(obj
) ); }
76 void register_object_imp( object_id obj
);
77 void register_use_imp( object_id owner
, object_id owned
);
78 void unregister_use_imp( object_id owner
, object_id owned
);
79 void unregister_object_imp( object_id obj
);
81 typedef std::set
<object_id
, object_id_compare
> set_type
;
83 typedef std::vector
<object_id
> error_record_type
;
84 typedef std::vector
< std::pair
<object_id
, object_id
> > error_pair_type
;
88 error_pair_type defrauders_in_
, defrauders_out_
;
89 error_record_type overeager_
, overkilled_
;
91 }; // object_registrar
93 // A sample type to be used by containing types
97 explicit base_or_member( int x
= 1, double y
= -0.25 );
102 // A sample type that uses base_or_member, used
103 // as a base for the main demonstration classes
107 explicit base_class( base_or_member
&x
, base_or_member
*y
= 0,
108 base_or_member
*z
= 0 );
113 base_or_member
*x_
, *y_
, *z_
;
117 // This bad class demonstrates the direct method of a base class needing
118 // to be initialized by a member. This is improper since the member
119 // isn't initialized until after the base class.
132 // The first good class demonstrates the correct way to initialize a
133 // base class with a member. The member is changed to another base
134 // class, one that is initialized before the base that needs it.
136 : private boost::base_from_member
<base_or_member
>
139 typedef boost::base_from_member
<base_or_member
> pbase_type
;
140 typedef base_class base_type
;
148 // The second good class also demonstrates the correct way to initialize
149 // base classes with other subobjects. This class uses the other helpers
150 // in the library, and shows the technique of using two base subobjects
151 // of the "same" type.
153 : private boost::base_from_member
<base_or_member
, 0>
154 , private boost::base_from_member
<base_or_member
, 1>
155 , private boost::base_from_member
<base_or_member
, 2>
158 typedef boost::base_from_member
<base_or_member
, 0> pbase_type0
;
159 typedef boost::base_from_member
<base_or_member
, 1> pbase_type1
;
160 typedef boost::base_from_member
<base_or_member
, 2> pbase_type2
;
161 typedef base_class base_type
;
169 // Declare/define the single object registrar
170 object_registrar obj_reg
;
173 // Main functionality
175 test_main( int , char * [] )
177 BOOST_CHECK( obj_reg
.db_
.empty() );
178 BOOST_CHECK( obj_reg
.defrauders_in_
.empty() );
179 BOOST_CHECK( obj_reg
.defrauders_out_
.empty() );
180 BOOST_CHECK( obj_reg
.overeager_
.empty() );
181 BOOST_CHECK( obj_reg
.overkilled_
.empty() );
183 // Make a separate block to examine pre- and post-effects
189 BOOST_CHECK( obj_reg
.db_
.size() == 3 );
190 BOOST_CHECK( obj_reg
.defrauders_in_
.size() == 1 );
193 BOOST_CHECK( obj_reg
.db_
.size() == 6 );
194 BOOST_CHECK( obj_reg
.defrauders_in_
.size() == 1 );
197 BOOST_CHECK( obj_reg
.db_
.size() == 11 );
198 BOOST_CHECK( obj_reg
.defrauders_in_
.size() == 1 );
200 BOOST_CHECK( obj_reg
.defrauders_out_
.empty() );
201 BOOST_CHECK( obj_reg
.overeager_
.empty() );
202 BOOST_CHECK( obj_reg
.overkilled_
.empty() );
204 // Getting the addresses of the objects ensure
205 // that they're used, and not optimized away.
206 cout
<< "Object 'bc' is at " << &bc
<< '.' << endl
;
207 cout
<< "Object 'gc1' is at " << &gc1
<< '.' << endl
;
208 cout
<< "Object 'gc2' is at " << &gc2
<< '.' << endl
;
211 BOOST_CHECK( obj_reg
.db_
.empty() );
212 BOOST_CHECK( obj_reg
.defrauders_in_
.size() == 1 );
213 BOOST_CHECK( obj_reg
.defrauders_out_
.size() == 1 );
214 BOOST_CHECK( obj_reg
.overeager_
.empty() );
215 BOOST_CHECK( obj_reg
.overkilled_
.empty() );
217 return boost::exit_success
;
221 // Print an object's ID
229 // I had an std::ostringstream to help, but I did not need it since
230 // the program never screws around with formatting. Worse, using
231 // std::ostringstream is an issue with some compilers.
233 return os
<< '[' << ( oi
.second
? oi
.second
->name() : "NOTHING" )
234 << " at " << oi
.first
<< ']';
237 // Get an object ID given an object
238 template < typename T
>
246 return std::make_pair( static_cast<void *>(&obj
), &(typeid( obj
)) );
249 // Compare two object IDs
251 object_id_compare::operator ()
257 std::less
<void *> vp_cmp
;
258 if ( vp_cmp(a
.first
, b
.first
) )
262 else if ( vp_cmp(b
.first
, a
.first
) )
268 // object pointers are equal, compare the types
269 if ( a
.second
== b
.second
)
273 else if ( !a
.second
)
275 return true; // NULL preceeds anything else
277 else if ( !b
.second
)
279 return false; // NULL preceeds anything else
283 return a
.second
->before( *b
.second
) != 0;
288 // Let an object register its existence
290 object_registrar::register_object_imp
295 if ( db_
.count(obj
) <= 0 )
299 #if CONTROL_EXTRA_PRINTING
300 std::cout
<< "Registered " << obj
<< '.' << std::endl
;
305 overeager_
.push_back( obj
);
307 #if CONTROL_EXTRA_PRINTING
308 std::cout
<< "Attempted to register a non-existant " << obj
314 // Let an object register its use of another object
316 object_registrar::register_use_imp
322 if ( db_
.count(owned
) > 0 )
324 // We don't care to record usage registrations
328 defrauders_in_
.push_back( std::make_pair(owner
, owned
) );
330 #if CONTROL_EXTRA_PRINTING
331 std::cout
<< "Attempted to own a non-existant " << owned
332 << " by " << owner
<< '.' << std::endl
;
337 // Let an object un-register its use of another object
339 object_registrar::unregister_use_imp
345 if ( db_
.count(owned
) > 0 )
347 // We don't care to record usage un-registrations
351 defrauders_out_
.push_back( std::make_pair(owner
, owned
) );
353 #if CONTROL_EXTRA_PRINTING
354 std::cout
<< "Attempted to disown a non-existant " << owned
355 << " by " << owner
<< '.' << std::endl
;
360 // Let an object un-register its existence
362 object_registrar::unregister_object_imp
367 set_type::iterator
const i
= db_
.find( obj
);
369 if ( i
!= db_
.end() )
373 #if CONTROL_EXTRA_PRINTING
374 std::cout
<< "Unregistered " << obj
<< '.' << std::endl
;
379 overkilled_
.push_back( obj
);
381 #if CONTROL_EXTRA_PRINTING
382 std::cout
<< "Attempted to unregister a non-existant " << obj
388 // Macros to abstract the registration of objects
389 #ifndef BOOST_NO_MEMBER_TEMPLATES
390 #define PRIVATE_REGISTER_BIRTH(o) obj_reg.register_object( (o) )
391 #define PRIVATE_REGISTER_DEATH(o) obj_reg.unregister_object( (o) )
392 #define PRIVATE_REGISTER_USE(o, w) obj_reg.register_use( (o), (w) )
393 #define PRIVATE_UNREGISTER_USE(o, w) obj_reg.unregister_use( (o), (w) )
395 #define PRIVATE_REGISTER_BIRTH(o) obj_reg.register_object_imp( \
397 #define PRIVATE_REGISTER_DEATH(o) obj_reg.unregister_object_imp( \
399 #define PRIVATE_REGISTER_USE(o, w) obj_reg.register_use_imp( identify((o)), \
401 #define PRIVATE_UNREGISTER_USE(o, w) obj_reg.unregister_use_imp( \
402 identify((o)), identify((w)) )
405 // Create a base_or_member, with arguments to simulate member initializations
406 base_or_member::base_or_member
412 PRIVATE_REGISTER_BIRTH( *this );
414 #if CONTROL_EXTRA_PRINTING
415 std::cout
<< "\tMy x-factor is " << x
<< " and my y-factor is " << y
420 // Destroy a base_or_member
422 base_or_member::~base_or_member
426 PRIVATE_REGISTER_DEATH( *this );
429 // Create a base_class, registering any objects used
430 base_class::base_class
433 base_or_member
* y
, // = 0
434 base_or_member
* z
// = 0
436 : x_( &x
), y_( y
), z_( z
)
438 PRIVATE_REGISTER_BIRTH( *this );
440 #if CONTROL_EXTRA_PRINTING
441 std::cout
<< "\tMy x-factor is " << x_
;
444 PRIVATE_REGISTER_USE( *this, *x_
);
448 #if CONTROL_EXTRA_PRINTING
449 std::cout
<< ", my y-factor is " << y_
;
452 PRIVATE_REGISTER_USE( *this, *y_
);
457 #if CONTROL_EXTRA_PRINTING
458 std::cout
<< ", my z-factor is " << z_
;
461 PRIVATE_REGISTER_USE( *this, *z_
);
464 #if CONTROL_EXTRA_PRINTING
465 std::cout
<< '.' << std::endl
;
469 // Destroy a base_class, unregistering the objects it uses
470 base_class::~base_class
474 PRIVATE_REGISTER_DEATH( *this );
476 #if CONTROL_EXTRA_PRINTING
477 std::cout
<< "\tMy x-factor was " << x_
;
480 PRIVATE_UNREGISTER_USE( *this, *x_
);
484 #if CONTROL_EXTRA_PRINTING
485 std::cout
<< ", my y-factor was " << y_
;
488 PRIVATE_UNREGISTER_USE( *this, *y_
);
493 #if CONTROL_EXTRA_PRINTING
494 std::cout
<< ", my z-factor was " << z_
;
497 PRIVATE_UNREGISTER_USE( *this, *z_
);
500 #if CONTROL_EXTRA_PRINTING
501 std::cout
<< '.' << std::endl
;
505 // Create a bad_class, noting the improper construction order
509 : x_( -7, 16.75 ), base_class( x_
) // this order doesn't matter
511 PRIVATE_REGISTER_BIRTH( *this );
513 #if CONTROL_EXTRA_PRINTING
514 std::cout
<< "\tMy factor is at " << &x_
515 << " and my base is at " << static_cast<base_class
*>(this) << '.'
520 // Destroy a bad_class, noting the improper destruction order
521 bad_class::~bad_class
525 PRIVATE_REGISTER_DEATH( *this );
527 #if CONTROL_EXTRA_PRINTING
528 std::cout
<< "\tMy factor was at " << &x_
529 << " and my base was at " << static_cast<base_class
*>(this)
534 // Create a good_class_1, noting the proper construction order
535 good_class_1::good_class_1
538 : pbase_type( 8 ), base_type( member
)
540 PRIVATE_REGISTER_BIRTH( *this );
542 #if CONTROL_EXTRA_PRINTING
543 std::cout
<< "\tMy factor is at " << &member
544 << " and my base is at " << static_cast<base_class
*>(this) << '.'
549 // Destroy a good_class_1, noting the proper destruction order
550 good_class_1::~good_class_1
554 PRIVATE_REGISTER_DEATH( *this );
556 #if CONTROL_EXTRA_PRINTING
557 std::cout
<< "\tMy factor was at " << &member
558 << " and my base was at " << static_cast<base_class
*>(this)
563 // Create a good_class_2, noting the proper construction order
564 good_class_2::good_class_2
567 : pbase_type0(), pbase_type1(-16, 0.125), pbase_type2(2, -3)
568 , base_type( pbase_type1::member
, &this->pbase_type0::member
,
569 &this->pbase_type2::member
)
571 PRIVATE_REGISTER_BIRTH( *this );
573 #if CONTROL_EXTRA_PRINTING
574 std::cout
<< "\tMy factors are at " << &this->pbase_type0::member
575 << ", " << &this->pbase_type1::member
<< ", "
576 << &this->pbase_type2::member
<< ", and my base is at "
577 << static_cast<base_class
*>(this) << '.' << std::endl
;
581 // Destroy a good_class_2, noting the proper destruction order
582 good_class_2::~good_class_2
586 PRIVATE_REGISTER_DEATH( *this );
588 #if CONTROL_EXTRA_PRINTING
589 std::cout
<< "\tMy factors were at " << &this->pbase_type0::member
590 << ", " << &this->pbase_type1::member
<< ", "
591 << &this->pbase_type2::member
<< ", and my base was at "
592 << static_cast<base_class
*>(this) << '.' << std::endl
;