1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Test program for The Loki Library
4 // Copyright (c) 2008 Richard Sposato
5 // The copyright on this file is protected under the terms of the MIT license.
6 //
7 // Permission to use, copy, modify, distribute and sell this software for any
8 // purpose is hereby granted without fee, provided that the above copyright
9 // notice appear in all copies and that both that copyright notice and this
10 // permission notice appear in supporting documentation.
11 //
12 // The author makes no representations about the suitability of this software
13 // for any purpose. It is provided "as is" without express or implied warranty.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16 
17 // $Id$
18 
19 /// @file main.cpp This provides examples on how to use Loki's Checker facility.
20 
21 
22 // ----------------------------------------------------------------------------
23 
24 #include "../../include/loki/Checker.h"
25 
26 #include <stdexcept>
27 #include <iostream>
28 #include <vector>
29 
30 
31 #if !defined( nullptr )
32     #define nullptr NULL
33 #endif
34 
35 #if !defined( NULL )
36     #define NULL 0
37 #endif
38 
39 using namespace std;
40 
41 
42 // ----------------------------------------------------------------------------
43 
44 /* This class has 2 invariants.  The this pointer may never equal NULL, and the
45  value may not equal zero.
46  */
47 class Thingy
48 {
49 public:
50 
51     static void ChangeThat( void );
52 
53     static unsigned int GetThat( void );
54 
55     explicit Thingy( unsigned int value );
56 
57     Thingy( const Thingy & that );
58 
59     Thingy & operator = ( const Thingy & that );
60 
61     ~Thingy( void );
62 
63     void Swap( Thingy & that );
64 
65     bool operator == ( const Thingy & that ) const;
66 
67     void DoSomethingEvil( void );
68 
69     unsigned int GetValue( void ) const;
70 
71     unsigned int DoSomething( bool doThrow ) const;
72 
73     void DoSomethingElse( void ) const;
74 
75     void AddCount( unsigned int count );
76 
77     unsigned int GetCount( unsigned int index ) const;
78 
79     void ClearCounts( void );
80 
81 private:
82 
83     /// This is a static validator.
84     static bool StaticIsValid( void );
85 
86     /// This is a per-instance validator.
87     bool IsValid( void ) const;
88 
89     /// This can be used to validate pre-conditions and post-conditions.
90     bool IsValidEmpty( void ) const;
91 
92     /// This can be used to validate pre-conditions and post-conditions.
93     bool IsValidFull( void ) const;
94 
95     // These lines show how to declare checkers for non-static functions in a host class.
96     typedef ::Loki::ContractChecker< Thingy, ::Loki::CheckForNoThrow  > CheckForNoThrow;
97     typedef ::Loki::ContractChecker< Thingy, ::Loki::CheckForNoChangeOrThrow > CheckForNoChangeOrThrow;
98     typedef ::Loki::ContractChecker< Thingy, ::Loki::CheckForNoChange > CheckForNoChange;
99     typedef ::Loki::ContractChecker< Thingy, ::Loki::CheckForEquality > CheckForEquality;
100     typedef ::Loki::ContractChecker< Thingy, ::Loki::CheckForNothing  > CheckInvariants;
101 
102     // These lines show how to declare checkers for static functions of a host class.
103     typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNoThrow  > CheckStaticForNoThrow;
104     typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNothing  > CheckStaticInvariants;
105 
106     typedef ::std::vector< unsigned int > IntBlock;
107 
108     static unsigned int s_value;
109 
110     unsigned int m_value;
111 
112     IntBlock m_counts;
113 
114 };
115 
116 unsigned int Thingy::s_value = 10;
117 
118 // ----------------------------------------------------------------------------
119 
120 // This example shows how static functions can use a no-throw checkers.
ChangeThat(void)121 void Thingy::ChangeThat( void )
122 {
123     CheckStaticForNoThrow checker( &Thingy::StaticIsValid );
124     (void)checker;
125     s_value--;
126 }
127 
128 // ----------------------------------------------------------------------------
129 
130 // This example shows how static functions can use an invariant checker.
GetThat(void)131 unsigned int Thingy::GetThat( void )
132 {
133     CheckStaticInvariants checker( &Thingy::StaticIsValid );
134     (void)checker;
135     return s_value;
136 }
137 
138 // ----------------------------------------------------------------------------
139 
140 // This example shows how ctors can use an invariant checker.
Thingy(unsigned int value)141 Thingy::Thingy( unsigned int value ) :
142     m_value( value ),
143     m_counts()
144 {
145     CheckInvariants checker( this, &Thingy::IsValid );
146     (void)checker;
147 }
148 
149 // ----------------------------------------------------------------------------
150 
Thingy(const Thingy & that)151 Thingy::Thingy( const Thingy & that ) :
152     m_value( that.m_value ),
153     m_counts( that.m_counts )
154 {
155     CheckInvariants checker( this, &Thingy::IsValid );
156     (void)checker;
157 }
158 
159 // ----------------------------------------------------------------------------
160 
operator =(const Thingy & that)161 Thingy & Thingy::operator = ( const Thingy & that )
162 {
163     CheckInvariants checker( this, &Thingy::IsValid );
164     (void)checker;
165     if ( &that != this )
166     {
167         Thingy temp( that );
168         temp.Swap( *this );
169     }
170     return *this;
171 }
172 
173 // ----------------------------------------------------------------------------
174 
175 // A destructor really doesn't need a checker, but does need to confirm
176 // the object is valid at the start of the destructor.
~Thingy(void)177 Thingy::~Thingy( void )
178 {
179     assert( IsValid() );
180 }
181 
182 // ----------------------------------------------------------------------------
183 
184 // A swap function gets 2 checkers - one for this, and another for that.
Swap(Thingy & that)185 void Thingy::Swap( Thingy & that )
186 {
187     CheckInvariants checker1( this, &Thingy::IsValid );
188     (void)checker1;
189     CheckInvariants checker2( &that, &Thingy::IsValid );
190     (void)checker2;
191 
192     const IntBlock counts( m_counts );
193     m_counts = that.m_counts;
194     that.m_counts = counts;
195     const unsigned int value = m_value;
196     m_value = that.m_value;
197     that.m_value = value;
198 }
199 
200 // ----------------------------------------------------------------------------
201 
operator ==(const Thingy & that) const202 bool Thingy::operator == ( const Thingy & that ) const
203 {
204     return ( m_value == that.m_value );
205 }
206 
207 // ----------------------------------------------------------------------------
208 
DoSomethingEvil(void)209 void Thingy::DoSomethingEvil( void )
210 {
211     m_value = 0;
212 }
213 
214 // ----------------------------------------------------------------------------
215 
216 // This example shows how to use the no-throw checker.
GetValue(void) const217 unsigned int Thingy::GetValue( void ) const
218 {
219     CheckForNoThrow checker( this, &Thingy::IsValid );
220     (void)checker;
221     return m_value;
222 }
223 
224 // ----------------------------------------------------------------------------
225 
226 // This example shows how to use the equality checker.
DoSomething(bool doThrow) const227 unsigned int Thingy::DoSomething( bool doThrow ) const
228 {
229     CheckForEquality checker( this, &Thingy::IsValid );
230     (void)checker;
231     if ( doThrow )
232         throw ::std::logic_error( "Test Exception." );
233     return m_value;
234 }
235 
236 // ----------------------------------------------------------------------------
237 
238 // This example shows how to use the no-change checker.
DoSomethingElse(void) const239 void Thingy::DoSomethingElse( void ) const
240 {
241     CheckForNoChange checker( this, &Thingy::IsValid );
242     (void)checker;
243 }
244 
245 // ----------------------------------------------------------------------------
246 
AddCount(unsigned int count)247 void Thingy::AddCount( unsigned int count )
248 {
249     // This function's checker cares about class invariants and post-conditions,
250     // but does not need to check pre-conditions, so it passes in a nullptr for
251     // the pre-condition validator.  Ths post-condition validator just makes sure
252     // the container has at least 1 element.
253     CheckInvariants checker( this, &Thingy::IsValid, nullptr, &Thingy::IsValidFull );
254     m_counts.push_back( count );
255 }
256 
257 // ----------------------------------------------------------------------------
258 
GetCount(unsigned int index) const259 unsigned int Thingy::GetCount( unsigned int index ) const
260 {
261     // This function's checker cares about class invariants and both the pre- and
262     // post-conditions, so it passes in pointers for all 3 validators.  The pre-
263     // and post-conditions are both about making sure the container is not empty.
264     CheckForNoChangeOrThrow checker( this, &Thingy::IsValid, &Thingy::IsValidFull, &Thingy::IsValidFull );
265     if ( m_counts.size() <= index )
266         return 0;
267     const unsigned int count = m_counts[ index ];
268     return count;
269 }
270 
271 // ----------------------------------------------------------------------------
272 
ClearCounts(void)273 void Thingy::ClearCounts( void )
274 {
275     // This function's checker cares about class invariants and post-conditions,
276     // but does not need to check pre-conditions, so it passes in a nullptr for
277     // the pre-condition validator.  Ths post-condition validator just makes sure
278     // the container has no elements.
279     CheckForNoThrow checker( this, &Thingy::IsValid, nullptr, &Thingy::IsValidEmpty );
280     m_counts.clear();
281 }
282 
283 // ----------------------------------------------------------------------------
284 
285 // This is a static validator.
StaticIsValid(void)286 bool Thingy::StaticIsValid( void )
287 {
288     assert( s_value != 0 );
289     return true;
290 }
291 
292 // ----------------------------------------------------------------------------
293 
294 // This is a per-instance validator.
IsValid(void) const295 bool Thingy::IsValid( void ) const
296 {
297     assert( nullptr != this );
298     assert( m_value != 0 );
299     return true;
300 }
301 
302 // ----------------------------------------------------------------------------
303 
IsValidEmpty(void) const304 bool Thingy::IsValidEmpty( void ) const
305 {
306     assert( nullptr != this );
307     assert( m_counts.size() == 0 );
308     return true;
309 }
310 
311 // ----------------------------------------------------------------------------
312 
IsValidFull(void) const313 bool Thingy::IsValidFull( void ) const
314 {
315     assert( nullptr != this );
316     assert( m_counts.size() != 0 );
317     return true;
318 }
319 
320 // ----------------------------------------------------------------------------
321 
322 // This is a validator function called by checkers inside standalone functions.
AllIsValid(void)323 bool AllIsValid( void )
324 {
325     assert( Thingy::GetThat() != 0 );
326     return true;
327 }
328 
329 // ----------------------------------------------------------------------------
330 
331 // These lines show how to declare checkers for standalone functions.
332 typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNoThrow  > CheckStaticForNoThrow;
333 typedef ::Loki::StaticChecker< ::Loki::CheckStaticForNothing  > CheckStaticInvariants;
334 
335 // ----------------------------------------------------------------------------
336 
DoSomething(void)337 void DoSomething( void )
338 {
339     // This example shows how to use a checker in a stand-alone function.
340     CheckStaticForNoThrow checker( &AllIsValid );
341     (void)checker;
342     Thingy::ChangeThat();
343 }
344 
345 // ----------------------------------------------------------------------------
346 
ThrowTest(void)347 void ThrowTest( void )
348 {
349     Thingy thingy( 10 );
350     throw ::std::logic_error( "Will Thingy assert during an exception?" );
351 }
352 
353 // ----------------------------------------------------------------------------
354 
main(unsigned int argc,const char * const argv[])355 int main( unsigned int argc, const char * const argv[] )
356 {
357 
358     try
359     {
360         cout << "Just before call to ThrowTest." << endl;
361         ThrowTest();
362         cout << "Just after call to ThrowTest." << endl;
363     }
364     catch ( const ::std::logic_error & ex )
365     {
366         cout << "Caught an exception!  " << ex.what() << endl;
367     }
368     catch ( const ::std::exception & ex )
369     {
370         cout << "Caught an exception!  " << ex.what() << endl;
371     }
372     catch ( ... )
373     {
374         cout << "Caught an exception!" << endl;
375     }
376 
377     unsigned int count = 0;
378     try
379     {
380         cout << "Running basic tests with Thingy." << endl;
381         // First do some tests on class member functions.
382         Thingy t1( 1 );
383         t1.DoSomething( false );
384         Thingy t2( 2 );
385         t2.DoSomething( true );
386         cout << "Done with basic tests with Thingy." << endl;
387     }
388     catch ( const ::std::logic_error & ex )
389     {
390         cout << "Caught an exception!  " << ex.what() << endl;
391     }
392     catch ( const ::std::exception & ex )
393     {
394         cout << "Caught an exception!  " << ex.what() << endl;
395     }
396     catch ( ... )
397     {
398         cout << "Caught an exception!" << endl;
399     }
400 
401     try
402     {
403         Thingy t1( 1 );
404         cout << "Now running tests with Thingy counts." << endl;
405         // These lines will exercise the functions with pre- and post-conditions.
406         t1.AddCount( 11 );
407         t1.AddCount( 13 );
408         t1.AddCount( 17 );
409         t1.AddCount( 19 );
410         count = t1.GetCount( 3 );
411         assert( count == 19 );
412         count = t1.GetCount( 0 );
413         assert( count == 11 );
414         t1.ClearCounts();
415         cout << "Done with tests with Thingy counts." << endl;
416     }
417     catch ( const ::std::logic_error & ex )
418     {
419         cout << "Caught an exception!  " << ex.what() << endl;
420     }
421     catch ( const ::std::exception & ex )
422     {
423         cout << "Caught an exception!  " << ex.what() << endl;
424     }
425     catch ( ... )
426     {
427         cout << "Caught an exception!" << endl;
428     }
429 
430     try
431     {
432         cout << "Now run tests on static member functions" << endl;
433         // Next do some tests with static member functions.
434         Thingy::ChangeThat();
435         const unsigned int value = Thingy::GetThat();
436         assert( value != 0 );
437         cout << "Done with tests on static member functions" << endl;
438     }
439     catch ( const ::std::logic_error & ex )
440     {
441         cout << "Caught an exception!  " << ex.what() << endl;
442     }
443     catch ( const ::std::exception & ex )
444     {
445         cout << "Caught an exception!  " << ex.what() << endl;
446     }
447     catch ( ... )
448     {
449         cout << "Caught an exception!" << endl;
450     }
451 
452     try
453     {
454         cout << "Now run test on a standalone function." << endl;
455         // Then do a test with a standalone function.
456         DoSomething();
457         cout << "Done with test on a standalone function." << endl;
458     }
459     catch ( const ::std::exception & ex )
460     {
461         cout << "Caught an exception!  " << ex.what() << endl;
462     }
463     catch ( ... )
464     {
465         cout << "Caught an exception!" << endl;
466     }
467 
468     cout << "All done!" << endl;
469     return 0;
470 }
471 
472 // ----------------------------------------------------------------------------
473