HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
predicateLibrary.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_LIBRARY_H
25 #define PXR_USD_SDF_PREDICATE_LIBRARY_H
26 
27 #include "pxr/pxr.h"
28 #include "pxr/usd/sdf/api.h"
29 
30 #include "pxr/base/tf/diagnostic.h"
33 #include "pxr/base/vt/value.h"
34 
36 #include "pxr/usd/sdf/invoke.hpp"
37 
38 #include <initializer_list>
39 #include <memory>
40 #include <string>
41 #include <vector>
42 
44 
45 /// \class SdfPredicateParamNamesAndDefaults
46 ///
47 /// Represents named function parameters, with optional default values. These
48 /// are generally constructed via an initializer_list and specified in
49 /// SdfPredicateLibrary::Define().
50 ///
51 /// Valid parameter names and defaults have non-empty names, and all parameters
52 /// following the first one with a default value must also have default values.
54  /// \class Param represents a single named parameter with an optional
55  /// default value.
56  struct Param {
57  /// Construct with or implicitly convert from name.
58  Param(char const *name) : name(name) {}
59 
60  /// Construct from name and default value.
61  template <class Val>
62  Param(char const *name, Val &&defVal)
63  : name(name), val(std::forward<Val>(defVal)) {}
64 
67  };
68 
69  /// Construct or implicitly convert from initializer_list<Param>.
71  std::initializer_list<Param> const &params) :
72  _params(params.begin(), params.end()) {}
73 
74  /// Check that all parameters have non-empty names and that all paramters
75  /// following the first with a default value also have default values.
76  /// Issue TF_CODING_ERROR()s and return false if these conditions are
77  /// violated, otherwise return true.
78  SDF_API
79  bool CheckValidity() const;
80 
81  /// Return a reference to the parameters in a vector.
82  std::vector<Param> const &GetParams() const & {
83  return _params;
84  }
85 
86  /// Move-return the parameters in a vector.
87  std::vector<Param> GetParams() const && {
88  return std::move(_params);
89  }
90 
91 private:
92  std::vector<Param> _params;
93 };
94 
95 
96 /// \class SdfPredicateFunctionResult
97 ///
98 /// Represents the result of a predicate function: a pair of the boolean result
99 /// and a Constancy token indicating whether the function result is constant
100 /// over "descendant" objects, or that it might vary over "descendant" objects.
102 {
103 public:
105 
106  /// Default construction produces a 'false' result that 'MayVary'.
108  : _value(false), _constancy(MayVary) {}
109 
110  /// Construct with \p value and \p constancy.
112  bool value, Constancy constancy=MayVary)
113  : _value(value), _constancy(constancy) {}
114 
115  /// Return the result value.
116  bool GetValue() const {
117  return _value;
118  }
119 
120  /// Return the result constancy.
122  return _constancy;
123  }
124 
125  /// Return GetValue().
126  explicit operator bool() const {
127  return _value;
128  }
129 
130 private:
131  bool _value;
132  Constancy _constancy;
133 };
134 
135 // fwd decl
136 template <class DomainType>
138 
139 // fwd decl
140 template <class DomainType>
142 
143 // fwd decl
144 template <class DomainType>
148 
149 /// \class SdfPredicateLibrary
150 ///
151 /// Represents a library of predicate functions for use with
152 /// SdfPredicateExpression. Call SdfLinkPredicateExpression() with an
153 /// expression and a library to produce a callable SdfPredicateProgram.
154 template <class DomainType>
156 {
158  SdfLinkPredicateExpression<DomainType>(
159  SdfPredicateExpression const &expr,
160  SdfPredicateLibrary const &lib);
161 
163 public:
164  /// Default constructor produces an empty library.
165  SdfPredicateLibrary() = default;
166 
167  /// Move-construct from an \p other library.
168  SdfPredicateLibrary(SdfPredicateLibrary &&other) = default;
169 
170  /// Copy-construct from an \p other library.
172  for (auto iter = other._binders.begin(), end = other._binders.end();
173  iter != end; ++iter) {
174  auto &theseBinders = _binders[iter->first];
175  for (auto const &otherBinder: iter->second) {
176  theseBinders.push_back(otherBinder->Clone());
177  }
178  }
179  }
180 
181  /// Move-assignment from an \p other library.
183 
184  /// Copy-assignment from an \p other library.
186  if (this != &other) {
187  SdfPredicateLibrary copy(other);
188  *this = std::move(copy);
189  }
190  return *this;
191  }
192 
193  /// Register a function with name \p name in this library. The first
194  /// argument must accept a DomainType instance. The remaining arguments
195  /// must be convertible from bool, int, float, string.
196  template <class Fn>
197  SdfPredicateLibrary &Define(char const *name, Fn &&fn) {
198  return Define(name, std::forward<Fn>(fn), {});
199  }
200 
201  /// Register a function with name \p name in this library. The first
202  /// argument must accept a DomainType instance. The remaining arguments
203  /// must be convertible from bool, int, float, string. Optional parameter
204  /// names and default values may be supplied in \p namesAndDefaults.
205  template <class Fn>
207  Define(std::string const &name, Fn &&fn,
208  NamesAndDefaults const &namesAndDefaults) {
209  // Try to create a new overload binder for 'name'. The main operation a
210  // binder does is, when "linking" a predicate expression, given a
211  // specific set of arguments from the expression, check to see if those
212  // arguments can be bound to 'fn', and if so return a type-erased
213  // callable that invokes fn with those arguments.
214  if (auto obinder = _OverloadBinder<std::decay_t<Fn>>
215  ::TryCreate(std::forward<Fn>(fn), namesAndDefaults)) {
216  _binders[name].push_back(std::move(obinder));
217  }
218  return *this;
219  }
220 
221 private:
222 
223  std::function<SdfPredicateFunctionResult (DomainType)>
224  _BindCall(std::string const &name,
225  std::vector<SdfPredicateExpression::FnArg> const &args) const {
226  std::function<SdfPredicateFunctionResult (DomainType)> ret;
227  auto iter = _binders.find(name);
228  if (iter == _binders.end()) {
229  TF_RUNTIME_ERROR("No registered function '%s'", name.c_str());
230  return ret;
231  }
232  // Run thru optimistically first -- if we fail to bind to any overload,
233  // then produce an error message with all the overload signatures.
234  for (auto i = iter->second.rbegin(),
235  end = iter->second.rend(); i != end; ++i) {
236  ret = (*i)->Bind(args);
237  if (ret) {
238  break;
239  }
240  }
241  return ret;
242  }
243 
244  template <class ParamType>
245  static void _CheckOneNameAndDefault(
246  bool &valid, size_t index, size_t numParams,
247  NamesAndDefaults const &namesAndDefaults) {
248 
249  // If the namesIndex-th param has a default, it must be convertible to
250  // the ArgIndex-th type.
251  std::vector<NamesAndDefaults::Param> const &
252  params = namesAndDefaults.GetParams();
253 
254  size_t nFromEnd = numParams - index - 1;
255  if (nFromEnd >= params.size()) {
256  // No more names & defaults to check.
257  return;
258  }
259 
260  size_t namesIndex = params.size() - nFromEnd - 1;
261 
262  auto const &param = params[namesIndex];
263  if (!param.val.IsEmpty() && !param.val.CanCast<ParamType>()) {
264  TF_CODING_ERROR("Predicate default parameter '%s' value of "
265  "type '%s' cannot convert to c++ argument of "
266  "type '%s' at index %zu",
267  param.name.c_str(),
268  param.val.GetTypeName().c_str(),
269  ArchGetDemangled<ParamType>().c_str(),
270  index);
271  valid = false;
272  }
273  }
274 
275  template <class ParamsTuple, size_t... I>
276  static bool
277  _CheckNamesAndDefaultsImpl(
278  NamesAndDefaults const &namesAndDefaults,
279  std::index_sequence<I...>) {
280  // A fold expression would let us just do &&, but that's c++'17, so we
281  // just do all of them and set a bool.
282  bool valid = true;
283  constexpr size_t N = std::tuple_size<ParamsTuple>::value;
284  // Need an unused array so we can use an initializer list to invoke
285  // _CheckOneNameAndDefault N times.
286  int unused[] = {
287  0,
288  (_CheckOneNameAndDefault<std::tuple_element_t<N-I-1, ParamsTuple>>(
289  valid, N-I-1, N, namesAndDefaults), 0)...
290  };
291  TF_UNUSED(unused);
292  return valid;
293  }
294 
295  template <class Fn>
296  static bool
297  _CheckNamesAndDefaultsWithSignature(
298  NamesAndDefaults const &namesAndDefaults) {
299  // Basic check for declared names & defaults.
300  if (!namesAndDefaults.CheckValidity()) {
301  return false;
302  }
303 
304  using Traits = TfFunctionTraits<Fn>;
305 
306  // Return type must convert to bool.
307  static_assert(std::is_convertible<
308  typename Traits::ReturnType, bool>::value, "");
309 
310  // Fn must have at least one argument, and DomainType must be
311  // convertible to the first arg.
312  using DomainArgType = typename Traits::template NthArg<0>;
313  static_assert(
315 
316  // Issue an error if there are more named arguments than c++ function
317  // arguments. Subtract one from Arity to account for the leading
318  // DomainType argument.
319  std::vector<NamesAndDefaults::Param> const &
320  params = namesAndDefaults.GetParams();
321  if (params.size() > Traits::Arity-1) {
322  TF_CODING_ERROR("Predicate named arguments (%zu) exceed number of "
323  "C++ function arguments (%zu)",
324  params.size(), Traits::Arity-1);
325  return false;
326  }
327 
328  // Now check the names and defaults against the Fn signature, from back
329  // to front, since namesAndDefaults must be "right-aligned" -- that is,
330  // any unnamed arguments must come first.
331  if (!params.empty()) {
332  // Strip DomainType arg...
333  using FullParams = typename Traits::ArgTypes;
334  using Params =
336  using ParamsTuple = TfMetaApply<std::tuple, Params>;
337 
338  return _CheckNamesAndDefaultsImpl<ParamsTuple>(
339  namesAndDefaults, std::make_index_sequence<Traits::Arity-1> {});
340  }
341  return true;
342  }
343 
344  template <class ParamType>
345  static void _TryBindOne(
346  size_t index, size_t numParams,
347  ParamType &param,
348  bool &boundAllParams,
349  std::vector<SdfPredicateExpression::FnArg> const &args,
350  std::vector<bool> &boundArgs,
351  NamesAndDefaults const &namesAndDefaults) {
352 
353  // Bind the index-th 'param' from 'args' &
354  // 'namesAndDefaults'. 'boundArgs' corresponds to 'args' and indicates
355  // which have already been bound. This function sets one bit in
356  // 'boundArgs' if it binds one of them to a parameter. It may bind a
357  // default from 'namesAndDefaults', in which case it sets no bit. If no
358  // suitable binding can be determined for this parameter, set
359  // 'boundAllParams' false.
360 
361  // If we've already failed to bind, just return early.
362  if (!boundAllParams) {
363  return;
364  }
365 
366  // namesAndDefaults covers trailing parameters -- that is, there may be
367  // zero or more leading unnamed parameters.
368  std::vector<NamesAndDefaults::Param> const &
369  params = namesAndDefaults.GetParams();
370  size_t numUnnamed = params.size() - numParams;
371  NamesAndDefaults::Param const *paramNameAndDefault = nullptr;
372  if (index >= numUnnamed) {
373  paramNameAndDefault = &params[index - numUnnamed];
374  }
375 
376  // If this is a purely positional parameter (paramNameAndDefault is
377  // nullptr) or the caller supplied a positional arg (unnamed) then we
378  // use index-correspondence.
379  auto const *posArg =
380  (index < args.size() && args[index].argName.empty()) ?
381  &args[index] : nullptr;
382 
383  auto tryBind = [&](VtValue const &val, size_t argIndex) {
384  VtValue cast = VtValue::Cast<ParamType>(val);
385  if (!cast.IsEmpty()) {
386  param = cast.UncheckedRemove<ParamType>();
387  boundArgs[argIndex] = true;
388  return true;
389  }
390  boundAllParams = false;
391  return false;
392  };
393 
394  if (!paramNameAndDefault) {
395  // If this is a positional parameter, the arg must be too.
396  if (!posArg || !posArg->argName.empty()) {
397  boundAllParams = false;
398  return;
399  }
400  // Try to bind posArg.
401  tryBind(posArg->value, index);
402  return;
403  }
404  else if (posArg) {
405  // Passed a positional arg, try to bind.
406  tryBind(posArg->value, index);
407  return;
408  }
409 
410  // Only possibility is a keyword arg. If there's a matching name, try
411  // to bind that, otherwise try to fill a default.
412  for (size_t i = 0, end = args.size(); i != end; ++i) {
413  if (boundArgs[i]) {
414  // Already bound.
415  continue;
416  }
417  if (args[i].argName == paramNameAndDefault->name) {
418  // Matching name -- try to bind.
419  tryBind(args[i].value, i);
420  return;
421  }
422  }
423 
424  // No matching arg, try to fill default val.
425  VtValue cast = VtValue::Cast<ParamType>(paramNameAndDefault->val);
426  if (!cast.IsEmpty()) {
427  param = cast.UncheckedRemove<ParamType>();
428  }
429  else {
430  // Error, could not fill default.
431  boundAllParams = false;
432  }
433  }
434 
435  template <class ParamsTuple, size_t... I>
436  static bool
437  _TryBindArgs(ParamsTuple &params,
438  std::vector<SdfPredicateExpression::FnArg> const &args,
439  NamesAndDefaults const &namesAndDefaults,
440  std::index_sequence<I...>) {
441 
442  // A fold expression would let us just do &&, but that's '17, so we just
443  // do all of them and set a bool.
444  bool bound = true;
446  std::vector<bool> boundArgs(N);
447  // Need a unused array so we can use an initializer list to invoke
448  // _TryBindOne N times.
449  int unused[] = {
450  0,
451  (_TryBindOne(I, N, std::get<I>(params), bound,
452  args, boundArgs, namesAndDefaults), 0)...
453  };
454  TF_UNUSED(unused);
455  return bound;
456  }
457 
458  template <class Fn>
459  static std::function<SdfPredicateFunctionResult (DomainType)>
460  _TryToBindCall(Fn const &fn,
461  std::vector<SdfPredicateExpression::FnArg> const &args,
462  NamesAndDefaults const &namesAndDefaults) {
463 
464  // We need to determine an argument for each parameter of Fn, then make
465  // a callable object that calls that function.
466 
467  // Strip DomainType arg...
468  using Traits = TfFunctionTraits<Fn>;
469  using FullParams = typename Traits::ArgTypes;
470  using Params =
472  using ParamsTuple = TfMetaApply<std::tuple, Params>;
473 
474  if (args.size() > Traits::Arity-1) {
475  TF_RUNTIME_ERROR("Function takes at most %zu argument%s, %zu given",
476  Traits::Arity-1, Traits::Arity-1 == 1 ? "" : "s",
477  args.size());
478  return {};
479  }
480 
481  ParamsTuple typedArgs;
482  if (_TryBindArgs(typedArgs, args, namesAndDefaults,
483  std::make_index_sequence<Traits::Arity-1> {})) {
484  return [typedArgs, fn](DomainType obj) {
485  // invoke fn with obj & typedArgs. (std::apply in '17).
487  invoke_hpp::apply(fn, std::tuple_cat(
488  std::make_tuple(obj), typedArgs))
489  };
490  };
491  }
492  return {};
493  }
494 
495  struct _OverloadBinderBase
496  {
497  virtual ~_OverloadBinderBase() = default;
498  std::function<SdfPredicateFunctionResult (DomainType)>
499  Bind(std::vector<SdfPredicateExpression::FnArg> const &args) const {
500  return _Bind(args);
501  }
502  virtual std::unique_ptr<_OverloadBinderBase> Clone() const = 0;
503  protected:
504  explicit _OverloadBinderBase(NamesAndDefaults const &namesAndDefaults)
505  : _namesAndDefaults(namesAndDefaults) {}
506 
507  virtual std::function<SdfPredicateFunctionResult (DomainType)>
508  _Bind(std::vector<
509  SdfPredicateExpression::FnArg> const &args) const = 0;
510 
511  NamesAndDefaults _namesAndDefaults;
512  };
513 
514  template <class Fn>
515  struct _OverloadBinder : _OverloadBinderBase
516  {
517  ~_OverloadBinder() override = default;
518 
519  static std::unique_ptr<_OverloadBinder>
520  TryCreate(Fn &&fn, NamesAndDefaults const &nd) {
521  auto ret = std::unique_ptr<_OverloadBinder>(
522  new _OverloadBinder(fn, nd));
523  if (!_CheckNamesAndDefaultsWithSignature<Fn>(nd)) {
524  ret.reset();
525  }
526  return ret;
527  }
528 
529  std::unique_ptr<_OverloadBinderBase> Clone() const override {
530  return std::unique_ptr<
531  _OverloadBinder>(new _OverloadBinder(*this));
532  }
533 
534  private:
535  _OverloadBinder(_OverloadBinder const &) = default;
536 
537  explicit _OverloadBinder(Fn &&fn,
538  NamesAndDefaults const &namesAndDefaults)
539  : _OverloadBinderBase(namesAndDefaults)
540  , _fn(std::move(fn)) {}
541 
542  explicit _OverloadBinder(Fn const &fn,
543  NamesAndDefaults const &namesAndDefaults)
544  : _OverloadBinder(Fn(fn), namesAndDefaults) {}
545 
546  std::function<SdfPredicateFunctionResult (DomainType)>
547  _Bind(std::vector<
548  SdfPredicateExpression::FnArg> const &args) const override {
549  // Try to bind 'args' to _fn's parameters, taking _namesAndDefaults
550  // into account.
551  return _TryToBindCall(_fn, args, this->_namesAndDefaults);
552  }
553 
554  Fn _fn;
555  };
556 
557  using _OverloadBinderBasePtr = std::unique_ptr<_OverloadBinderBase>;
558 
560  std::string, std::vector<_OverloadBinderBasePtr>
561  > _binders;
562 };
563 
565 
566 #endif // PXR_USD_SDF_PREDICATE_EXPRESSION_EVAL_H
std::vector< Param > GetParams() const &&
Move-return the parameters in a vector.
iterator end() noexcept
Definition: robin_map.h:222
Param(char const *name, Val &&defVal)
Construct from name and default value.
bool GetValue() const
Return the result value.
SdfPredicateProgram< DomainType > SdfLinkPredicateExpression(SdfPredicateExpression const &expr, SdfPredicateLibrary< DomainType > const &lib)
OIIO_UTIL_API bool copy(string_view from, string_view to, std::string &err)
GLsizei const GLchar *const * string
Definition: glcorearb.h:814
GLsizei const GLfloat * value
Definition: glcorearb.h:824
#define TF_CODING_ERROR
Param(char const *name)
Construct with or implicitly convert from name.
SdfPredicateLibrary & Define(char const *name, Fn &&fn)
bool IsEmpty() const
Returns true iff this value is empty.
Definition: value.h:1272
GLenum const GLfloat * params
Definition: glcorearb.h:105
std::decay_t< decltype(make_index_sequence_impl< N >())> make_index_sequence
Definition: Types.h:234
SdfPredicateFunctionResult(bool value, Constancy constancy=MayVary)
Construct with value and constancy.
iterator find(const Key &key)
Definition: robin_map.h:493
#define TF_RUNTIME_ERROR
Constancy GetConstancy() const
Return the result constancy.
GLuint GLuint end
Definition: glcorearb.h:475
T UncheckedRemove()
Definition: value.h:1044
GLuint const GLchar * name
Definition: glcorearb.h:786
SdfPredicateLibrary & operator=(SdfPredicateLibrary const &other)
Copy-assignment from an other library.
SdfPredicateLibrary(SdfPredicateLibrary const &other)
Copy-construct from an other library.
#define SDF_API
Definition: api.h:40
GLenum GLfloat param
Definition: glcorearb.h:104
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1441
SDF_API bool CheckValidity() const
GLuint index
Definition: glcorearb.h:786
#define TF_UNUSED(x)
Definition: tf.h:185
GLuint GLfloat * val
Definition: glcorearb.h:1608
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:91
std::vector< Param > const & GetParams() const &
Return a reference to the parameters in a vector.
GA_API const UT_StringHolder N
**If you just want to fire and args
Definition: thread.h:609
constexpr SdfPredicateFunctionResult()
Default construction produces a 'false' result that 'MayVary'.
SdfPredicateLibrary & operator=(SdfPredicateLibrary &&other)=default
Move-assignment from an other library.
Definition: core.h:1131
SdfPredicateLibrary & Define(std::string const &name, Fn &&fn, NamesAndDefaults const &namesAndDefaults)
#define const
Definition: zconf.h:214
typename Tf_GetFuncSig< Fn >::Type TfFunctionTraits
iterator begin() noexcept
Definition: robin_map.h:218
Definition: value.h:167
SdfPredicateLibrary()=default
Default constructor produces an empty library.
typename Tf_MetaApplyImpl< Cls, TypeList >::Type TfMetaApply
Definition: meta.h:53
SdfPredicateParamNamesAndDefaults(std::initializer_list< Param > const &params)
Construct or implicitly convert from initializer_list<Param>.
PcpNodeRef_ChildrenIterator begin(const PcpNodeRef::child_const_range &r)
Support for range-based for loops for PcpNodeRef children ranges.
Definition: node.h:483