Loading...
Searching...
No Matches
diagnosticContainer.h
Go to the documentation of this file.
1//
2// Copyright 2026 Pixar
3//
4// Licensed under the terms set forth in the LICENSE.txt file available at
5// https://openusd.org/license.
6//
7#ifndef PXR_BASE_TF_DIAGNOSTIC_CONTAINER_H
8#define PXR_BASE_TF_DIAGNOSTIC_CONTAINER_H
9
11
12#include "pxr/pxr.h"
13#include "pxr/base/tf/api.h"
14#include "pxr/base/tf/error.h"
15#include "pxr/base/tf/status.h"
17#include "pxr/base/tf/warning.h"
18
19#include <functional>
20#include <optional>
21#include <string>
22#include <type_traits>
23#include <vector>
24
25PXR_NAMESPACE_OPEN_SCOPE
26
27// Internal class that accumulates \c TfError, \c TfWarning, and \c TfStatus
28// diagnostics in the order they were issued, and provides iteration,
29// predicate-based query and erasure, and re-posting.
30//
31// This class is an implementation detail shared by \c TfDiagnosticTrap and \c
32// TfDiagnosticTransport and is not intended for direct use.
33class Tf_DiagnosticContainer
34{
35public:
36 // A stateful cursor over the diagnostics in a \c Tf_DiagnosticContainer.
37 // Advance by calling \c Next() with a callable -- the iterator will advance
38 // to the next diagnostic the callable can accept, invoke it, and return the
39 // result. The callable may be changed between \c Next() calls, allowing
40 // different operations to be interleaved over a single pass.
41 //
42 // \c Next() returns a truthy value if it found and invoked the callable,
43 // and a falsy value when the container is exhausted. Specifically: - If
44 // the callable returns void, \c Next() returns \c bool - Otherwise, \c
45 // Next() returns \c std::optional<ReturnType>
46 struct Iterator {
47 explicit Iterator(Tf_DiagnosticContainer const &container,
48 size_t skip = 0)
49 : _container(container) {
50 // Advance past the first `skip` diagnostics.
51 while (skip-- > 0 && _orderIndex < _container._order.size()) {
52 switch (_container._order[_orderIndex++]) {
53 case 'E': ++_errorIndex; break;
54 case 'W': ++_warningIndex; break;
55 case 'S': ++_statusIndex; break;
56 }
57 }
58 }
59
60 template <class Fn>
61 auto Next(Fn &&fn)
62 {
63 using RetT = typename _IterNextReturnType<Fn>::Type;
64
65 while (_orderIndex < _container._order.size()) {
66 char c = _container._order[_orderIndex++];
67 switch (c) {
68 case 'E':
69 if (auto r = TfTryInvoke<RetT>(
70 fn, _container._errors[_errorIndex++])) {
71 return r;
72 }
73 break;
74 case 'W':
75 if (auto r = TfTryInvoke<RetT>(
76 fn, _container._warnings[_warningIndex++])) {
77 return r;
78 }
79 break;
80 case 'S':
81 if (auto r = TfTryInvoke<RetT>(
82 fn, _container._statuses[_statusIndex++])) {
83 return r;
84 }
85 break;
86 }
87 }
88 return TfNotInvoked<RetT>();
89 }
90
91 private:
92 Tf_DiagnosticContainer const &_container;
93 size_t _orderIndex = 0;
94 size_t _errorIndex = 0;
95 size_t _warningIndex = 0;
96 size_t _statusIndex = 0;
97 };
98
99 // Return true if no diagnostics have been accumulated.
100 bool IsEmpty() const { return _order.empty(); }
101
102 // Return the total number of accumulated diagnostics.
103 size_t size() const { return _order.size(); }
104
105 // Return the accumulated errors.
106 std::vector<TfError> const& GetErrors() const { return _errors; }
107
108 // Return the accumulated warnings.
109 std::vector<TfWarning> const& GetWarnings() const { return _warnings; }
110
111 // Return the accumulated status messages.
112 std::vector<TfStatus> const& GetStatuses() const { return _statuses; }
113
114 // Mutation
115
116 void Append(TfError const &e) {
117 _errors.push_back(e);
118 _order.push_back('E');
119 }
120
121 void Append(TfWarning const &w) {
122 _warnings.push_back(w);
123 _order.push_back('W');
124 }
125
126 void Append(TfStatus const &s) {
127 _statuses.push_back(s);
128 _order.push_back('S');
129 }
130
131 // Discard all accumulated diagnostics.
132 void Clear() {
133 _errors.clear();
134 _warnings.clear();
135 _statuses.clear();
136 _order.clear();
137 }
138
139 // Iteration
140
141 // Return an iterator over this container's diagnostics, optionally
142 // skipping the first \p skip entries.
143 Iterator GetIterator(size_t skip = 0) const {
144 return Iterator(*this, skip);
145 }
146
147 // Post all diagnostics in their original order then clear.
148 TF_API void Post();
149
150private:
151 // These are required to avoid instantiating invoke_result_t when Invocable
152 // is false. We can't use std::conditional_t because it always instantiates
153 // both branches of the condition.
154 template <class Fn, class Arg, bool Invocable>
155 struct _SafeInvokeResult {
156 using type = void;
157 };
158
159 template <class Fn, class Arg>
160 struct _SafeInvokeResult<Fn, Arg, true> {
161 using type = std::invoke_result_t<Fn, Arg>;
162 };
163
164 template <class Fn>
165 struct _IterNextReturnType {
166 static constexpr bool _ErrInvocable =
167 std::is_invocable_v<Fn, TfError const &>;
168 static constexpr bool _WarnInvocable =
169 std::is_invocable_v<Fn, TfWarning const &>;
170 static constexpr bool _StatInvocable =
171 std::is_invocable_v<Fn, TfStatus const &>;
172
173 static_assert(_ErrInvocable || _WarnInvocable || _StatInvocable,
174 "Callable must be invocable with at least one of "
175 "TfError, TfWarning, or TfStatus");
176
177 using _ErrResult = typename _SafeInvokeResult<
178 Fn, TfError const &, _ErrInvocable>::type;
179 using _WarnResult = typename _SafeInvokeResult<
180 Fn, TfWarning const &, _WarnInvocable>::type;
181 using _StatResult = typename _SafeInvokeResult<
182 Fn, TfStatus const &, _StatInvocable>::type;
183
184 using Type = std::conditional_t<
185 _ErrInvocable, _ErrResult,
186 std::conditional_t<
187 _WarnInvocable, _WarnResult, _StatResult>>;
188
189 static_assert(!_ErrInvocable || !_WarnInvocable ||
190 std::is_same_v<_ErrResult, _WarnResult>,
191 "Callable must return the same type for all "
192 "invocable diagnostic types");
193 static_assert(!_ErrInvocable || !_StatInvocable ||
194 std::is_same_v<_ErrResult, _StatResult>,
195 "Callable must return the same type for all "
196 "invocable diagnostic types");
197 static_assert(!_WarnInvocable || !_StatInvocable ||
198 std::is_same_v<_WarnResult, _StatResult>,
199 "Callable must return the same type for all "
200 "invocable diagnostic types");
201 };
202
203 std::vector<TfError> _errors;
204 std::vector<TfWarning> _warnings;
205 std::vector<TfStatus> _statuses;
206 std::string _order;
207};
208
209PXR_NAMESPACE_CLOSE_SCOPE
210
211#endif // PXR_BASE_TF_DIAGNOSTIC_CONTAINER_H
Provide facilities for error handling in script.
Represents an object that contains error information.
Definition error.h:32
Represents an object that contains information about a status message.
Definition status.h:28
Represents an object that contains information about a warning.
Definition warning.h:28
constexpr auto TfNotInvoked()
Return the result type of TfTryInvoke<Ret> in the case where the function was not invoked – false if ...
Definition tryInvoke.h:26
auto TfTryInvoke(Fn &&fn, Args &&...args)
Invoke fn with args if fn is invocable with those arguments and return a result that indicates both w...
Definition tryInvoke.h:68
SDF_API const std::vector< std::string > & GetErrors() const
Returns a list of errors encountered when parsing this expression.