HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pyEnum.h
Go to the documentation of this file.
1 //
2 // Copyright 2016 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_BASE_TF_PY_ENUM_H
25 #define PXR_BASE_TF_PY_ENUM_H
26 
27 /// \file tf/pyEnum.h
28 /// Provide facilities for wrapping enums for script.
29 
30 #include "pxr/pxr.h"
31 
32 #include "pxr/base/tf/api.h"
34 #include "pxr/base/tf/pyUtils.h"
35 #include "pxr/base/tf/type.h"
36 
37 #include "pxr/base/arch/demangle.h"
38 #include "pxr/base/tf/enum.h"
39 #include "pxr/base/tf/hash.h"
40 #include "pxr/base/tf/hashmap.h"
41 #include "pxr/base/tf/iterator.h"
42 #include "pxr/base/tf/singleton.h"
44 
45 #include <hboost/python/class.hpp>
46 #include <hboost/python/converter/from_python.hpp>
47 #include <hboost/python/converter/registered.hpp>
48 #include <hboost/python/converter/rvalue_from_python_data.hpp>
49 #include <hboost/python/list.hpp>
50 #include <hboost/python/object.hpp>
51 #include <hboost/python/operators.hpp>
52 #include <hboost/python/refcount.hpp>
53 #include <hboost/python/scope.hpp>
54 #include <hboost/python/to_python_converter.hpp>
55 #include <hboost/python/tuple.hpp>
56 
57 #include <string>
58 
60 
61 /// \class Tf_PyEnum
62 ///
63 /// Base class of all python enum classes.
64 class Tf_PyEnum { };
65 
66 /// \class Tf_PyEnumRegistry
67 ///
68 /// This is a private class that manages registered enum objects.
69 /// \private
71 
72  public:
74 
75  private:
77  virtual ~Tf_PyEnumRegistry();
78  friend class TfSingleton<This>;
79 
80  public:
81 
82  TF_API static This &GetInstance() {
84  }
85 
86  TF_API
87  void RegisterValue(TfEnum const &e, hboost::python::object const &obj);
88 
89  template <typename T>
91  // Register conversions to and from python.
92  hboost::python::to_python_converter<T, _EnumToPython<T> >();
93  _EnumFromPython<T>();
94  }
95 
96  private:
97 
98  template <typename T>
99  struct _EnumFromPython {
100  _EnumFromPython() {
102  (&convertible, &construct, hboost::python::type_id<T>());
103  }
104  static void *convertible(PyObject *obj) {
106  Tf_PyEnumRegistry::GetInstance()._objectsToEnums;
108  i = o2e.find(obj);
109  // In the case of producing a TfEnum or an integer, any
110  // registered enum type is fine. In all other cases, the
111  // enum types must match.
114  return i != o2e.end() ? obj : 0;
115  else
116  return (i != o2e.end() && i->second.IsA<T>()) ? obj : 0;
117  }
118  static void construct(PyObject *src, hboost::python::converter::
119  rvalue_from_python_stage1_data *data) {
120  void *storage =
121  ((hboost::python::converter::
122  rvalue_from_python_storage<T> *)data)->storage.bytes;
123  new (storage) T(_GetEnumValue(src, (T *)0));
124  data->convertible = storage;
125  }
126  private:
127  // Overloads to explicitly allow conversion of the TfEnum integer
128  // value to other enum/integral types.
129  template <typename U>
130  static U _GetEnumValue(PyObject *src, U *) {
131  return U(Tf_PyEnumRegistry::GetInstance()._objectsToEnums[src].
132  GetValueAsInt());
133  }
134  static TfEnum _GetEnumValue(PyObject *src, TfEnum *) {
135  return Tf_PyEnumRegistry::GetInstance()._objectsToEnums[src];
136  }
137  };
138 
139  template <typename T>
140  struct _EnumToPython {
141  static PyObject *convert(T const &t);
142  };
143 
144  // Since our enum objects live as long as the registry does, we can use the
145  // pointer values for a hash.
146  struct _ObjectHash {
147  size_t operator()(PyObject *o) const {
148  return reinterpret_cast<size_t>(o);
149  }
150  };
151 
154 
155 };
156 
158 
159 // Private function used for __repr__ of wrapped enum types.
160 TF_API
162 
163 // Private base class for types which are instantiated and exposed to python
164 // for each registered enum type.
166  : public Tf_PyEnum, hboost::totally_ordered<Tf_PyEnumWrapper>
167 {
169 
171  name(n), value(val) {}
172  long GetValue() const {
173  return value.GetValueAsInt();
174  }
176  return name;
177  }
180  }
182  return TfEnum::GetFullName(value);
183  }
184  friend bool operator ==(Tf_PyEnumWrapper const &self,
185  long other) {
186  return self.value.GetValueAsInt() == other;
187  }
188 
189  friend bool operator ==(Tf_PyEnumWrapper const &lhs,
190  Tf_PyEnumWrapper const &rhs) {
191  return lhs.value == rhs.value;
192  }
193 
194  friend bool operator <(Tf_PyEnumWrapper const &lhs,
195  Tf_PyEnumWrapper const &rhs)
196  {
197  // If same, not less.
198  if (lhs == rhs)
199  return false;
200  // If types don't match, string compare names.
201  if (!lhs.value.IsA(rhs.value.GetType()))
202  return TfEnum::GetFullName(lhs.value) <
204  // If types do match, numerically compare values.
205  return lhs.GetValue() < rhs.GetValue();
206  }
207 
208  //
209  // XXX Bitwise operators for Enums are a temporary measure to support the
210  // use of Enums as Bitmasks in libSd. It should be noted that Enums are
211  // NOT closed under these operators. The proper place for such operators
212  // is in a yet-nonexistent Bitmask type.
213  //
214 
215  friend TfEnum operator |(Tf_PyEnumWrapper const &lhs,
216  Tf_PyEnumWrapper const &rhs) {
217  if (lhs.value.IsA(rhs.value.GetType())) {
218  return TfEnum(lhs.value.GetType(),
219  lhs.value.GetValueAsInt() |
220  rhs.value.GetValueAsInt());
221  }
222  TfPyThrowTypeError("Enum type mismatch");
223  return TfEnum();
224  }
225  friend TfEnum operator |(Tf_PyEnumWrapper const &lhs, long rhs) {
226  return TfEnum(lhs.value.GetType(), lhs.value.GetValueAsInt() | rhs);
227  }
228  friend TfEnum operator |(long lhs, Tf_PyEnumWrapper const &rhs) {
229  return TfEnum(rhs.value.GetType(), lhs | rhs.value.GetValueAsInt());
230  }
231 
232  friend TfEnum operator &(Tf_PyEnumWrapper const &lhs,
233  Tf_PyEnumWrapper const &rhs) {
234  if (lhs.value.IsA(rhs.value.GetType())) {
235  return TfEnum(lhs.value.GetType(),
236  lhs.value.GetValueAsInt() &
237  rhs.value.GetValueAsInt());
238  }
239  TfPyThrowTypeError("Enum type mismatch");
240  return TfEnum();
241  }
242  friend TfEnum operator &(Tf_PyEnumWrapper const &lhs, long rhs) {
243  return TfEnum(lhs.value.GetType(), lhs.value.GetValueAsInt() & rhs);
244  }
245  friend TfEnum operator &(long lhs, Tf_PyEnumWrapper const &rhs) {
246  return TfEnum(rhs.value.GetType(), lhs & rhs.value.GetValueAsInt());
247  }
248 
249  friend TfEnum operator ^(Tf_PyEnumWrapper const &lhs,
250  Tf_PyEnumWrapper const &rhs) {
251  if (lhs.value.IsA(rhs.value.GetType())) {
252  return TfEnum(lhs.value.GetType(),
253  lhs.value.GetValueAsInt() ^
254  rhs.value.GetValueAsInt());
255  }
256  TfPyThrowTypeError("Enum type mismatch");
257  return TfEnum();
258  }
259  friend TfEnum operator ^(Tf_PyEnumWrapper const &lhs, long rhs) {
260  return TfEnum(lhs.value.GetType(), lhs.value.GetValueAsInt() ^ rhs);
261  }
262  friend TfEnum operator ^(long lhs, Tf_PyEnumWrapper const &rhs) {
263  return TfEnum(rhs.value.GetType(), lhs ^ rhs.value.GetValueAsInt());
264  }
265 
266  friend TfEnum operator ~(Tf_PyEnumWrapper const &rhs) {
267  return TfEnum(rhs.value.GetType(), ~rhs.value.GetValueAsInt());
268  }
271 };
272 
273 template <typename T>
274 PyObject *
276 {
277  TfEnum e(t);
278 
279  // If there is no registered enum object, create a new one of
280  // the appropriate type.
281  if (!Tf_PyEnumRegistry::GetInstance()._enumsToObjects.count(e)) {
283  name = TfStringReplace(name, " ", "_");
284  name = TfStringReplace(name, "::", "_");
285  name = TfStringReplace(name, "<", "_");
286  name = TfStringReplace(name, ">", "_");
287  name = "AutoGenerated_" + name + "_" +
289 
290  hboost::python::object wrappedVal =
292 
293  wrappedVal.attr("_baseName") = std::string();
294 
296  }
297 
298  return hboost::python::
299  incref(Tf_PyEnumRegistry::GetInstance()._enumsToObjects[e]);
300 }
301 
302 // Private template class which is instantiated and exposed to python for each
303 // registered enum type.
304 template <typename T>
306 {
308  Tf_PyEnumWrapper(n, val) {}
309 
311  bool found = false;
312  const TfEnum value = TfEnum::GetValueFromName<T>(name, &found);
313  if (found) return hboost::python::object(value);
314  return hboost::python::object();
315  }
316 };
317 
318 // Removes the MFB package prefix from name if it starts with it, and replaces
319 // spaces with underscores.
320 TF_API
322 
323 // Adds attribute of given name with given value to given scope.
324 // Issues a coding error if attribute by that name already existed.
325 TF_API
326 void Tf_PyEnumAddAttribute(hboost::python::scope &s,
327  const std::string &name,
329 
330 /// \class TfPyWrapEnum
331 ///
332 /// Used to wrap enum types for script.
333 ///
334 /// TfPyWrapEnum provides a way to wrap enums for python, tying in with the \a
335 /// TfEnum system, and potentially providing automatic wrapping by using names
336 /// registered with the \a TfEnum system and by making some assumptions about
337 /// the way we structure our code. Enums that are not registered with TfEnum
338 /// may be manually wrapped using hboost::python::enum_ instead.
339 ///
340 /// Example usage. For an enum that looks like this:
341 /// \code
342 /// enum FooChoices {
343 /// FooFirst,
344 /// FooSecond,
345 /// FooThird
346 /// };
347 /// \endcode
348 ///
349 /// Which has been registered in the \a TfEnum system and has names provided for
350 /// all values, it may be wrapped like this:
351 /// \code
352 /// TfPyWrapEnum<FooChoices>();
353 /// \endcode
354 ///
355 /// The enum will appear in script as Foo.Choices.{First, Second, Third} and
356 /// the values will also appear as Foo.{First, Second, Third}.
357 ///
358 /// An enum may be given an explicit name by passing a string to
359 /// TfPyWrapEnum's constructor.
360 ///
361 /// If the enum is a C++11 scoped enum (aka enum class), the values will appear
362 /// as Foo.Choices.{First, Second, Third} in the following example:
363 /// \code
364 /// enum class FooChoices {
365 /// First,
366 /// Second,
367 /// Third
368 /// };
369 /// \endcode
370 ///
371 
372 // Detect scoped enums by using that the C++ standard does not allow them to
373 // be converted to int implicitly.
375 struct TfPyWrapEnum {
376 
377 private:
378  typedef hboost::python::class_<
379  Tf_TypedPyEnumWrapper<T>, hboost::python::bases<Tf_PyEnumWrapper> >
380  _EnumPyClassType;
381 
382 public:
383 
384  /// Construct an enum wrapper object.
385  /// If \a name is provided, it is used as the name of the enum. Otherwise
386  /// the type name of \a T is used, with a leading MFB package name
387  /// stripped.
388  explicit TfPyWrapEnum( std::string const &name = std::string())
389  {
390  using namespace hboost::python;
391 
392  const bool explicitName = !name.empty();
393 
394  // First, take either the given name, or the demangled type name.
395  std::string enumName = explicitName ? name :
396  TfStringReplace(ArchGetDemangled(typeid(T)), "::", ".");
397 
398  // If the name is dotted, take everything before the dot as the base
399  // name. This is used in repr.
400  std::string baseName = TfStringGetBeforeSuffix(enumName);
401  if (baseName == enumName)
402  baseName = std::string();
403 
404  // If the name is dotted, take the last element as the enum name.
405  if (!TfStringGetSuffix(enumName).empty())
406  enumName = TfStringGetSuffix(enumName);
407 
408  // If the name was not explicitly given, then clean it up by removing
409  // the mfb package name prefix if it exists.
410  if (!explicitName) {
411  if (!baseName.empty())
412  baseName = Tf_PyCleanEnumName(baseName);
413  else
414  enumName = Tf_PyCleanEnumName(enumName);
415  }
416 
417  if (IsScopedEnum) {
418  // Make the enumName appear in python representation
419  // for scoped enums.
420  if (!baseName.empty()) {
421  baseName += ".";
422  }
423  baseName += enumName;
424  }
425 
426  // Make a python type for T.
427  _EnumPyClassType enumClass(enumName.c_str(), no_init);
428  enumClass.def("GetValueFromName", &Tf_TypedPyEnumWrapper<T>::GetValueFromName, arg("name"));
429  enumClass.staticmethod("GetValueFromName");
430  enumClass.setattr("_baseName", baseName);
431 
432  // Register conversions for it.
434 
435  // Export values. Only clean names if basename is empty (i.e. the enum
436  // is top-level).
437  _ExportValues(baseName.empty(), enumClass);
438 
439  // Register with Tf so that python clients of a TfType
440  // that represents an enum are able to get to the equivalent
441  // python class with .pythonclass
442  const TfType &type = TfType::Find<T>();
443  if (!type.IsUnknown())
444  type.DefinePythonClass(enumClass);
445  }
446 
447  private:
448 
449  /// Export all values in this enum to the enclosing scope.
450  /// If no explicit names have been registered, this will export the TfEnum
451  /// registered names and values (if any).
452  void _ExportValues(bool cleanNames, _EnumPyClassType &enumClass) {
453  hboost::python::list valueList;
454 
455  std::vector<std::string> names = TfEnum::GetAllNames<T>();
456  TF_FOR_ALL(name, names) {
457  bool success = false;
458  TfEnum enumValue = TfEnum::GetValueFromName<T>(*name, &success);
459  if (!success) {
460  continue;
461  }
462 
463  std::string cleanedName = cleanNames ?
464  Tf_PyCleanEnumName(*name) : *name;
465 
466  // convert value to python.
467  Tf_TypedPyEnumWrapper<T> wrappedValue(cleanedName, enumValue);
468  hboost::python::object pyValue(wrappedValue);
469 
470  // register it as the python object for this value.
471  Tf_PyEnumRegistry::GetInstance().RegisterValue(enumValue, pyValue);
472 
473  // Take all the values and export them into the current scope.
474  std::string valueName = wrappedValue.GetName();
475  if (IsScopedEnum) {
476  // If scoped enum, enum values appear on the enumClass ...
477  hboost::python::scope s(enumClass);
478  Tf_PyEnumAddAttribute(s, valueName, pyValue);
479  } else {
480  // ... otherwise, enum values appear on the enclosing scope.
481  hboost::python::scope s;
482  Tf_PyEnumAddAttribute(s, valueName, pyValue);
483  }
484 
485  valueList.append(pyValue);
486  }
487 
488  // Add a tuple of all the values to the enum class.
489  enumClass.setattr("allValues", hboost::python::tuple(valueList));
490  }
491 
492 };
493 
495 
496 #endif // PXR_BASE_TF_PY_ENUM_H
GLdouble s
Definition: glew.h:1390
vbool4 insert(const vbool4 &a, bool val)
Helper: substitute val for a[i].
Definition: simd.h:3340
static T & GetInstance()
Definition: singleton.h:137
GLenum src
Definition: glew.h:2410
TfPyWrapEnum(std::string const &name=std::string())
Definition: pyEnum.h:388
GLuint const GLchar * name
Definition: glew.h:1814
#define TF_API
Definition: api.h:40
Tf_PyEnumWrapper(std::string const &n, TfEnum const &val)
Definition: pyEnum.h:170
iterator end()
Definition: hashmap.h:318
GLuint const GLfloat * val
Definition: glew.h:2794
TF_API std::string TfStringGetSuffix(const std::string &name, char delimiter= '.')
internal::named_arg< T, char > arg(string_view name, const T &arg)
Definition: core.h:1393
TF_API void RegisterValue(TfEnum const &e, hboost::python::object const &obj)
ARCH_API std::string ArchGetDemangled(const std::string &typeName)
std::string GetFullName() const
Definition: pyEnum.h:181
Tf_TypedPyEnumWrapper(std::string const &n, TfEnum const &val)
Definition: pyEnum.h:307
Definition: enum.h:139
Tto convert(const Tfrom &source)
GLhandleARB obj
Definition: glew.h:6236
friend TfEnum operator|(Tf_PyEnumWrapper const &lhs, Tf_PyEnumWrapper const &rhs)
Definition: pyEnum.h:215
friend bool operator<(Tf_PyEnumWrapper const &lhs, Tf_PyEnumWrapper const &rhs)
Definition: pyEnum.h:194
friend TfEnum operator^(Tf_PyEnumWrapper const &lhs, Tf_PyEnumWrapper const &rhs)
Definition: pyEnum.h:249
long GetValue() const
Definition: pyEnum.h:172
TF_API std::string Tf_PyCleanEnumName(std::string name)
friend bool operator==(Tf_PyEnumWrapper const &self, long other)
Definition: pyEnum.h:184
Tf_PyEnumWrapper This
Definition: pyEnum.h:168
GLint GLenum GLsizei GLint GLsizei const void * data
Definition: glew.h:1379
const std::type_info & GetType() const
Returns the type of the enum value, as an std::type_info.
Definition: enum.h:222
void RegisterEnumConversions()
Definition: pyEnum.h:90
static TF_API This & GetInstance()
Definition: pyEnum.h:82
GLuint object
Definition: glew.h:8986
GLuint const GLuint * names
Definition: glew.h:2690
GLsizei n
Definition: glew.h:4040
static TF_API std::string GetFullName(TfEnum val)
TF_API_TEMPLATE_CLASS(TfSingleton< Tf_PyEnumRegistry >)
const int & GetValueAsInt() const
Returns the integral value of the enum value.
Definition: enum.h:227
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1253
TfEnum value
Definition: pyEnum.h:270
friend TfEnum operator&(Tf_PyEnumWrapper const &lhs, Tf_PyEnumWrapper const &rhs)
Definition: pyEnum.h:232
std::string name
Definition: pyEnum.h:269
friend TfEnum operator~(Tf_PyEnumWrapper const &rhs)
Definition: pyEnum.h:266
GLsizei const GLchar *const * string
Definition: glew.h:1844
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1346
static TF_API std::string GetDisplayName(TfEnum val)
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:91
TF_API void TfPyThrowTypeError(std::string const &msg)
Tf_PyEnumRegistry This
Definition: pyEnum.h:73
Definition: type.h:64
TF_API void Tf_PyEnumAddAttribute(hboost::python::scope &s, const std::string &name, const hboost::python::object &value)
getOption("OpenEXR.storage") storage
Definition: HDK_Image.dox:276
std::string GetDisplayName() const
Definition: pyEnum.h:178
static hboost::python::object GetValueFromName(const std::string &name)
Definition: pyEnum.h:310
#define TF_FOR_ALL(iter, c)
Definition: iterator.h:390
std::string GetName() const
Definition: pyEnum.h:175
TF_API std::string TfStringGetBeforeSuffix(const std::string &name, char delimiter= '.')
std::enable_if<!std::is_enum< T >::value, std::string >::type TfStringify(const T &v)
Definition: stringUtils.h:523
TF_API std::string Tf_PyEnumRepr(hboost::python::object const &self)
bool IsA() const
True if *this has been assigned any enumerated value of type T.
Definition: enum.h:211
GLsizei const GLfloat * value
Definition: glew.h:1849
GLdouble GLdouble t
Definition: glew.h:1398
TF_API std::string TfStringReplace(const std::string &source, const std::string &from, const std::string &to)