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