Namespace lsst::utils¶
-
namespace
utils
Functions
-
std::string
demangleType
(std::string const _typeName)
-
std::size_t
hashCombine
(std::size_t seed) Combine hashes
A specialization of hashCombine for a trivial argument list.
-
template<typename
T
, typename ...Rest
>
std::size_thashCombine
(std::size_t seed, const T &value, Rest... rest) Combine hashes
This is provided as a convenience for those who need to hash a composite. C++11 includes std::hash, but neglects to include a facility for combining hashes.
Shall not throw exceptions.
- Return
A combined hash for all the arguments after
seed
.- Template Parameters
TRest
: the types to hash. All types must have a valid (in particular, non-throwing) specialization of std::hash.
- Parameters
seed
: An arbitrary starting value.valuerest
: The objects to hash.
To use it:
// Arbitrary seed; can change to get different hashes of same argument list std::size_t seed = 0; result = hashCombine(seed, obj1, obj2, obj3);
-
template<typename
InputIterator
>
std::size_thashIterable
(std::size_t seed, InputIterator begin, InputIterator end) Combine hashes in an iterable.
This is provided as a convenience for those who need to hash a container.
Shall not throw exceptions.
- Return
A combined hash for all the elements in [begin, end).
- Template Parameters
InputIterator
: an iterator to the objects to be hashed. The pointed-to type must have a valid (in particular, non-throwing) specialization of std::hash.
- Parameters
seed
: An arbitrary starting value.beginend
: The range to hash.
To use it:
// Arbitrary seed; can change to get different hashes of same argument list std::size_t seed = 0; result = hashIterable(seed, container.begin(), container.end());
-
double
nanojanskyToABMagnitude
(double flux) Convert a flux in nanojansky to AB magnitude.
-
double
ABMagnitudeToNanojansky
(double magnitude) Convert an AB magnitude to a flux in nanojansky.
-
std::string
getPackageDir
(std::string const &packageName) return the root directory of a setup package
- Parameters
[in] packageName
: name of package (e.g. “utils”)
- Exceptions
lsst::pex::exceptions::NotFoundError
: if desired version can’t be found
-
template<typename
T
>
constexpr voidassertValidHash
() Compile-time test of whether a specialization of std::hash conforms to the general spec.
The function itself is a no-op.
- Template Parameters
T
: The properties ofstd::hash<T>
will be tested.
-
template<typename
T
>
voidassertHashesEqual
(T obj1, T obj2) Test that equal objects have equal hashes.
If objects of type
T
can be equal despite having different internal representations, you should include pairs of such objects.- Template Parameters
T
: A hashable type.
- Parameters
obj1obj2
: Two equal objects.
Variables
-
const double
referenceFlux
= 1e23 * pow(10, ) * 1e9 The Oke & Gunn (1983) AB magnitude reference flux, in nJy (often approximated as 3631.0).
-
class
Backtrace
- #include <Backtrace.h>
Singleton, enables automatic backtraces on the following signals:
SIGABRT
SIGSEGV
SIGILL
SIGFPE
- Note
Uses low level malloc and fprintf since higher level constructs may not be available when a signal is received.
-
template<typename
Key
, typenameValue
, typenameKeyHash
, typenameKeyPred
>
classCache
- #include <Cache.h>
Cache of most recently used values
This object stores the most recent
maxElements
values, wheremaxElements
is set in the constructor. Objects (of typeValue
) are stored by a key (of typeKey
; hence the need to provide aKeyHash
andKeyPred
), and the class presents a dict-like interface. Objects may be added to (add
) and retrieved from (operator[]
) the cache. For ease of use, an interface (operator()
) is also provided that will check the cache for an existing key, and if the key is not present, generate it with a function provided by the user.- Note
Value
andKey
must be copyable.- Note
This header (
Cache.h
) should generally only be included in source files, not other header files, because you probably don’t want all of theboost::multi_index
includes in your header. We suggest you se theCacheFwd.h
file in your header instead, and hold theCache
as astd::unique_ptr
.- Note
Python bindings (for pybind11) are available in
lsst/utils/python/Cache.h
.
-
namespace
python
Functions
-
template<typename
Key
, typenameValue
, typenameKeyHash
= boost::hash<Key>, typenameKeyPred
= std::equal_to<Key>>
voiddeclareCache
(py::module &mod, std::string const &name)
Add
__eq__
and__ne__
methods based on two std::shared_ptr<T> pointing to the same addressExample:
- Template Parameters
T
: The type to which the std::shared_ptr points.PyClass
: The pybind11 class_ type; this can be automatically deduced.
lsst::afw::table records are considered equal if two
std::shared_ptr<record>
point to the same record. This is wrapped as follows forlsst::afw::table::BaseRecord
, wherecls
is an instance ofpybind11::class_<BaseRecord, std::shared_ptr<BaseRecord>>)
:utils::addSharedPtrEquality<BaseRecord>(cls);
Note that all record subclasses inherit this behavior without needing to call this function.
-
template<class
PyClass
>
voidaddOutputOp
(PyClass &cls, std::string const &method) Add
__str__
or__repr__
method implemented byoperator<<
.For flexibility, this method can be used to define one or both of
__str__
and__repr__
. It can also be used to define any Python method that takes no arguments and returns a string, regardless of name.- Template Parameters
PyClass
: The pybind11 class_ type. The wrapped class must support << as a stream output operator.
- Parameters
cls
: ThePyClass
object to which to add a wrapper.method
: The name of the method to implement. Should be"__str__"
or"__repr__"
.
-
template<class
PyClass
>
voidaddHash
(PyClass &cls) Add
__hash__
method implemented bystd::hash
.- Template Parameters
PyClass
: The pybind11 class_ type. The wrapped class must have an enabled specialization ofstd::hash
.
- Parameters
cls
: ThePyClass
object to which to add a wrapper.
-
std::size_t
cppIndex
(std::ptrdiff_t size, std::ptrdiff_t i) Compute a C++ index from a Python index (negative values count from the end) and range-check.
- Return
index in the range [0, size - 1]
- Note
the size argument has type std::ptrdiff_t instead of std::size_t in order to to match the allowed range for the i argument.
- Parameters
[in] size
: Number of elements in the collection.[in] i
: Index into the collection; negative values count from the end
- Exceptions
Python
: IndexError if i not in range [-size, size - 1]
-
std::pair<std::size_t, std::size_t>
cppIndex
(std::ptrdiff_t size_i, std::ptrdiff_t size_j, std::ptrdiff_t i, std::ptrdiff_t j) Compute a pair of C++ indices from a pair of Python indices (negative values count from the end) and range-check.
- Return
a std::pair of indices, each in the range [0, size - 1]
- Parameters
[in] size_i
: Number of elements along the first axis.[in] size_j
: Number of elements along the second axis.[in] i
: Index along first axis; negative values count from the end[in] j
: Index along second axis; negative values count from the end
- Exceptions
Python
: IndexError if either input index not in range [-size, size - 1]
- #include <PySharedPtr.h>
A shared pointer that tracks both a C++ object and its associated PyObject.
Each group of PySharedPtr for a given object collectively counts as one reference to that object for the purpose of Python garbage collection.
A PySharedPtr is implicitly convertible to and from a std::shared_ptr to minimize API impact. Any
shared_ptr
created this way will (I think) keep the Python reference alive, as described above.
-
class
TemplateInvoker
- #include <TemplateInvoker.h>
A helper class for wrapping C++ template functions as Python functions with dtype arguments.
TemplateInvoker takes a templated callable object, a
pybind11::dtype
object, and a sequence of supported C++ types via its nestedTag
struct. The callable is invoked with a scalar argument of the type matching thedtype
object. If none of the supported C++ types match, a different error callback is invoked instead.As an example, we’ll wrap this function:
template <typename T> T doSomething(std::string const & argument);
TemplateInvoker provides a default error callback, which we’ll use here (otherwise you’d need to pass one when constructing the TemplateInvoker).
For the main callback, we’ll define this helper struct:
struct DoSomethingHelper { template <typename T> T operator()(T) const { return doSomething<T>(argument); } std::string argument; };
The pybind11 wrapper for
doSomething
is then another lambda that usesTemplateInvoker::apply
to call the helper:mod.def( "doSomething", [](std::string const & argument, py::dtype const & dtype) { return TemplateInvoker().apply( DoSomethingHelper{argument}, dtype, TemplateInvoker::Tag<int, float, double>() ); }, "argument"_a );
The type returned by the helper callable’s
operator()
can be anything pybind11 knows how to convert to Python.While defining a full struct with a templated
operator()
makes it more obvious what TemplateInvoker is doing, it’s much more concise to use a universal lambda with thedecltype
operator. This wrapper is equivalent to the one above, but it doesn’t needDoSomethingHelper
:Note that the value ofmod.def( "doSomething", [](std::string const & argument, py::dtype const & dtype) { return TemplateInvoker().apply( [&argument](auto t) { return doSomething<decltype(t)>(argument); }, dtype, TemplateInvoker::Tag<int, float, double>() ); }, "argument"_a );
t
here doesn’t matter; what’s important is that its C++ type corresponds to the type passed in thedtype
argument. So instead of using that value, we use thedecltype
operator to extract that type and use it as a template parameter.
-
class
WrapperCollection
- #include <python.h>
A helper class for subdividing pybind11 module across multiple translation units (i.e. source files).
Merging wrappers for different classes into a single compiled module can dramatically decrease the total size of the binary, but putting the source for multiple wrappers into a single file slows down incremental rebuilds and makes editing unwieldy. The right approach is to define wrappers in different source files and link them into a single module at build time. In simple cases, that’s quite straightforward: pybind11 declarations are just regular C++ statements, and you can factor them out into different functions in different source files.
That approach doesn’t work so well when the classes being wrapped are interdependent, because bindings are only guaranteed to work when all types used in a wrapped method signature have been declared to pybind11 before the method using them is itself declared. Naively, then, each source file would thus have to have multiple wrapper-declaring functions, so all type-wrapping functions could be executed before any method-wrapping functions. Of course, each type-wrapping function would also have to pass its type object to at least one method-wrapping function (to wrap the types own methods), and the result is a tangled mess of wrapper-declaring functions that obfuscate the code with a lot of boilerplate.
WrapperCollection provides a way out of that by allowing type wrappers and their associated methods to be declared at a single point, but the method wrappers wrapped in a lambda to defer their execution. A single WrapperCollection instance is typically constructed at the beginning of a PYBIND11_MODULE block, then passed by reference to wrapper-declaring functions defined in other source files. As type and method wrappers are added to the WrapperCollection by those functions, the types are registered immediately, and the method-wrapping lambdas are collected. After all wrapper-declaring functions have been called, finish() is called at the end of the PYBIND11_MODULE block to execute the collecting method-wrapping lambdas.
Typical usage:
// _mypackage.cc void wrapClassA(WrapperCollection & wrappers); void wrapClassB(WrapperCollection & wrappers); PYBIND11_MODULE(_mypackage, module) { WrapperCollection wrappers(module, "mypackage"); wrapClassA(wrappers); wrapClassB(wrappers); wrappers.finish(); }
// _ClassA.cc void wrapClassA(WrapperCollection & wrappers) { wrappers.wrapType( py::class_<ClassA>(wrappers.module, "ClassA"), [](auto & mod, auto & cls) { cls.def("methodOnClassA", &methodOnClassA); } ); }
// _ClassB.cc void wrapClassB(WrapperCollection & wrappers) { wrappers.wrapType( py::class_<ClassB>(wrappers.module, "ClassB"), [](auto & mod, auto & cls) { cls.def("methodOnClassB", &methodOnClassB); mod.def("freeFunction", &freeFunction); } ); }
Note that we recommend the use of universal lambdas (i.e.
auto &
parameters) to reduce verbosity.
-
template<typename
-
std::string