check.hpp
1 //*****************************************************************************
2 // Copyright 2017-2021 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //*****************************************************************************
16 
17 #pragma once
18 
19 #include <exception>
20 #include <sstream>
21 #include <vector>
22 
23 #include "ngraph/except.hpp"
24 
25 namespace ngraph
26 {
27  static inline std::ostream& write_all_to_stream(std::ostream& str) { return str; }
28  template <typename T, typename... TS>
29  static inline std::ostream& write_all_to_stream(std::ostream& str, const T& arg, TS&&... args)
30  {
31  return write_all_to_stream(str << arg, args...);
32  }
33 
34  struct CheckLocInfo
35  {
36  const char* file;
37  int line;
38  const char* check_string;
39  };
40 
41  /// Base class for check failure exceptions.
42  class NGRAPH_API CheckFailure : public ngraph_error
43  {
44  public:
45  CheckFailure(const CheckLocInfo& check_loc_info,
46  const std::string& context_info,
47  const std::string& explanation)
48  : ngraph_error(make_what(check_loc_info, context_info, explanation))
49  {
50  }
51 
52  private:
53  static std::string make_what(const CheckLocInfo& check_loc_info,
54  const std::string& context_info,
55  const std::string& explanation);
56  };
57 }
58 
59 //
60 // Helper macro for defining custom check macros, which throw custom exception classes and provide
61 // useful context information (the check condition, source filename, line number, and any domain-
62 // specific context information [e.g., a summary of the node that was being processed at the time
63 // of the check]).
64 //
65 // For example (actually implemented in node.cpp), let's say we want to define a macro for
66 // checking conditions during node validation, usable as follows:
67 //
68 // NODE_VALIDATION_CHECK(node_being_checked,
69 // node_being_checked->get_input_shape(0).size() == 1,
70 // "Node must have an input rank of 1, but got ",
71 // node_being_checked->get_input_shape(0).size(), ".");
72 //
73 // In case of failure, this will throw an exception of type NodeValidationFailure with a what()
74 // string something like:
75 //
76 // Check 'node_being_checked->get_input_shape(0).size() == 1' failed at foo.cpp:123:
77 // While validating node 'Broadcast[Broadcast_10](Reshape_9: float{1,3,4,5}) -> (??)':
78 // Node must have an input of rank 1, but got 2.
79 //
80 // To implement this, he first step is to define a subclass of CheckFailure (let's say it's called
81 // MyFailure), which must have a constructor of the form:
82 //
83 // MyFailure(const CheckLocInfo& check_loc_info,
84 // T context_info, // "T" can be any type; you'll supply a function to convert "T"
85 // // to std::string
86 // const std::string& explanation)
87 //
88 // Here, we define a custom class for node validation failures as follows:
89 //
90 // static std::string node_validation_failure_loc_string(const Node* node)
91 // {
92 // std::stringstream ss;
93 // ss << "While validating node '" << *node << "'";
94 // return ss.str();
95 // }
96 //
97 // class NodeValidationFailure : public CheckFailure
98 // {
99 // public:
100 // NodeValidationFailure(const CheckLocInfo& check_loc_info,
101 // const Node* node,
102 // const std::string& explanation)
103 // : CheckFailure(check_loc_info, node_validation_failure_loc_string(node), explanation)
104 // {
105 // }
106 // };
107 //
108 // Then, we define the macro NODE_VALIDATION_CHECK as follows:
109 //
110 // #define NODE_VALIDATION_CHECK(node, cond, ...) <backslash>
111 // NGRAPH_CHECK_HELPER(::ngraph::NodeValidationFailure, (node), (cond), ##__VA_ARGS__)
112 //
113 // The macro NODE_VALIDATION_CHECK can now be called on any condition, with a Node* pointer
114 // supplied to generate an informative error message via node_validation_failure_loc_string().
115 //
116 // Take care to fully qualify the exception class name in the macro body.
117 //
118 // The "..." may be filled with expressions of any type that has an "operator<<" overload for
119 // insertion into std::ostream.
120 //
121 // TODO(amprocte): refactor NGRAPH_CHECK_HELPER so we don't have to introduce a locally-scoped
122 // variable (ss___) and risk shadowing.
123 //
124 #define NGRAPH_CHECK_HELPER2(exc_class, ctx, check, ...) \
125  do \
126  { \
127  if (!(check)) \
128  { \
129  ::std::stringstream ss___; \
130  ::ngraph::write_all_to_stream(ss___, __VA_ARGS__); \
131  throw exc_class( \
132  (::ngraph::CheckLocInfo{__FILE__, __LINE__, #check}), (ctx), ss___.str()); \
133  } \
134  } while (0)
135 
136 #define NGRAPH_CHECK_HELPER1(exc_class, ctx, check) \
137  do \
138  { \
139  if (!(check)) \
140  { \
141  throw exc_class((::ngraph::CheckLocInfo{__FILE__, __LINE__, #check}), (ctx), ""); \
142  } \
143  } while (0)
144 
145 /// \brief Macro to check whether a boolean condition holds.
146 /// \param cond Condition to check
147 /// \param ... Additional error message info to be added to the error message via the `<<`
148 /// stream-insertion operator. Note that the expressions here will be evaluated lazily,
149 /// i.e., only if the `cond` evalutes to `false`.
150 /// \throws ::ngraph::CheckFailure if `cond` is false.
151 #define NGRAPH_CHECK(...) NGRAPH_CHECK_HELPER(::ngraph::CheckFailure, "", __VA_ARGS__)
152 
153 /// \brief Macro to signal a code path that is unreachable in a successful execution. It's
154 /// implemented with NGRAPH_CHECK macro.
155 /// \param ... Additional error message that should describe why that execution path is unreachable.
156 /// \throws ::ngraph::CheckFailure if the macro is executed.
157 #define NGRAPH_UNREACHABLE(...) NGRAPH_CHECK(false, "Unreachable: ", __VA_ARGS__)
158 #define NGRAPH_CHECK_HELPER(exc_class, ctx, ...) \
159  CALL_OVERLOAD(NGRAPH_CHECK_HELPER, exc_class, ctx, __VA_ARGS__)
160 
161 #define GLUE(x, y) x y
162 
163 #define RETURN_ARG_COUNT(_1_, \
164  _2_, \
165  _3_, \
166  _4_, \
167  _5_, \
168  _6, \
169  _7, \
170  _8, \
171  _9, \
172  _10, \
173  _11, \
174  _12, \
175  _13, \
176  _14, \
177  _15, \
178  _16, \
179  _17, \
180  _18, \
181  _19, \
182  _20, \
183  _21, \
184  _22, \
185  _23, \
186  _24, \
187  _25, \
188  count, \
189  ...) \
190  count
191 #define EXPAND_ARGS(args) RETURN_ARG_COUNT args
192 #define COUNT_ARGS_MAXN(...) \
193  EXPAND_ARGS((__VA_ARGS__, \
194  2, \
195  2, \
196  2, \
197  2, \
198  2, \
199  2, \
200  2, \
201  2, \
202  2, \
203  2, \
204  2, \
205  2, \
206  2, \
207  2, \
208  2, \
209  2, \
210  2, \
211  2, \
212  2, \
213  2, \
214  2, \
215  2, \
216  2, \
217  2, \
218  1, \
219  0))
220 
221 #define OVERLOAD_MACRO2(name, count) name##count
222 #define OVERLOAD_MACRO1(name, count) OVERLOAD_MACRO2(name, count)
223 #define OVERLOAD_MACRO(name, count) OVERLOAD_MACRO1(name, count)
224 
225 #define CALL_OVERLOAD(name, exc_class, ctx, ...) \
226  GLUE(OVERLOAD_MACRO(name, COUNT_ARGS_MAXN(__VA_ARGS__)), (exc_class, ctx, __VA_ARGS__))
Base class for check failure exceptions.
Definition: check.hpp:43
Base error for ngraph runtime errors.
Definition: except.hpp:28
The Intel nGraph C++ API.
Definition: attribute_adapter.hpp:28
Definition: check.hpp:35