1 /*************************************************************************** 2 * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht * 3 * Copyright (c) QuantStack * 4 * * 5 * Distributed under the terms of the BSD 3-Clause License. * 6 * * 7 * The full license is in the file LICENSE, distributed with this software. * 8 ****************************************************************************/ 9 10 #ifndef XTENSOR_EXCEPTION_HPP 11 #define XTENSOR_EXCEPTION_HPP 12 13 #include <iterator> 14 #include <sstream> 15 #include <stdexcept> 16 #include <string> 17 18 #include "xtensor_config.hpp" 19 20 namespace xt 21 { 22 23 /******************* 24 * broadcast_error * 25 *******************/ 26 27 class broadcast_error : public std::runtime_error 28 { 29 public: 30 broadcast_error(const char * msg)31 explicit broadcast_error(const char* msg) 32 : std::runtime_error(msg) 33 { 34 } 35 }; 36 37 template <class S1, class S2> 38 [[noreturn]] void throw_broadcast_error(const S1& lhs, const S2& rhs); 39 40 /********************* 41 * concatenate_error * 42 *********************/ 43 44 class concatenate_error : public std::runtime_error 45 { 46 public: 47 concatenate_error(const char * msg)48 explicit concatenate_error(const char* msg) 49 : std::runtime_error(msg) 50 { 51 } 52 }; 53 54 template <class S1, class S2> 55 [[noreturn]] void throw_concatenate_error(const S1& lhs, const S2& rhs); 56 57 /********************************** 58 * broadcast_error implementation * 59 **********************************/ 60 61 namespace detail 62 { 63 template <class S1, class S2> shape_error_message(const S1 & lhs,const S2 & rhs)64 inline std::string shape_error_message(const S1& lhs, const S2& rhs) 65 { 66 std::ostringstream buf("Incompatible dimension of arrays:", std::ios_base::ate); 67 68 buf << "\n LHS shape = ("; 69 using size_type1 = typename S1::value_type; 70 std::ostream_iterator<size_type1> iter1(buf, ", "); 71 std::copy(lhs.cbegin(), lhs.cend(), iter1); 72 73 buf << ")\n RHS shape = ("; 74 using size_type2 = typename S2::value_type; 75 std::ostream_iterator<size_type2> iter2(buf, ", "); 76 std::copy(rhs.cbegin(), rhs.cend(), iter2); 77 buf << ")"; 78 79 return buf.str(); 80 } 81 } 82 83 #ifdef NDEBUG 84 // Do not inline this function 85 template <class S1, class S2> throw_broadcast_error(const S1 &,const S2 &)86 [[noreturn]] void throw_broadcast_error(const S1&, const S2&) 87 { 88 XTENSOR_THROW(broadcast_error, "Incompatible dimension of arrays, compile in DEBUG for more info"); 89 } 90 #else 91 template <class S1, class S2> throw_broadcast_error(const S1 & lhs,const S2 & rhs)92 [[noreturn]] void throw_broadcast_error(const S1& lhs, const S2& rhs) 93 { 94 std::string msg = detail::shape_error_message(lhs, rhs); 95 XTENSOR_THROW(broadcast_error, msg.c_str()); 96 } 97 #endif 98 99 /************************************ 100 * concatenate_error implementation * 101 ************************************/ 102 103 #ifdef NDEBUG 104 // Do not inline this function 105 template <class S1, class S2> throw_concatenate_error(const S1 &,const S2 &)106 [[noreturn]] void throw_concatenate_error(const S1&, const S2&) 107 { 108 XTENSOR_THROW(concatenate_error, "Incompatible dimension of arrays, compile in DEBUG for more info"); 109 } 110 #else 111 template <class S1, class S2> throw_concatenate_error(const S1 & lhs,const S2 & rhs)112 [[noreturn]] void throw_concatenate_error(const S1& lhs, const S2& rhs) 113 { 114 std::string msg = detail::shape_error_message(lhs, rhs); 115 XTENSOR_THROW(concatenate_error, msg.c_str()); 116 } 117 #endif 118 119 /******************* 120 * transpose_error * 121 *******************/ 122 123 class transpose_error : public std::runtime_error 124 { 125 public: 126 transpose_error(const char * msg)127 explicit transpose_error(const char* msg) 128 : std::runtime_error(msg) 129 { 130 } 131 }; 132 133 /*************** 134 * check_index * 135 ***************/ 136 137 template <class S, class... Args> 138 void check_index(const S& shape, Args... args); 139 140 template <class S, class It> 141 void check_element_index(const S& shape, It first, It last); 142 143 namespace detail 144 { 145 template <class S, std::size_t dim> check_index_impl(const S &)146 inline void check_index_impl(const S&) 147 { 148 } 149 150 template <class S, std::size_t dim, class T, class... Args> check_index_impl(const S & shape,T arg,Args...args)151 inline void check_index_impl(const S& shape, T arg, Args... args) 152 { 153 if (std::size_t(arg) >= std::size_t(shape[dim]) && shape[dim] != 1) 154 { 155 XTENSOR_THROW(std::out_of_range, 156 "index " + std::to_string(arg) + " is out of bounds for axis " + 157 std::to_string(dim) + " with size " + std::to_string(shape[dim])); 158 } 159 check_index_impl<S, dim + 1>(shape, args...); 160 } 161 } 162 163 template <class S> check_index(const S &)164 inline void check_index(const S&) 165 { 166 } 167 168 template <class S, class Arg, class... Args> check_index(const S & shape,Arg arg,Args...args)169 inline void check_index(const S& shape, Arg arg, Args... args) 170 { 171 constexpr std::size_t nargs = sizeof...(Args) + 1; 172 if (nargs == shape.size()) 173 { 174 detail::check_index_impl<S, 0>(shape, arg, args...); 175 } 176 else if (nargs > shape.size()) 177 { 178 // Too many arguments: drop the first 179 check_index(shape, args...); 180 } 181 else 182 { 183 // Too few arguments: ignore the beginning of the shape 184 auto it = shape.end() - nargs; 185 detail::check_index_impl<decltype(it), 0>(it, arg, args...); 186 } 187 } 188 189 template <class S, class It> check_element_index(const S & shape,It first,It last)190 inline void check_element_index(const S& shape, It first, It last) 191 { 192 using value_type = typename std::iterator_traits<It>::value_type; 193 using size_type = typename S::size_type; 194 auto dst = static_cast<size_type>(last - first); 195 It efirst = last - static_cast<std::ptrdiff_t>((std::min)(shape.size(), dst)); 196 std::size_t axis = 0; 197 198 while (efirst != last) 199 { 200 if (*efirst >= value_type(shape[axis]) && shape[axis] != 1) 201 { 202 XTENSOR_THROW(std::out_of_range, 203 "index " + std::to_string(*efirst) + 204 " is out of bounds for axis " + 205 std::to_string(axis) + " with size " + 206 std::to_string(shape[axis])); 207 } 208 ++efirst, ++axis; 209 } 210 } 211 212 /******************* 213 * check_dimension * 214 *******************/ 215 216 template <class S, class... Args> check_dimension(const S & shape,Args...)217 inline void check_dimension(const S& shape, Args...) 218 { 219 if (sizeof...(Args) > shape.size()) 220 { 221 XTENSOR_THROW(std::out_of_range, 222 "Number of arguments (" + std::to_string(sizeof...(Args)) + 223 ") is greater than the number of dimensions (" + 224 std::to_string(shape.size()) + ")"); 225 } 226 } 227 228 /**************** 229 * check_access * 230 ****************/ 231 232 template <class S, class... Args> check_access(const S & shape,Args...args)233 inline void check_access(const S& shape, Args... args) 234 { 235 check_dimension(shape, args...); 236 check_index(shape, args...); 237 } 238 239 #if (defined(XTENSOR_ENABLE_ASSERT) && !defined(XTENSOR_DISABLE_EXCEPTIONS)) 240 #define XTENSOR_TRY(expr) XTENSOR_TRY_IMPL(expr, __FILE__, __LINE__) 241 #define XTENSOR_TRY_IMPL(expr, file, line) \ 242 try \ 243 { \ 244 expr; \ 245 } \ 246 catch (std::exception& e) \ 247 { \ 248 XTENSOR_THROW(std::runtime_error, \ 249 std::string(file) + ':' + std::to_string(line) + \ 250 ": check failed\n\t" + std::string(e.what())); \ 251 } 252 #else 253 #define XTENSOR_TRY(expr) 254 #endif 255 256 #ifdef XTENSOR_ENABLE_ASSERT 257 #define XTENSOR_ASSERT(expr) XTENSOR_ASSERT_IMPL(expr, __FILE__, __LINE__) 258 #define XTENSOR_ASSERT_IMPL(expr, file, line) \ 259 if (!(expr)) \ 260 { \ 261 XTENSOR_THROW(std::runtime_error, \ 262 std::string(file) + ':' + std::to_string(line) + \ 263 ": assertion failed (" #expr ") \n\t"); \ 264 } 265 #else 266 #define XTENSOR_ASSERT(expr) 267 #endif 268 269 #ifdef XTENSOR_ENABLE_CHECK_DIMENSION 270 #define XTENSOR_CHECK_DIMENSION(S, ARGS) XTENSOR_TRY(check_dimension(S, ARGS)) 271 #else 272 #define XTENSOR_CHECK_DIMENSION(S, ARGS) 273 #endif 274 275 #ifdef XTENSOR_ENABLE_ASSERT 276 #define XTENSOR_ASSERT_MSG(expr, msg) \ 277 if (!(expr)) \ 278 { \ 279 XTENSOR_THROW(std::runtime_error, \ 280 std::string("Assertion error!\n") + msg + \ 281 "\n " + __FILE__ + '(' + std::to_string(__LINE__) + \ 282 ")\n"); \ 283 } 284 #else 285 #define XTENSOR_ASSERT_MSG(expr, msg) 286 #endif 287 288 #define XTENSOR_PRECONDITION(expr, msg) \ 289 if (!(expr)) \ 290 { \ 291 XTENSOR_THROW(std::runtime_error, \ 292 std::string("Precondition violation!\n") + msg + \ 293 "\n " + __FILE__ + '(' + std::to_string(__LINE__) + \ 294 ")\n"); \ 295 } 296 } 297 #endif // XEXCEPTION_HPP 298