All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
makePyConstructor.h
Go to the documentation of this file.
1//
2// Copyright 2016 Pixar
3//
4// Licensed under the terms set forth in the LICENSE.txt file available at
5// https://openusd.org/license.
6//
7
8#ifndef PXR_BASE_TF_MAKE_PY_CONSTRUCTOR_H
9#define PXR_BASE_TF_MAKE_PY_CONSTRUCTOR_H
10
14
15#ifndef TF_MAX_ARITY
16# define TF_MAX_ARITY 7
17#endif // TF_MAX_ARITY
18
19
20#include "pxr/pxr.h"
21#include "pxr/base/tf/api.h"
22#include "pxr/base/tf/functionTraits.h"
23#include "pxr/base/tf/refPtr.h"
24#include "pxr/base/tf/weakPtr.h"
26#include "pxr/base/tf/pyError.h"
27#include "pxr/base/tf/pyIdentity.h"
28#include "pxr/base/tf/pyUtils.h"
30
32
33#include "pxr/external/boost/python/def_visitor.hpp"
34#include "pxr/external/boost/python/dict.hpp"
35#include "pxr/external/boost/python/errors.hpp"
36#include "pxr/external/boost/python/list.hpp"
37#include "pxr/external/boost/python/object/iterator.hpp"
38#include "pxr/external/boost/python/raw_function.hpp"
39#include "pxr/external/boost/python/tuple.hpp"
40#include "pxr/external/boost/python/type_id.hpp"
41
42#include <array>
43#include <string>
44#include <type_traits>
45#include <utility>
46
47PXR_NAMESPACE_OPEN_SCOPE
48
49// Helper for wrapping objects that are held by weak pointers, but may also be
50// constructed from script. This lets one construct an object from script and
51// stores a ref pointer to the C++ object inside the python object. This way,
52// objects created from script are owned by script, but objects obtained from
53// the C++ API cannot be owned by script. When the owning python object is
54// collected, its ref pointer will go away and the C++ object will be
55// deallocated.
56//
57// Example usage:
58//
59// class_<MyClass, MyClassPtr>("MyClass", no_init)
60// .def(TfPyRefAndWeakPtr())
61// .def(TfMakePyConstructor(MyClass::New))
62// .def(...)
63// ...
64//
65// TfMakePyConstructorWithVarArgs may be used to wrap an object so that it
66// may be constructed with a variable number of positional and keyword
67// arguments. The last two arguments of the function being wrapped must
68// be a pxr_boost::python::tuple and dict. These will contain the remaining
69// positional and keyword args after required arguments are parsed.
70//
71// Example usage:
72//
73// static MyObjectRefPtr MyObjectFactory(
74// int formalArg1, const std::string& formalArg2,
75// const pxr_boost::python::tuple& args, const pxr_boost::python::dict& kwargs);
76//
77// class_<MyClass, MyClassPtr>("MyClass", no_init)
78// .def(TfPyRefAndWeakPtr())
79// .def(TfMakePyConstructorWithVarArgs(MyObjectFactory))
80// .def(...)
81//
82// NOTE: The current implementation does not handle pxr_boost::python::arg for
83// specifying keywords for required arguments.
84
85namespace Tf_MakePyConstructor {
86
87namespace bp = pxr_boost::python;
88
89template <typename CTOR>
90struct InitVisitor : bp::def_visitor<InitVisitor<CTOR> > {
91 friend class bp::def_visitor_access;
92 const std::string _doc;
93 InitVisitor(const std::string &doc = std::string()) : _doc(doc) {}
94
95 template <typename CLS>
96 void visit(CLS &c) const {
97 c.def("__init__", CTOR::template init_callable<CLS>(), _doc.c_str());
98 }
99
100 template <class CLS, class Options>
101 void visit(CLS &c, char const* name, Options& options) const {
102 // Note: we ignore options.doc() in favor of _doc
103 c.def(name, CTOR::template init_callable<CLS>(options), _doc.c_str());
104 }
105
106};
107
108TF_API
109bp::object _DummyInit(bp::tuple const & /* args */,
110 bp::dict const & /* kw */);
111
112template <typename CTOR>
113struct NewVisitor : bp::def_visitor<NewVisitor<CTOR> > {
114 friend class bp::def_visitor_access;
115 const std::string _doc;
116 NewVisitor(const std::string &doc = std::string()) : _doc(doc) {}
117
118 template <typename CLS>
119 void visit(CLS &c) const {
120 // If there's already a __new__ method, look through the staticmethod to
121 // get the underlying function, replace __new__ with that, then add the
122 // overload, and recreate the staticmethod. This is required because
123 // boost python needs to have all overloads exported before you say
124 // .staticmethod.
125
126 // Note that it looks like this should do nothing, but it actually does
127 // something! Here's what it does: looking up __new__ on c doesn't
128 // actually produce the staticmethod object -- it does a "descriptor
129 // __get__" which produces the underlying function. Replacing __new__
130 // with that underlying thing has the effect of unwrapping the
131 // staticmethod, which is exactly what we want.
132 if (PyObject_HasAttrString(c.ptr(), "__new__"))
133 c.attr("__new__") = c.attr("__new__");
134 c.def("__new__", CTOR::template __new__<CLS>, _doc.c_str());
135 c.staticmethod("__new__");
136
137 //c.def("__init__", CTOR::template __init__<CLS>, _doc.c_str());
138 c.def("__init__", bp::raw_function(_DummyInit));
139 }
140
141 template <class CLS, class Options>
142 void visit(CLS &c, char const* name, Options& options) const {
143 // If there's already a __new__ method, look through the staticmethod to
144 // get the underlying function, replace __new__ with that, then add the
145 // overload, and recreate the staticmethod. This is required because
146 // boost python needs to have all overloads exported before you say
147 // .staticmethod.
148
149 // Note that it looks like this should do nothing, but it actually does
150 // something! Here's what it does: looking up __new__ on c doesn't
151 // actually produce the staticmethod object -- it does a "descriptor
152 // __get__" which produces the underlying function. Replacing __new__
153 // with that underlying thing has the effect of unwrapping the
154 // staticmethod, which is exactly what we want.
155 if (PyObject_HasAttrString(c.ptr(), "__new__"))
156 c.attr("__new__") = c.attr("__new__");
157 c.def("__new__", CTOR::template __new__<CLS>,
158 // Note: we ignore options.doc() in favor of _doc
159 _doc.c_str(),
160 options.keywords(),
161 options.policies()
162 );
163 c.staticmethod("__new__");
164
165 //c.def("__init__", CTOR::template __init__<CLS>, _doc.c_str());
166 c.def("__init__", bp::raw_function(_DummyInit));
167 }
168
169};
170
171
172typedef bp::object object;
173
174template <typename T>
175struct InstallPolicy {
176 static void PostInstall(object const &self, T const &t,
177 const void *) {}
178};
179
180// Specialize install policy for refptrs.
181template <typename T>
182struct InstallPolicy<TfRefPtr<T> > {
183 static_assert(Tf_SupportsUniqueChanged<T>::Value,
184 "Type T must support refcount unique changed notification.");
185 static void PostInstall(object const &self, TfRefPtr<T> const &ptr,
186 const void *uniqueId) {
187 // Stash a self-reference ref ptr into the python object that will
188 // keep the object alive. Need to get a ref ptr to the held type,
189 // since that's what's wrapped.
190 Tf_PyAddPythonOwnership(ptr, uniqueId, self.ptr());
191 }
192};
193
194template <typename CLS, typename T>
195void Install(object const &self, T const &t, TfErrorMark const &m) {
196 // Stick the weakptr into the python object self to complete
197 // construction.
198 typedef typename CLS::metadata::holder Holder;
199 typedef typename bp::objects::instance<Holder> instance_t;
200 typedef InstallPolicy<T> Policy;
201 typedef typename CLS::metadata::held_type HeldType;
202
203 // CODE_COVERAGE_OFF
204 void *memory = Holder::
205 // CODE_COVERAGE_ON
206 allocate(self.ptr(), offsetof(instance_t, storage), sizeof(Holder));
207 try {
208 HeldType held(t);
209 Holder *holder = (new (memory) Holder(held));
210 // If there was a TfError, raise that back to python.
211 if (TfPyConvertTfErrorsToPythonException(m))
212 bp::throw_error_already_set();
213 // If no TfError, but object construction failed, raise a generic error
214 // back to python.
215 if (!held)
216 TfPyThrowRuntimeError("could not construct " +
217 ArchGetDemangled(typeid(HeldType)));
218 bp::detail::initialize_wrapper(self.ptr(), &(*(held.operator->())));
219 holder->install(self.ptr());
220
221 // Set object identity
222 Tf_PySetPythonIdentity(held, self.ptr());
223
224 Policy::PostInstall(self, t, held.GetUniqueIdentifier());
225
226 } catch(...) {
227 Holder::deallocate(self.ptr(), memory); throw;
228 }
229
230}
231
232
233template <typename WeakPtr, typename P>
234struct _RefPtrFactoryConverter {
235 typedef std::remove_reference_t<P> Ptr;
236 bool convertible() const {
237 // FIXME should actually check here... It's not really horrible because
238 // if the conversion will fail, we'll just get a runtime error down
239 // below when we try to create the resulting object. That's basically
240 // what we want anyway.
241 return true;
242 }
243 PyObject *operator()(Ptr const &p) const {
244 typedef InstallPolicy<Ptr> Policy;
245 WeakPtr ptr(static_cast<typename WeakPtr::DataType *>
246 (get_pointer(p)));
247
248 // If resulting pointer is null, return None.
249 if (!ptr)
250 return bp::incref(Py_None);
251
252 // The to-python converter will set identity here.
253 object result(ptr);
254
255 Policy::PostInstall(result, p, ptr.GetUniqueIdentifier());
256 return bp::incref(result.ptr());
257 }
258 // Required for boost.python signature generator, in play when
259 // BOOST_PYTHON_NO_PY_SIGNATURES is undefined.
260 PyTypeObject const *get_pytype() const {
261 return pxr_boost::python::objects::registered_class_object(
262 pxr_boost::python::type_id<typename WeakPtr::DataType>()).get();
263 }
264};
265
266template <typename WeakPtr = void>
267struct RefPtrFactory {
268 template <typename FactoryResultPtr>
269 struct apply {
270 using WeakPtrType = std::conditional_t<
271 std::is_same<WeakPtr, void>::value,
273 WeakPtr>;
274 typedef _RefPtrFactoryConverter<WeakPtrType, FactoryResultPtr> type;
275 };
276};
277
278// EXTRA_ARITY is added for InitCtorWithVarArgs backwards compatability.
279// The previous BOOST_PP implementation didn't count the tuple and dict
280// against the arity limit while the new version does. A future change
281// should remove EXTRA_ARITY and increase TF_MAX_ARITY now that the
282// implementations are templated and no longer generated by BOOST_PP
283template <typename SIG, size_t EXTRA_ARITY = 0>
284struct CtorBase {
285 typedef SIG Sig;
286 using Traits = TfFunctionTraits<SIG*>;
287 static_assert(Traits::Arity <= (TF_MAX_ARITY + EXTRA_ARITY));
288
289 static Sig *_func;
290 static void SetFunc(Sig *func) {
291 if (!_func)
292 _func = func;
293 else {
294 // CODE_COVERAGE_OFF
295 TF_CODING_ERROR("Ctor with signature '%s' is already registered. "
296 "Duplicate will be ignored.",
297 ArchGetDemangled(typeid(Sig)).c_str());
298 // CODE_COVERAGE_ON
299 }
300 }
301};
302
303template <typename SIG, size_t EXTRA_ARITY>
304SIG *CtorBase<SIG, EXTRA_ARITY>::_func = nullptr;
305
306template <typename SIG> struct InitCtor;
307template <typename SIG> struct InitCtorWithBackReference;
308template <typename SIG> struct InitCtorWithVarArgs;
309template <typename SIG> struct NewCtor;
310template <typename SIG> struct NewCtorWithClassReference;
311
312}
313
314
315template <typename T>
316Tf_MakePyConstructor::InitVisitor
317<typename Tf_MakePyConstructor::InitCtor<T> >
318TfMakePyConstructor(T *func, const std::string &doc = std::string()) {
319 // Instantiate to set static constructor pointer, then return the visitor.
320 Tf_MakePyConstructor::InitCtor<T> Ctor(func);
321 return Tf_MakePyConstructor::InitVisitor
322 <Tf_MakePyConstructor::InitCtor<T> >(doc);
323}
324
325template <typename T>
326Tf_MakePyConstructor::InitVisitor
327<typename Tf_MakePyConstructor::InitCtorWithBackReference<T> >
328TfMakePyConstructorWithBackReference(T *func, const std::string &doc = std::string()) {
329 // Instantiate to set static constructor pointer, then return the visitor.
330 Tf_MakePyConstructor::InitCtorWithBackReference<T> Ctor(func);
331 return Tf_MakePyConstructor::InitVisitor
332 <Tf_MakePyConstructor::InitCtorWithBackReference<T> >(doc);
333}
334
335template <typename T>
336Tf_MakePyConstructor::InitVisitor
337<typename Tf_MakePyConstructor::InitCtorWithVarArgs<T> >
338TfMakePyConstructorWithVarArgs(T *func, const std::string &doc = std::string()) {
339 // Instantiate to set static constructor pointer, then return the visitor.
340 Tf_MakePyConstructor::InitCtorWithVarArgs<T> Ctor(func);
341 return Tf_MakePyConstructor::InitVisitor
342 <Tf_MakePyConstructor::InitCtorWithVarArgs<T> >(doc);
343}
344
345template <typename T>
346Tf_MakePyConstructor::NewVisitor
347<typename Tf_MakePyConstructor::NewCtor<T> >
348TfMakePyNew(T *func, const std::string &doc = std::string()) {
349 // Instantiate to set static constructor pointer, then return the visitor.
350 Tf_MakePyConstructor::NewCtor<T> Ctor(func);
351 return Tf_MakePyConstructor::NewVisitor
352 <Tf_MakePyConstructor::NewCtor<T> >(doc);
353}
354
355template <typename T>
356Tf_MakePyConstructor::NewVisitor
357<typename Tf_MakePyConstructor::NewCtorWithClassReference<T> >
358TfMakePyNewWithClassReference(T *func, const std::string &doc = std::string()) {
359 // Instantiate to set static constructor pointer, then return the visitor.
360 Tf_MakePyConstructor::NewCtorWithClassReference<T> Ctor(func);
361 return Tf_MakePyConstructor::NewVisitor
362 <Tf_MakePyConstructor::NewCtorWithClassReference<T> >(doc);
363}
364
365
366template <typename T = void>
367struct TfPyRefPtrFactory : public Tf_MakePyConstructor::RefPtrFactory<T> {};
368
369template <typename T> struct Tf_PySequenceToListConverterRefPtrFactory;
370
374 template <typename T>
375 struct apply {
376 typedef Tf_PySequenceToListConverterRefPtrFactory<T> type;
377 };
378};
379
380// XXX: would be nicer to be able to compose converters with factory
381template <typename T>
382struct Tf_PySequenceToListConverterRefPtrFactory {
383 typedef std::remove_reference_t<T> SeqType;
384 bool convertible() const {
385 return true;
386 }
387 PyObject *operator()(T seq) const {
388 using namespace pxr_boost::python;
389
390 typedef typename Tf_MakePyConstructor::RefPtrFactory<>::
391 apply<typename SeqType::value_type>::type RefPtrFactory;
392
393 pxr_boost::python::list l;
394 for (typename SeqType::const_iterator i = seq.begin();
395 i != seq.end(); ++i) {
396 l.append(object(handle<>(RefPtrFactory()(*i))));
397 }
398 return pxr_boost::python::incref(l.ptr());
399 }
400 // Required for boost.python signature generator, in play when
401 // BOOST_PYTHON_NO_PY_SIGNATURES is undefined.
402 PyTypeObject const *get_pytype() const {
403 return &PyList_Type;
404 }
405};
406
407namespace Tf_MakePyConstructor {
408
409template <typename R, typename... Args>
410struct InitCtor<R(Args...)> : CtorBase<R(Args...)> {
411 typedef CtorBase<R(Args...)> Base;
412 typedef typename Base::Sig Sig;
413
414 InitCtor(Sig* func) { Base::SetFunc(func); }
415
416 template <typename CLS>
417 static bp::object init_callable() {
418 return bp::make_function(__init__<CLS>);
419 }
420
421 template <typename CLS, typename Options>
422 static bp::object init_callable(Options& o) {
423 return bp::make_function(__init__<CLS>, o.policies(), o.keywords()) ;
424 }
425
426 template <typename CLS>
427 static void __init__(object &self, Args... args) {
428 TfErrorMark m;
429 Install<CLS>(self, Base::_func(args...), m);
430 }
431};
432
433template <typename R, typename... Args>
434struct NewCtor<R(Args...)> : CtorBase<R(Args...)> {
435 typedef CtorBase<R(Args...)> Base;
436 typedef typename Base::Sig Sig;
437 NewCtor(Sig *func) { Base::SetFunc(func); }
438
439 template <class CLS>
440 static bp::object __new__(object &cls, Args... args) {
441 typedef typename CLS::metadata::held_type HeldType;
442 TfErrorMark m;
443 R r((Base::_func(args...)));
444 HeldType h((r));
445 if (TfPyConvertTfErrorsToPythonException(m))
446 bp::throw_error_already_set();
447 bp::object ret = TfPyObject(h);
448 if (TfPyIsNone(ret))
449 TfPyThrowRuntimeError("could not construct " +
450 ArchGetDemangled(typeid(HeldType)));
451
452 bp::detail::initialize_wrapper(ret.ptr(), get_pointer(h));
453 // make the object have the right class.
454 bp::setattr(ret, "__class__", cls);
455
456 InstallPolicy<R>::PostInstall(ret, r, h.GetUniqueIdentifier());
457 return ret;
458 }
459};
460
461template <typename R, typename... Args>
462struct InitCtorWithVarArgs<R(Args...)> :
463 // Pad the arity for backwards compatability
464 CtorBase<R(Args...), /*EXTRA_ARITY*/ 2> {
465 typedef CtorBase<R(Args...), /*EXTRA_ARITY*/ 2> Base;
466 typedef typename Base::Sig Sig;
467
468 // Ideally, Arity would be pulled from Base::Traits, but
469 // compilers have inconsistently allowed this. Redefine
470 // Arity as a workaround for now.
471 using Arity = TfMetaLength<Args...>;
472
473 InitCtorWithVarArgs(Sig *func) { Base::SetFunc(func); }
474
475 static_assert((Arity::value >= 2) &&
476 (std::is_same_v<
477 const bp::tuple&,
478 typename Base::Traits::template NthArg<(Arity::value-2)>>) &&
479 (std::is_same_v<
480 const bp::dict&,
481 typename Base::Traits::template NthArg<(Arity::value-1)>>),
482 "InitCtorWithVarArgs requires a function of form "
483 "(..., const tuple&, const dict&)");
484
485 template <typename CLS>
486 static bp::object init_callable() {
487 // Specify min_args as 1 to account for just the 'self' argument.
488 // min_args really should be N + 1. However, we want to do this check
489 // ourselves later so we can emit a better error message.
490 return bp::raw_function(__init__<CLS>, /* min_args = */ 1);
491 }
492
493 template <typename CLS, typename Options>
494 static bp::object init_callable(Options& options) {
495 // XXX: Note ignoring options.keywords(), current implementation can't
496 // handle that correctly.
497 return bp::raw_function(
498 bp::make_function(__init__<CLS>, options.policies()),
499 /* min_args = */ 1);
500 }
501
502 template <typename CLS, size_t... I>
503 static bp::object __init__impl(const bp::tuple& args,
504 const bp::dict& kwargs,
505 std::index_sequence<I...>) {
506 TfErrorMark m;
507
508 // We know that there are at least two args because the specialization only
509 // matches against (..., *args, **kwargs)
510 const unsigned int expectedNamedArgs = Arity::value - 2;
511 // self is included in the tuple, so it should always be at least 1
512 const unsigned int positionalArgs = bp::len(args) - 1;
513 if (positionalArgs < expectedNamedArgs) {
514 std::array<std::string, Arity::value - 2>
515 positionalArgTypes = {{
516 (bp::type_id<typename Base::Traits::template NthArg<I>>().name())...
517 }};
518 std::string joinedTypes = TfStringJoin(
519 std::begin(positionalArgTypes),
520 std::end(positionalArgTypes), ", "
521 );
522 if (!joinedTypes.empty()) {
523 joinedTypes += ", ";
524 }
525 // User didn't provide enough positional arguments for the factory
526 // function. Complain.
529 "Arguments to __init__ did not match C++ signature:\n"
530 "\t__init__(%s...)", joinedTypes.c_str()
531 )
532 );
533 return bp::object();
534 }
535
536 Install<CLS>(
537 // self object for new instance is the first arg to __init__
538 args[0],
539 Base::_func(
540 bp::extract<
541 std::remove_reference_t<
542 typename Base::Traits::template NthArg<I>>>(args[I + 1])...,
543 bp::tuple(args.slice(expectedNamedArgs + 1, bp::len(args))), kwargs),
544 m);
545
546 return bp::object();
547 }
548
549 template <typename CLS>
550 static bp::object __init__(const bp::tuple& args,
551 const bp::dict& kwargs) {
552 return __init__impl<CLS>(
553 args, kwargs, std::make_index_sequence<Arity::value - 2>());
554
555 }
556};
557
558// This is a variant of Ctor which includes a back reference to self
559// (the Python object being initialized) in the args passed to the
560// constructor. This is used to expose the factory methods for classes
561// which we expect to subclass in Python. When the constructor is called,
562// it can examine self and initialize itself appropriately.
563template <typename R, typename SelfRef, typename... Args>
564struct InitCtorWithBackReference<R(SelfRef, Args...)> :
565 CtorBase<R(SelfRef, Args...)> {
566 typedef CtorBase<R(SelfRef, Args...)> Base;
567 typedef typename Base::Sig Sig;
568 InitCtorWithBackReference(Sig *func) { Base::SetFunc(func); }
569
570 template <typename CLS>
571 static bp::object init_callable() {
572 return bp::make_function(__init__<CLS>);
573 }
574
575 template <typename CLS, typename Options>
576 static bp::object init_callable(Options& o) {
577 return bp::make_function(__init__<CLS>, o.policies(), o.keywords());
578 }
579
580 template <typename CLS>
581 static void __init__(SelfRef self, Args... args) {
582 TfErrorMark m;
583 Install<CLS>(self, Base::_func(self, args...), m);
584 }
585};
586
587template <typename R, typename ClsRef, typename... Args>
588struct NewCtorWithClassReference<R(ClsRef, Args...)> :
589 CtorBase<R(ClsRef, Args...)> {
590 typedef CtorBase<R(ClsRef, Args...)> Base;
591 typedef typename Base::Sig Sig;
592 NewCtorWithClassReference(Sig *func) { Base::SetFunc(func); }
593
594 template <class CLS>
595 static bp::object __new__(ClsRef cls, Args... args) {
596 typedef typename CLS::metadata::held_type HeldType;
597 TfErrorMark m;
598 R r(Base::_func(cls, args...));
599 HeldType h(r);
600 if (TfPyConvertTfErrorsToPythonException(m))
601 bp::throw_error_already_set();
602 bp::object ret = TfPyObject(h);
603 if (TfPyIsNone(ret))
604 TfPyThrowRuntimeError("could not construct " +
605 ArchGetDemangled(typeid(HeldType)));
606
607 bp::detail::initialize_wrapper(ret.ptr(), get_pointer(h));
608 // make the object have the right class.
609 bp::setattr(ret, "__class__", cls);
610
611 InstallPolicy<R>::PostInstall(ret, r, h.GetUniqueIdentifier());
612 return ret;
613 }
614};
615}
616
617PXR_NAMESPACE_CLOSE_SCOPE
618
619#endif // PXR_BASE_TF_MAKE_PY_CONSTRUCTOR_H
Low-level utilities for informing users of various internal and external diagnostic conditions.
Miscellaneous Utilities for dealing with script.
TF_API void TfPyThrowRuntimeError(const char *msg)
Raises a Python RuntimeError with the given error msg and throws a pxr_boost::python::error_already_s...
TF_API bool TfPyIsNone(pxr_boost::python::object const &obj)
Return true iff obj is None.
pxr_boost::python::object TfPyObject(T const &t, bool complainOnFailure=true)
Return a python object for the given C++ object, loading the appropriate wrapper code if necessary.
Definition: pyUtils.h:127
TF_API void TfPyThrowTypeError(const char *msg)
Raises a Python TypeError with the given error msg and throws a pxr_boost::python::error_already_set ...
Class used to record the end of the error-list.
Definition: errorMark.h:48
Reference-counted smart pointer utility class.
Definition: refPtr.h:590
Pointer storage with deletion detection.
Definition: weakPtr.h:128
Demangle C++ typenames generated by the typeid() facility.
std::string ArchGetDemangled()
Return demangled RTTI generated-type name.
Definition: demangle.h:86
#define TF_CODING_ERROR(fmt, args)
Issue an internal programming error, but continue execution.
Definition: diagnostic.h:68
std::string TfStringJoin(ForwardIterator begin, ForwardIterator end, const char *separator=" ")
Concatenates the strings (begin, end), with default separator.
Definition: stringUtils.h:358
TF_API std::string TfStringPrintf(const char *fmt,...)
Returns a string formed by a printf()-like specification.
A pxr_boost::python result converter generator which converts standard library sequences to lists of ...
Reference counting.
Definitions of basic string utilities in tf.
Pointer storage with deletion detection.