1 //
2 // Copyright 2021 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24
25 #ifndef PXR_BASE_TF_EXCEPTION_H
26 #define PXR_BASE_TF_EXCEPTION_H
27
28 /// \file tf/exception.h
29 /// \ingroup group_tf_Diagnostic
30
31 #include "pxr/pxr.h"
32 #include "pxr/base/tf/api.h"
33 #include "pxr/base/tf/callContext.h"
34 #include "pxr/base/tf/functionRef.h"
35
36 #include <cstdint>
37 #include <exception>
38 #include <string>
39 #include <vector>
40
41 PXR_NAMESPACE_OPEN_SCOPE
42
43 /// This structure is used to indicate that some number of caller frames should
44 /// be skipped when capturing exception stack traces at the throw point.
45 struct TfSkipCallerFrames
46 {
numToSkipTfSkipCallerFrames47 explicit TfSkipCallerFrames(int n=0) : numToSkip(n) {}
48 int numToSkip;
49 };
50
51 /// The base class for exceptions supported by the Tf exceptions facilities.
52 /// Typical usage is to publically derive your own exception class from this
53 /// one, and throw using the TF_THROW() macro.
54 ///
55 /// Deriving this base class and throwing via TF_THROW() will record the throw
56 /// point's call context (see GetThrowContext()) and will also capture a portion
57 /// of the throwing thread's call stack (see GetThrowStack()).
58 ///
59 /// Additionally, the Tf library registers an exception translator with
60 /// boost.python to raise a Python exeption wrapping the thrown exception
61 /// object. Similarly utilties that call Python via Tf will re-throw the
62 /// embedded C++ exception if the Python exception unwinds back into C++.
63 class TfBaseException : public std::exception
64 {
65 public:
66 TF_API
67 virtual ~TfBaseException();
68
69 /// Construct with \p message, reported by this class's what()
70 /// implementation.
71 TF_API
72 explicit TfBaseException(std::string const &message);
73
74 /// Return the call context from the throw point associated with this
75 /// exception. Note that this context may be invalid if this exception was
76 /// not thrown with TF_THROW().
GetThrowContext()77 TfCallContext const &GetThrowContext() const {
78 return _callContext;
79 }
80
81 /// Return the stack frame pointers from the throw point. See
82 /// ArchPrintStackFrames() to turn these into human-readable strings.
GetThrowStack()83 std::vector<uintptr_t> const &GetThrowStack() const {
84 return _throwStack;
85 }
86
87 /// Move the stack frame pointers from the throw point to \p out. See
88 /// GetThrowStack() for more details.
MoveThrowStackTo(std::vector<uintptr_t> & out)89 void MoveThrowStackTo(std::vector<uintptr_t> &out) {
90 out = std::move(_throwStack);
91 _throwStack.clear();
92 }
93
94 /// Override std::exception::what() to return the message passed during
95 /// construction.
96 TF_API
97 virtual const char *what() const noexcept override;
98
99 // Friend throw support.
100 template <class Derived, class ... Args>
101 friend void
102 Tf_Throw(TfCallContext const &cc,
103 TfSkipCallerFrames skipFrames,
104 Args && ... args);
105
106 private:
107 TF_API
108 static void _ThrowImpl(TfCallContext const &cc,
109 TfBaseException &exc,
110 TfFunctionRef<void ()> thrower,
111 int skipNCallerFrames);
112
113 TfCallContext _callContext;
114 std::vector<uintptr_t> _throwStack;
115 std::string _message;
116 };
117
118 // TF_THROW() support function.
119 template <class Exception, class ... Args>
120 void
Tf_Throw(TfCallContext const & cc,TfSkipCallerFrames skipFrames,Args &&...args)121 Tf_Throw(TfCallContext const &cc,
122 TfSkipCallerFrames skipFrames,
123 Args && ... args) {
124 Exception exc(std::forward<Args>(args)...);
125 auto thrower = [&exc]() { throw std::move(exc); };
126 TfBaseException::_ThrowImpl(cc, exc, thrower, skipFrames.numToSkip);
127 }
128
129 // TF_THROW() support function.
130 template <class Exception, class ... Args>
Tf_Throw(TfCallContext const & cc,Args &&...args)131 void Tf_Throw(TfCallContext const &cc, Args && ... args) {
132 Tf_Throw<Exception>(cc, TfSkipCallerFrames(), std::forward<Args>(args)...);
133 }
134
135 #ifdef doxygen
136
137 /// Construct an instance of Exception (which must derive TfBaseException) with
138 /// Exception-ctor-args and throw it. Also capture a portion of this thread's
139 /// current call stack and the throw point's source filename & line number to
140 /// embed in the exception. If the exception goes unhandled these will be
141 /// reported in the crash report that Tf's terminate handler generates, or in
142 /// the unhandled exception message in the python interpreter.
143 #define TF_THROW(Exception, Exception-ctor-args...)
144 #define TF_THROW(Exception, TfSkipCallerFrames, Exception-ctor-args...)
145
146 #else
147
148 #define TF_THROW(Exception, ...) \
149 Tf_Throw<Exception>(TF_CALL_CONTEXT, __VA_ARGS__)
150
151 #endif
152
153 PXR_NAMESPACE_CLOSE_SCOPE
154
155 #endif // PXR_BASE_TF_EXCEPTION_H
156