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