• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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