1 /** @file omassert.h
2  * @brief Various assertion macros.
3  */
4 /* Copyright (C) 2007,2008,2009,2013 Olly Betts
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  */
20 
21 /* The "om" prefix is a historical vestige dating back to the "Open Muscat"
22  * code base upon which Xapian is partly based.  It's preserved here only
23  * to avoid colliding with the ISO C "assert.h" header.
24  */
25 
26 #ifndef XAPIAN_INCLUDED_OMASSERT_H
27 #define XAPIAN_INCLUDED_OMASSERT_H
28 
29 /** Assert that a constant expression is non-zero.
30  *
31  *  If the assertion fails, compilation fails with an error.  There's no
32  *  run-time overhead for a compile-time assertion, so we always enable
33  *  them.
34  *
35  *  This macro must be used within a function (this is because we can only
36  *  portably generate a "unique" name using __LINE__, but then if this macro
37  *  is used in header files, multiple headers might be included from the same
38  *  source file and have CompileTimeAssert() at the same line number.
39  */
40 #if __cplusplus >= 201103L
41 // Under C++11, just use static_assert.
42 # define CompileTimeAssert(COND) static_assert(COND, #COND)
43 #else
44 # define CompileTimeAssert(COND)\
45     do {\
46 	typedef int xapian_compile_time_check_[(COND) ? 1 : -1];\
47 	xapian_compile_time_check_ xapian_compile_time_check_var_;\
48 	(void)xapian_compile_time_check_var_;\
49     } while (0)
50 #endif
51 
52 #ifndef XAPIAN_ASSERTIONS
53 // The configure script should always define XAPIAN_ASSERTIONS if it defines
54 // XAPIAN_ASSERTIONS_PARANOID.
55 # ifdef XAPIAN_ASSERTIONS_PARANOID
56 #  error XAPIAN_ASSERTIONS_PARANOID defined without XAPIAN_ASSERTIONS
57 # endif
58 #else
59 
60 #include <xapian/error.h>
61 
62 #include "str.h"
63 #include "utils.h" // For within_DBL_EPSILON().
64 
65 #define XAPIAN_ASSERT_LOCATION__(LINE,MSG) __FILE__":"#LINE": "#MSG
66 #define XAPIAN_ASSERT_LOCATION_(LINE,MSG) XAPIAN_ASSERT_LOCATION__(LINE,MSG)
67 #define XAPIAN_ASSERT_LOCATION(MSG) XAPIAN_ASSERT_LOCATION_(__LINE__,MSG)
68 
69 // Expensive (or potentially expensive) assertions can be marked as "Paranoid"
70 // - these can be disabled separately from other assertions to allow a build
71 // with assertions which still has good performance.
72 #ifdef XAPIAN_ASSERTIONS_PARANOID
73 # define AssertParanoid(COND) Assert(COND)
74 # define AssertRelParanoid(A,REL,B) AssertRel(A,REL,B)
75 # define AssertEqParanoid(A,B) AssertEq(A,B)
76 # define AssertEqDoubleParanoid(A,B) AssertEqDouble(A,B)
77 #endif
78 
79 /** Assert that condition COND is non-zero.
80  *
81  *  If this assertion fails, Xapian::AssertionError() will be thrown.
82  */
83 #define Assert(COND) \
84     do {\
85 	if (rare(!(COND)))\
86 	    throw Xapian::AssertionError(XAPIAN_ASSERT_LOCATION(COND));\
87     } while (0)
88 
89 /** Assert that A REL B is non-zero.
90  *
91  *  The intended usage is that REL is ==, !=, <=, <, >=, or >.
92  *
93  *  If this assertion fails, Xapian::AssertionError() will be thrown, and the
94  *  exception message will include the values of A and B.
95  */
96 #define AssertRel(A,REL,B) \
97     do {\
98 	if (rare(!((A) REL (B)))) {\
99 	    std::string xapian_assertion_msg(XAPIAN_ASSERT_LOCATION(A REL B));\
100 	    xapian_assertion_msg += " : values were ";\
101 	    xapian_assertion_msg += str(A);\
102 	    xapian_assertion_msg += " and ";\
103 	    xapian_assertion_msg += str(B);\
104 	    throw Xapian::AssertionError(xapian_assertion_msg);\
105 	}\
106     } while (0)
107 
108 /** Assert that A == B.
109  *
110  *  This is just a wrapper for AssertRel(A,==,B) and is provided partly for
111  *  historical reasons (we've have AssertEq() for ages) and partly because
112  *  it's convenient to have a shorthand for the most common relation which
113  *  we want to assert.
114  */
115 #define AssertEq(A,B) AssertRel(A,==,B)
116 
117 /// Assert two values differ by DBL_EPSILON or more.
118 #define AssertEqDouble(A,B) \
119     do {\
120 	using Xapian::Internal::within_DBL_EPSILON;\
121 	if (rare(within_DBL_EPSILON(A,B))) {\
122 	    std::string xapian_assertion_msg(XAPIAN_ASSERT_LOCATION(within_DBL_EPSILON(A,B)));\
123 	    xapian_assertion_msg += " : values were ";\
124 	    xapian_assertion_msg += str(A);\
125 	    xapian_assertion_msg += " and ";\
126 	    xapian_assertion_msg += str(B);\
127 	    throw Xapian::AssertionError(xapian_assertion_msg);\
128 	}\
129     } while (0)
130 
131 #endif
132 
133 // If assertions are disabled, set the macros to expand to (void)0 so that
134 // we get a compiler error in this case for assertions missing a trailing
135 // semicolon.  This avoids one source of compile errors in debug builds
136 // which don't manifest in non-debug builds.
137 
138 #ifndef Assert
139 # define Assert(COND) (void)0
140 # define AssertRel(A,REL,B) (void)0
141 # define AssertEq(A,B) (void)0
142 # define AssertEqDouble(A,B) (void)0
143 #endif
144 
145 #ifndef AssertParanoid
146 # define AssertParanoid(COND) (void)0
147 # define AssertRelParanoid(A,REL,B) (void)0
148 # define AssertEqParanoid(A,B) (void)0
149 # define AssertEqDoubleParanoid(A,B) (void)0
150 #endif
151 
152 /** A "compile-time" assertion.
153  *
154  *  STATIC_ASSERT must be used inside a function (not at the top level, or
155  *  in a class definition).
156  *
157  *  COND must be a compile-time constant expression.
158  *
159  *  If COND is false, the compiler will try to compile an array with negative
160  *  length, which is invalid (we don't use 0 as the length, since GCC allows
161  *  zero length arrays as an extension).  We name the array
162  *  "xapian_static_assert_failed" since this won't clash with any sanely named
163  *  variable, and it's likely to appear in the compiler error message and so
164  *  indicate to the developer what went wrong.
165  */
166 #define STATIC_ASSERT(COND) \
167     do { \
168 	char xapian_static_assert_failed[(COND) ? 1 : -1]; \
169 	(void)xapian_static_assert_failed; \
170     } while (0)
171 
172 /** Assert at compile-time that type TYPE is unsigned. */
173 #define STATIC_ASSERT_UNSIGNED_TYPE(TYPE) \
174     STATIC_ASSERT(static_cast<TYPE>(-1) > 0)
175 
176 /** Assert at compile-time that integer type T1 can hold any value which
177  *  integer type T2 can.
178  *
179  *  NB Doesn't currently work reliably for type bool (which is typically
180  *  incorrectly treated as equivalent to unsigned char or unsigned int).
181  *
182  *  If T1 is unsigned, T2 must be unsigned and have no more bits.
183  *
184  *  If T1 is signed, then T2 must have no more bits (if also signed) or
185  *  fewer bits (if unsigned).
186  */
187 #define STATIC_ASSERT_TYPE_DOMINATES(T1, T2) \
188     STATIC_ASSERT(static_cast<T1>(-1) > 0 ? \
189 	(static_cast<T2>(-1) > 0 && sizeof(T1) >= sizeof(T2)) : \
190 	(sizeof(T1) >= sizeof(T2) + (static_cast<T2>(-1) > 0)))
191 
192 #endif // XAPIAN_INCLUDED_OMASSERT_H
193