Logo  0.95.0-final
Finite Element Embedded Library and Language in C++
Feel++ Feel++ on Github Feel++ community
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
smartassert.hpp
1 /* -*- mode: c++; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; show-trailing-whitespace: t -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4
2 
3  This file is part of the Feel library
4 
5  Author(s): Christophe Prud'homme <christophe.prudhomme@feelpp.org>
6  Date: 2005-01-16
7 
8  Copyright (C) 2009 Université de Grenoble 1
9  Copyright (C) 2005,2006 EPFL
10 
11  This library is free software; you can redistribute it and/or
12  modify it under the terms of the GNU Lesser General Public
13  License as published by the Free Software Foundation; either
14  version 3.0 of the License, or (at your option) any later version.
15 
16  This library is distributed in the hope that it will be useful,
17  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  Lesser General Public License for more details.
20 
21  You should have received a copy of the GNU Lesser General Public
22  License along with this library; if not, write to the Free Software
23  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 */
30 #if !defined(SMART_ASSERT_H)
31 #define SMART_ASSERT_H
32 
33 #include <string>
34 #include <iostream>
35 #include <sstream>
36 #include <utility>
37 #include <vector>
38 #include <map>
39 #include <glog/logging.h>
40 
41 namespace Feel
42 {
43 enum
44 {
45  lvl_info = google::GLOG_INFO,
46 
47  // default behavior - just loggs this assert
48  // (a message is shown to the user to the console)
49  lvl_warn = google::GLOG_WARNING,
50 
51  // default behavior - asks the user what to do:
52  // Ignore/ Retry/ etc.
53  lvl_debug = google::GLOG_INFO,
54 
55  // default behavior - throws a SmartAssert_error
56  lvl_error = google::GLOG_ERROR,
57 
58  // default behavior - dumps all assert context to console,
59  // and aborts
60  lvl_fatal = google::GLOG_FATAL
61 };
62 
63 
64 
71 {
72  typedef std::string string;
73 public:
74  AssertContext() : M_level( lvl_info )
75  {}
76 
77  // where the assertion failed: file & line
78  void setFileLine( const char * file, int line )
79  {
80  M_file = file;
81  M_line = line;
82  }
83  const string & getContextFile() const
84  {
85  return M_file;
86  }
87  int getContextLine() const
88  {
89  return M_line;
90  }
91 
92  // get/ set expression
93  void setExpression( const string & str )
94  {
95  M_expression = str;
96  }
97  const string & expression() const
98  {
99  return M_expression;
100  }
101 
102  typedef std::pair< string, string> val_and_str;
103  typedef std::vector< val_and_str> vals_array;
104  // return values array as a vector of pairs:
105  // [Value, corresponding string]
106  const vals_array & get_vals_array() const
107  {
108  return M_vals;
109  }
110  // adds one value and its corresponding string
111  void add_val( const string & val, const string & str )
112  {
113  M_vals.push_back( val_and_str( val, str ) );
114  }
115 
116  // get/set level of assertion
117  void setLevel( int nLevel )
118  {
119  M_level = nLevel;
120  }
121  int get_level() const
122  {
123  return M_level;
124  }
125 
126  // get/set (user-friendly) message
127  void setLevelMsg( const char * strMsg )
128  {
129  if ( strMsg )
130  M_msg = strMsg;
131 
132  else
133  M_msg.erase();
134  }
135  const string & get_level_msg() const
136  {
137  return M_msg;
138  }
139 
140 private:
141  // where the assertion occured
142  string M_file;
143  int M_line;
144 
145  // expression and values
146  string M_expression;
147  vals_array M_vals;
148 
149  // level and message
150  int M_level;
151  string M_msg;
152 };
153 
154 
155 namespace SmartAssert
156 {
157 
158 typedef void ( *assert_function_type )( const AssertContext & context );
159 
160 // helpers
161 std::string getTypeofLevel( int nLevel );
162 void dumpContextSummary( const AssertContext & context, std::ostream & out );
163 void dumpContextDetail( const AssertContext & context, std::ostream & out );
164 
165 // defaults
166 void defaultWarnHandler( const AssertContext & context );
167 void defaultDebugHandler( const AssertContext & context );
168 void defaultErrorHandler( const AssertContext & context );
169 void defaultFatalHandler( const AssertContext & context );
170 void defaultLogger( const AssertContext & context );
171 
172 } // namespace SmartAssert
173 
174 namespace Private
175 {
176 void initAssert();
177 void setDefaultLogStream( std::ostream & out );
178 void setDefaultLogName( const char * str );
179 
180 // allows finding if a value is of type 'const char *'
181 // and is null; if so, we cannot print it to an ostream
182 // directly!!!
183 template< class T>
184 struct isNullFinder
185 {
186  bool is( const T & ) const
187  {
188  return false;
189  }
190 };
191 
192 template<>
193 struct isNullFinder< char*>
194 {
195  bool is( char * const & val )
196  {
197  return val == 0;
198  }
199 };
200 
201 template<>
202 struct isNullFinder< const char*>
203 {
204  bool is( const char * const & val )
205  {
206  return val == 0;
207  }
208 };
209 
210 
211 } // namespace Private
212 
213 
214 struct Assert
215 {
216  typedef SmartAssert::assert_function_type assert_function_type;
217 
218  // helpers, in order to be able to compile the code
219  Assert & SMART_ASSERT_A;
220  Assert & SMART_ASSERT_B;
221 
222  Assert( const char * expr )
223  : SMART_ASSERT_A( *this ),
224  SMART_ASSERT_B( *this ),
225  M_needs_handling( true )
226  {
227  M_context.setExpression( expr );
228 
229  if ( ( logger() == 0 ) || handlers().size() < 4 )
230  {
231  // used before main!
232  Private::initAssert();
233  }
234  }
235 
236  Assert( const Assert & other )
237  : SMART_ASSERT_A( *this ),
238  SMART_ASSERT_B( *this ),
239  M_context( other.M_context ),
240  M_needs_handling( true )
241  {
242  other.M_needs_handling = false;
243  }
244 
245  ~Assert()
246  {
247  if ( M_needs_handling )
248  handleAssert();
249  }
250 
251  template< class type>
252  Assert & printCurrentValue( const type & val, const char * msg );
253 
254  Assert & printContext( const char * file, int line )
255  {
256  M_context.setFileLine( file, line );
257  return *this;
258  }
259 
260  Assert & msg( const char * strMsg )
261  {
262  M_context.setLevelMsg( strMsg );
263  return *this;
264  }
265 
266  Assert & level( int nLevel, const char * strMsg = 0 )
267  {
268  M_context.setLevel( nLevel );
269  M_context.setLevelMsg( strMsg );
270  return *this;
271  }
272 
273  Assert & info( const char * strMsg = 0 )
274  {
275  return level( lvl_info, strMsg );
276  }
277  Assert & warn( const char * strMsg = 0 )
278  {
279  return level( lvl_warn, strMsg );
280  }
281 
282  Assert & debug( const char * strMsg = 0 )
283  {
284  return level( lvl_debug, strMsg );
285  }
286 
287  Assert & error( const char * strMsg = 0 )
288  {
289  return level( lvl_error, strMsg );
290  }
291 
292  Assert & fatal( const char * strMsg = 0 )
293  {
294  return level( lvl_fatal, strMsg );
295  }
296 
297  // in this case, we set the default logger, and make it
298  // write everything to this file
299  static void setLog( const char * strFileName )
300  {
301  Private::setDefaultLogName( strFileName );
302  logger() = &SmartAssert::defaultLogger;
303  }
304 
305  // in this case, we set the default logger, and make it
306  // write everything to this log
307  static void setLog( std::ostream & out )
308  {
309  Private::setDefaultLogStream( out );
310  logger() = &SmartAssert::defaultLogger;
311  }
312 
313  static void setLog( assert_function_type log )
314  {
315  logger() = log;
316  }
317 
318  static void setHandler( int nLevel, assert_function_type handler )
319  {
320  handlers()[ nLevel] = handler;
321  }
322 
323 private:
324  // handles the current assertion.
325  void handleAssert()
326  {
327  logger()( M_context );
328  get_handler( M_context.get_level() )( M_context );
329  }
330 
331  /*
332  IMPORTANT NOTE:
333  The only reason logger & handlers are functions, are
334  because you might use SMART_ASSERT before main().
335 
336  In this case, since they're statics, they might not
337  be initialized. However, making them functions
338  will make it work.
339  */
340 
341  // the log
342  static assert_function_type & logger()
343  {
344  static assert_function_type inst;
345  return inst;
346  }
347 
348  // the handler
349  typedef std::map< int, assert_function_type> handlers_collection;
350  static handlers_collection & handlers()
351  {
352  static handlers_collection inst;
353  return inst;
354  }
355 
356  static assert_function_type get_handler( int nLevel )
357  {
358  handlers_collection::const_iterator found = handlers().find( nLevel );
359 
360  if ( found != handlers().end() )
361  return found->second;
362 
363  else
364  // we always assume the debug handler has been set
365  return handlers().find( lvl_debug )->second;
366  }
367 
368 private:
369  AssertContext M_context;
370  mutable bool M_needs_handling;
371 
372 };
373 
374 template<class type>
375 Assert &
376 Assert::printCurrentValue( const type & _val, const char * _msg )
377 {
378  std::ostringstream out;
379 
380  Private::isNullFinder< type> f;
381  bool bIsNull = f.is( _val );
382 
383  if ( !bIsNull )
384  out << _val;
385 
386  else
387  // null string
388  out << "null";
389 
390  M_context.add_val( out.str(), _msg );
391  return *this;
392 }
393 
394 
395 namespace SmartAssert
396 {
397 inline ::Feel::Assert make_assert( const char * expr )
398 {
399  return ::Feel::Assert( expr );
400 }
401 } // namespace SmartAssert
402 
403 } // Feel Namespace
404 
405 #ifdef FEELPP_SMART_ASSERT_DEBUG_MODE
406 
407 #if FEELPP_SMART_ASSERT_DEBUG_MODE == 1
408 #define FEELPP_SMART_ASSERT_DEBUG
409 #else
410 #undef FEELPP_SMART_ASSERT_DEBUG
411 #endif
412 
413 #else
414 
415 // defaults
416 #ifndef NDEBUG
417 #define FEELPP_SMART_ASSERT_DEBUG
418 #else
419 #undef FEELPP_SMART_ASSERT_DEBUG
420 #endif
421 
422 #endif
423 
424 
425 #ifdef FEELPP_SMART_ASSERT_DEBUG
426 // "debug" mode
427 #define FEELPP_SMART_ASSERT( expr) \
428  if ( (expr) ) ; \
429  else ::Feel::SmartAssert::make_assert( #expr).printContext( __FILE__, __LINE__).SMART_ASSERT_A \
430 
431 
432 #else
433 // "release" mode
434 #define FEELPP_SMART_ASSERT( expr) \
435  if ( true ) ; \
436  else ::Feel::SmartAssert::make_assert( "").SMART_ASSERT_A \
437 
438 
439 #endif // ifdef FEELPP_SMART_ASSERT_DEBUG
440 
441 // FEELPP_ASSERT is a equivalent to FEELPP_SMART_ASSERT
442 #define FEELPP_ASSERT( expr) FEELPP_SMART_ASSERT(expr)
443 
444 
445 #define FEELPP_SMART_VERIFY( expr) \
446  if ( (expr) ) ; \
447  else ::Feel::SmartAssert::make_assert( #expr).error().printContext( __FILE__, __LINE__).SMART_ASSERT_A \
448 
449 #define FEELPP_VERIFY( expr) FEELPP_SMART_VERIFY(expr)
450 
451 
452 #define SMART_ASSERT_A(x) FEELPP_SMART_ASSERT_OP(x, B)
453 #define SMART_ASSERT_B(x) FEELPP_SMART_ASSERT_OP(x, A)
454 
455 #define FEELPP_SMART_ASSERT_OP(x, next) \
456  SMART_ASSERT_A.printCurrentValue((x), #x).SMART_ASSERT_ ## next \
457 
458 
459 
460 
461 #endif

Generated on Fri Oct 25 2013 14:24:24 for Feel++ by doxygen 1.8.4