HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
predicateProgram.h
Go to the documentation of this file.
1 //
2 // Copyright 2023 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 #ifndef PXR_USD_SDF_PREDICATE_PROGRAM_H
25 #define PXR_USD_SDF_PREDICATE_PROGRAM_H
26 
27 #include "pxr/pxr.h"
28 #include "pxr/usd/sdf/api.h"
29 
30 #include "pxr/base/tf/diagnostic.h"
32 #include "pxr/base/vt/value.h"
33 
36 #include "pxr/usd/sdf/invoke.hpp"
37 
38 #include <initializer_list>
39 #include <memory>
40 #include <string>
41 #include <vector>
42 
44 
45 // fwd decl
46 template <class DomainType>
48 
49 // fwd decl
50 template <class DomainType>
54 
55 /// \class SdfPredicateProgram
56 ///
57 /// Represents a callable "program", the result of linking an
58 /// SdfPredicateExpression with an SdfPredicateLibrary via
59 /// SdfLinkPredicateExpression().
60 ///
61 /// The main public interface this class exposes is the function-call
62 /// operator(), accepting a single argument of type `DomainType`, as it is
63 /// specified to the template. Consider using `const Type &` as the
64 /// `DomainType` for both SdfPredicateProgram and SdfPredicateLibrary if it's
65 /// important that domain type instances aren't passed by-value.
66 ///
67 template <class DomainType>
69 {
70 public:
71  friend SdfPredicateProgram
72  SdfLinkPredicateExpression<DomainType>(
73  SdfPredicateExpression const &expr,
75 
76  /// Return true if this program has any ops, false otherwise.
77  explicit operator bool() const {
78  return !_ops.empty();
79  }
80 
81  /// Run the predicate program on \p obj, and return the result.
82  bool
83  operator()(DomainType obj) const {
84  bool result = false;
85  int nest = 0;
86  auto funcIter = _funcs.cbegin();
87  auto opIter = _ops.cbegin(), opEnd = _ops.cend();
88 
89  // Helper for short-circuiting "and" and "or" operators. Advance,
90  // ignoring everything until we reach the next Close that brings us to
91  // the starting nest level.
92  auto shortCircuit = [&]() {
93  const int origNest = nest;
94  for (; opIter != opEnd; ++opIter) {
95  switch(*opIter) {
96  case Call: ++funcIter; break; // Skip calls.
97  case Not: case And: case Or: break; // Skip operations.
98  case Open: ++nest; break;
99  case Close:
100  if (--nest == origNest) {
101  return;
102  }
103  break;
104  };
105  }
106  };
107 
108  // Evaluate the predicate expression by processing operations and
109  // invoking predicate functions.
110  for (; opIter != opEnd; ++opIter) {
111  switch (*opIter) {
112  case Call: result = static_cast<bool>((*funcIter++)(obj)); break;
113  case Not: result = !result; break;
114  case And: case Or: {
115  const bool decidingValue = *opIter != And;
116  // If the and/or result is already the deciding value,
117  // short-circuit. Otherwise the result is the rhs, so continue.
118  if (result == decidingValue) {
119  shortCircuit();
120  }
121  }
122  break;
123  case Open: ++nest; break;
124  case Close: --nest; break;
125  };
126  }
127  return result;
128  }
129 
130 private:
131  enum _Op { Call, Not, Open, Close, And, Or };
132  std::vector<_Op> _ops;
133  std::vector<std::function<SdfPredicateFunctionResult (DomainType)>> _funcs;
134 };
135 
136 
137 /// Link \p expr with \p lib and return a callable program that evaluates \p
138 /// expr on given objects of the \p DomainType. If linking \p expr and \p lib
139 /// fails, issue a TF_RUNTIME_ERROR with a message, and return an empty program.
140 template <class DomainType>
144 {
145  using Expr = SdfPredicateExpression;
146  using Program = SdfPredicateProgram<DomainType>;
147 
148  // Walk expr and populate prog, binding calls with lib.
149 
150  Program prog;
151  std::string errs;
152 
153  auto exprToProgramOp = [](Expr::Op op) {
154  switch (op) {
155  case Expr::Call: return Program::Call;
156  case Expr::Not: return Program::Not;
157  case Expr::ImpliedAnd: case Expr::And: return Program::And;
158  case Expr::Or: return Program::Or;
159  };
160  return static_cast<typename Program::_Op>(-1);
161  };
162 
163  auto translateLogic = [&](Expr::Op op, int argIndex) {
164  switch (op) {
165  case Expr::Not: // Not is postfix, RPN-style.
166  if (argIndex == 1) {
167  prog._ops.push_back(Program::Not);
168  }
169  break;
170  case Expr::ImpliedAnd: // Binary logic ops are infix to facilitate
171  case Expr::And: // short-circuiting.
172  case Expr::Or:
173  if (argIndex == 1) {
174  prog._ops.push_back(exprToProgramOp(op));
175  prog._ops.push_back(Program::Open);
176  }
177  else if (argIndex == 2) {
178  prog._ops.push_back(Program::Close);
179  }
180  break;
181  case Expr::Call:
182  break; // do nothing, handled in translateCall.
183  };
184  };
185 
186  auto translateCall = [&](Expr::FnCall const &call) {
187  // Try to bind the call against library overloads. If successful,
188  // insert a call op and the function.
189  if (auto fn = lib._BindCall(call.funcName, call.args)) {
190  prog._funcs.push_back(std::move(fn));
191  prog._ops.push_back(Program::Call);
192  }
193  else {
194  if (!errs.empty()) {
195  errs += ", ";
196  }
197  errs += "Failed to bind call of " + call.funcName;
198  }
199  };
200 
201  // Walk the expression and build the "compiled" program.
202  expr.Walk(translateLogic, translateCall);
203 
204  if (!errs.empty()) {
205  prog = {};
206  TF_RUNTIME_ERROR(errs);
207  }
208  return prog;
209 }
210 
212 
213 #endif // PXR_USD_SDF_PREDICATE_PROGRAM_H
SDF_API void Walk(TfFunctionRef< void(Op, int)> logic, TfFunctionRef< void(FnCall const &)> call) const
GLsizei const GLchar *const * string
Definition: glcorearb.h:814
bool operator()(DomainType obj) const
Run the predicate program on obj, and return the result.
**But if you need a result
Definition: thread.h:613
#define TF_RUNTIME_ERROR
SdfPredicateProgram< DomainType > SdfLinkPredicateExpression(SdfPredicateExpression const &expr, SdfPredicateLibrary< DomainType > const &lib)
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1441
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:91