xexception.hpp 13 KB

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