1 //
2 // Copyright (C)  2001-2013 Greg Landrum, Randal M. Henne and Rational Discovery
3 // LLC
4 //
5 //  @@ All Rights Reserved @@
6 //  This file is part of the RDKit.
7 //  The contents are covered by the terms of the BSD license
8 //  which is included in the file license.txt, found at the root
9 //  of the RDKit source tree.
10 //
11 
12 #include <RDGeneral/export.h>
13 #ifndef __RD_INVARIANT_H__
14 #define __RD_INVARIANT_H__
15 
16 #include <cassert>
17 #include <string>
18 #include <iostream>
19 #include <stdexcept>
20 
21 #include "BoostStartInclude.h"
22 #include <RDGeneral/RDLog.h>
23 #include "BoostEndInclude.h"
24 
25 #ifdef RDDEBUG
26 // Enable RDDEBUG for testing whether rdcast
27 //  conversions are within numerical limits
28 #include <RDGeneral/BoostStartInclude.h>
29 #include <boost/numeric/conversion/cast.hpp>
30 #include <RDGeneral/BoostEndInclude.h>
31 #endif
32 //
33 // What if no invariant method is defined?
34 //
35 #if !defined INVARIANT_EXCEPTION_METHOD && !defined INVARIANT_ASSERT_METHOD && \
36     !defined INVARIANT_SILENT_METHOD
37 #define INVARIANT_EXCEPTION_METHOD 1
38 #endif
39 
40 //
41 // What if an invariant method is defined, but none are true?
42 //
43 #if !INVARIANT_EXCEPTION_METHOD && !INVARIANT_ASSERT_METHOD && \
44     !INVARIANT_SILENT_METHOD
45 #undef INVARIANT_EXCEPTION_METHOD
46 #define INVARIANT_EXCEPTION_METHOD 1
47 #endif
48 
49 namespace Invar {
50 
51 class RDKIT_RDGENERAL_EXPORT Invariant : public std::runtime_error {
52  public:
Invariant(const char * prefix,const char * mess,const char * expr,const char * const file,int line)53   Invariant(const char* prefix, const char* mess, const char* expr,
54             const char* const file, int line)
55       : std::runtime_error(prefix),
56         mess_d(mess),
57         expr_d(expr),
58         prefix_d(prefix),
59         file_dp(file),
60         line_d(line) {}
Invariant(const char * prefix,const std::string & mess,const char * expr,const char * const file,int line)61   Invariant(const char* prefix, const std::string& mess, const char* expr,
62             const char* const file, int line)
63       : std::runtime_error(prefix),
64         mess_d(mess.c_str()),
65         expr_d(expr),
66         prefix_d(prefix),
67         file_dp(file),
68         line_d(line) {}
~Invariant()69   ~Invariant() noexcept {};
70 
what()71   const char* what() const noexcept override { return mess_d.c_str(); }
72 
getFile()73   const char* getFile() const { return file_dp; }
74 
getExpression()75   std::string getExpression() const { return expr_d; }
76 
getLine()77   int getLine() const { return line_d; }
78 
79   std::string toString() const;
80   std::string toUserString() const;  // strips build info, adds version
81 
82  private:
83   std::string mess_d, expr_d, prefix_d;
84 
85   const char* const file_dp;
86 
87   int line_d;
88 };
89 RDKIT_RDGENERAL_EXPORT std::ostream& operator<<(std::ostream& s,
90                                                 const Invariant& inv);
91 }  // end of namespace Invar
92 
93 #define ASSERT_INVARIANT(expr, mess) assert(expr)
94 
95 //
96 // Set desired reporting method
97 //
98 
99 #if INVARIANT_EXCEPTION_METHOD
100 
101 #define CHECK_INVARIANT(expr, mess)                                    \
102   if (!(expr)) {                                                       \
103     Invar::Invariant inv("Invariant Violation", mess, #expr, __FILE__, \
104                          __LINE__);                                    \
105     BOOST_LOG(rdErrorLog) << "\n\n****\n" << inv << "****\n\n";        \
106     throw inv;                                                         \
107   }
108 
109 #define PRECONDITION(expr, mess)                                           \
110   if (!(expr)) {                                                           \
111     Invar::Invariant inv("Pre-condition Violation", mess, #expr, __FILE__, \
112                          __LINE__);                                        \
113     BOOST_LOG(rdErrorLog) << "\n\n****\n" << inv << "****\n\n";            \
114     throw inv;                                                             \
115   }
116 
117 #define POSTCONDITION(expr, mess)                                           \
118   if (!(expr)) {                                                            \
119     Invar::Invariant inv("Post-condition Violation", mess, #expr, __FILE__, \
120                          __LINE__);                                         \
121     BOOST_LOG(rdErrorLog) << "\n\n****\n" << inv << "****\n\n";             \
122     throw inv;                                                              \
123   }
124 
125 #define UNDER_CONSTRUCTION(fn)                                        \
126   Invar::Invariant inv("Incomplete Code",                             \
127                        "This routine is still under development", fn, \
128                        __FILE__, __LINE__);                           \
129   BOOST_LOG(rdErrorLog) << "\n\n****\n" << inv << "****\n\n";         \
130   throw inv;
131 
132 #define RANGE_CHECK(lo, x, hi)                                              \
133   if ((lo) > (hi) || (x) < (lo) || (x) > (hi)) {                            \
134     std::stringstream errstr;                                               \
135     errstr << lo << " <= " << x << " <= " << hi;                            \
136     Invar::Invariant inv("Range Error", #x, errstr.str().c_str(), __FILE__, \
137                          __LINE__);                                         \
138     BOOST_LOG(rdErrorLog) << "\n\n****\n" << inv << "****\n\n";             \
139     throw inv;                                                              \
140   }
141 
142 #define URANGE_CHECK(x, hi)                                                 \
143   if (x >= (hi)) {                                                          \
144     std::stringstream errstr;                                               \
145     errstr << x << " < " << hi;                                             \
146     Invar::Invariant inv("Range Error", #x, errstr.str().c_str(), __FILE__, \
147                          __LINE__);                                         \
148     BOOST_LOG(rdErrorLog) << "\n\n****\n" << inv << "****\n\n";             \
149     throw inv;                                                              \
150   }
151 
152 #define TEST_ASSERT(expr)                                             \
153   if (!(expr)) {                                                      \
154     Invar::Invariant inv("Test Assert", "Expression Failed: ", #expr, \
155                          __FILE__, __LINE__);                         \
156     BOOST_LOG(rdErrorLog) << "\n\n****\n" << inv << "****\n\n";       \
157     throw inv;                                                        \
158   }
159 
160 #elif INVARIANT_ASSERT_METHOD
161 
162 #define CHECK_INVARIANT(expr, mess) assert(expr);
163 #define PRECONDITION(expr, mess) assert(expr);
164 #define POSTCONDITION(expr, mess) assert(expr);
165 #define UNDER_CONSTRUCTION(fn) assert(0);
166 #define RANGE_CHECK(lo, x, hi) \
167   assert((lo) <= (hi) && (x) >= (lo) && (x) <= (hi));
168 #define URANGE_CHECK(lo, x, hi) assert((hi > 0) && (x < hi));
169 #define TEST_ASSERT(expr) assert(expr);
170 
171 #elif INVARIANT_SILENT_METHOD
172 
173 #define CHECK_INVARIANT(expr, mess)
174 #define PRECONDITION(expr, mess)
175 #define POSTCONDITION(expr, mess)
176 #define UNDER_CONSTRUCTION(fn)
177 #define RANGE_CHECK(lo, x, hi)
178 #define URANGE_CHECK(x, hi)
179 #define TEST_ASSERT(expr)
180 
181 #endif
182 
183 #ifdef RDDEBUG
184 // use rdcast to convert between types
185 //  when RDDEBUG is defined, this checks for
186 //  validity (overflow, etc)
187 //  when RDDEBUG is off, the cast is a no-cost
188 //   static_cast
189 #define rdcast boost::numeric_cast
190 #else
191 #define rdcast static_cast
192 #endif
193 
194 // Silence warnings for unused params while
195 //   still indicating that they are unused
196 #define RDUNUSED_PARAM(x) (void)x;
197 
198 #endif
199