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  using PredicateFunction =
73 
74  friend SdfPredicateProgram
75  SdfLinkPredicateExpression<DomainType>(
76  SdfPredicateExpression const &expr,
78 
79  /// Return true if this program has any ops, false otherwise.
80  explicit operator bool() const {
81  return !_ops.empty();
82  }
83 
84  /// Run the predicate program on \p obj, and return the result.
86  operator()(DomainType const &obj) const {
89  int nest = 0;
90  auto funcIter = _funcs.cbegin();
91  auto opIter = _ops.cbegin(), opEnd = _ops.cend();
92 
93  // The current implementation favors short-circuiting over constance
94  // propagation. It might be beneficial to avoid short-circuiting when
95  // constancy isn't known, in hopes of establishing constancy. For
96  // example, if we have 'A or B', and 'A' evaluates to 'true' with
97  // MayVaryOverDescendants, we will skip evaluating B
98  // (short-circuit). This means we would miss the possibility of
99  // upgrading the constancy in case B returned 'true' with
100  // ConstantOverDescendants. This isn't a simple switch to flip though;
101  // we'd have to do some code restructuring here.
102  //
103  // For posterity, the rules for propagating constancy are the following,
104  // where A and B are the truth-values, and c(A), c(B), are whether or
105  // not the constancy is ConstantOverDescendants for A, B, respectively:
106  //
107  // c(A or B) = (A and c(A)) or (B and c(B)) or (c(A) and c(B))
108  // c(A and B) = (!A and c(A)) or (!B and c(B)) or (c(A) and c(B))
109 
110  // Helper for short-circuiting "and" and "or" operators. Advance,
111  // ignoring everything until we reach the next Close that brings us to
112  // the starting nest level.
113  auto shortCircuit = [&]() {
114  const int origNest = nest;
115  for (; opIter != opEnd; ++opIter) {
116  switch(*opIter) {
117  case Call: ++funcIter; break; // Skip calls.
118  case Not: case And: case Or: break; // Skip operations.
119  case Open: ++nest; break;
120  case Close:
121  if (--nest == origNest) {
122  return;
123  }
124  break;
125  };
126  }
127  };
128 
129  // Evaluate the predicate expression by processing operations and
130  // invoking predicate functions.
131  for (; opIter != opEnd; ++opIter) {
132  switch (*opIter) {
133  case Call:
134  result.SetAndPropagateConstancy((*funcIter++)(obj));
135  break;
136  case Not: result = !result; break;
137  case And: case Or: {
138  const bool decidingValue = *opIter != And;
139  // If the and/or result is already the deciding value,
140  // short-circuit. Otherwise the result is the rhs, so continue.
141  if (result == decidingValue) {
142  shortCircuit();
143  }
144  }
145  break;
146  case Open: ++nest; break;
147  case Close: --nest; break;
148  };
149  }
150  return result;
151  }
152 
153 private:
154  enum _Op { Call, Not, Open, Close, And, Or };
155  std::vector<_Op> _ops;
156  std::vector<PredicateFunction> _funcs;
157 };
158 
159 
160 /// Link \p expr with \p lib and return a callable program that evaluates \p
161 /// expr on given objects of the \p DomainType. If linking \p expr and \p lib
162 /// fails, issue a TF_RUNTIME_ERROR with a message, and return an empty program.
163 template <class DomainType>
167 {
168  using Expr = SdfPredicateExpression;
169  using Program = SdfPredicateProgram<DomainType>;
170 
171  // Walk expr and populate prog, binding calls with lib.
172 
173  Program prog;
174  std::string errs;
175 
176  auto exprToProgramOp = [](Expr::Op op) {
177  switch (op) {
178  case Expr::Call: return Program::Call;
179  case Expr::Not: return Program::Not;
180  case Expr::ImpliedAnd: case Expr::And: return Program::And;
181  case Expr::Or: return Program::Or;
182  };
183  return static_cast<typename Program::_Op>(-1);
184  };
185 
186  auto translateLogic = [&](Expr::Op op, int argIndex) {
187  switch (op) {
188  case Expr::Not: // Not is postfix, RPN-style.
189  if (argIndex == 1) {
190  prog._ops.push_back(Program::Not);
191  }
192  break;
193  case Expr::ImpliedAnd: // Binary logic ops are infix to facilitate
194  case Expr::And: // short-circuiting.
195  case Expr::Or:
196  if (argIndex == 1) {
197  prog._ops.push_back(exprToProgramOp(op));
198  prog._ops.push_back(Program::Open);
199  }
200  else if (argIndex == 2) {
201  prog._ops.push_back(Program::Close);
202  }
203  break;
204  case Expr::Call:
205  break; // do nothing, handled in translateCall.
206  };
207  };
208 
209  auto translateCall = [&](Expr::FnCall const &call) {
210  // Try to bind the call against library overloads. If successful,
211  // insert a call op and the function.
212  if (auto fn = lib._BindCall(call.funcName, call.args)) {
213  prog._funcs.push_back(std::move(fn));
214  prog._ops.push_back(Program::Call);
215  }
216  else {
217  if (!errs.empty()) {
218  errs += ", ";
219  }
220  errs += "Failed to bind call of " + call.funcName;
221  }
222  };
223 
224  // Walk the expression and build the "compiled" program.
225  expr.Walk(translateLogic, translateCall);
226 
227  if (!errs.empty()) {
228  prog = {};
229  TF_RUNTIME_ERROR(errs);
230  }
231  return prog;
232 }
233 
235 
236 #endif // PXR_USD_SDF_PREDICATE_PROGRAM_H
typename SdfPredicateLibrary< DomainType >::PredicateFunction PredicateFunction
SDF_API void Walk(TfFunctionRef< void(Op, int)> logic, TfFunctionRef< void(FnCall const &)> call) const
GLsizei const GLchar *const * string
Definition: glcorearb.h:814
**But if you need a result
Definition: thread.h:613
#define TF_RUNTIME_ERROR
void SetAndPropagateConstancy(SdfPredicateFunctionResult other)
static SdfPredicateFunctionResult MakeConstant(bool value)
Create with value and 'ConstantOverDescendants'.
SdfPredicateProgram< DomainType > SdfLinkPredicateExpression(SdfPredicateExpression const &expr, SdfPredicateLibrary< DomainType > const &lib)
std::function< SdfPredicateFunctionResult(DomainType const &)> PredicateFunction
The type of a bound function, the result of binding passed arguments.
SdfPredicateFunctionResult operator()(DomainType const &obj) const
Run the predicate program on obj, and return the result.
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1432
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:91