Containers π
Raw LibraryLink containers like MTensor or MNumericArray store their element type as a regular field in the structure. This means that the type cannot be used at compile-time, which makes writing generic code that does something with the underlying data very difficult (lots of switches on the element type and code repetition).
On the other hand, having the element type as template parameter, like STL containers, is often inconvenient and requires some template magic for simple things like passing forward the container or reading metadata when the data type is not known a priori.
To get the best of both worlds and to make the library suitable for different needs, LLU provides two categories of container wrappers - generic, datatype-agnostic wrappers and full-fledged wrappers templated with the datatype. This is illustrated in the table below:
LibraryLink element |
Generic wrapper |
Typed wrapper |
---|---|---|
Memory management π
When passing a container from Wolfram Language to a C++ library, one of 4 passing modes must be chosen:
Automatic
Constant
Manual
Shared
With the exception of DataStore, which cannot be Constant or Shared.
More about memory management can be found in the LibraryLink documentation.
In plain LibraryLink, the choice you make is reflected only in the Wolfram Language code where LibraryFunctionLoad specifies the list of parameters for the library function. There is no way to query the WolframLibraryData or MArgument about the passing modes of function arguments from within C++ code. Therefore, the programmer must remember the passing mode for each argument and then ensure the correct action is taken (releasing/not releasing memory depending on the combination of passing mode and whether the container has been returned from the library function to the Wolfram Language).
LLU defines a notion of container ownership:
-
enum
LLU
::
Ownership
π An enum listing possible owners of a LibraryLink container.
Ownership determines the memory management of a container.
Values:
-
enumerator
LibraryLink
π LibraryLink is responsible for managing the containerβs memory. Corresponds to Automatic and βConstantβ passing.
-
enumerator
Library
π The library (LLU) is responsible for managing the containerβs memory. Used for Manual passing and containers created by the library.
When the container is shared LLU only needs to decrease share count when itβs done. Only used for arguments passed as βSharedβ.
-
enumerator
LLU ensures that at any point of time every container has a well-defined owner. The ownership is mostly static and may change only on a few occasions e.g. when passing a container to DataList or setting it as a result of a library function.
When a container is received from the Wolfram Language as an argument to a library function, the developer must inform the MArgumentManager
about the passing mode used for that container. There is a separate enumeration for this purpose:
-
enum
LLU
::
Passing
π Enumerated type representing different modes in which a container can be passed from LibraryLink to the library.
Values:
-
enumerator
Automatic
π
-
enumerator
Constant
π
-
enumerator
Manual
π
-
enumerator
The Passing
value is used by the MArgumentManager
to determine the initial owner of the container.
Here are some examples:
LLU::Tensor<mint> t { 1, 2, 3, 4, 5 }; // this Tensor is created (and therefore owned) by the library (LLU)
LLU::MArgumentManager manager {...};
auto tensor = manager.getTensor<double>(0); // tensors acquired via MArgumentManager are by default owned by the LibraryLink
auto image = manager.getGenericImage<LLU::Passing::Shared>(0); // the image is shared between LLU and the Kernel, so LLU knows not to deallocate
// the underlying MImage when image goes out of scope
auto newImage = image.clone(); // the newImage has the same contents as image but it is not shared, it is owned by LLU
More examples can be found in the unit tests.
Raw Containers π
These are just raw LibraryLink containers.
DataStore π
DataStore
is C structure (technically, a pointer to structure) defined in the WolframLibrary. It is a unidirectional linked list of immutable nodes.
Each node consists of a name (char*
) and value (MArgument
). DataStore itself can be stored in the MArgument union, which means that DataStores
can be nested. DataStores can be passed to and from library functions. Existing nodes cannot be removed but adding new nodes is supported.
The complete DataStore API can be found inside Wolfram Language (12.0+) installations at SystemFiles/IncludeFiles/C/WolframIOLibraryFunctions.h
.
On the Wolfram Language side a DataStore
is represented as an expression with head Developer`DataStore
that takes a list of expressions, where each
expressions is either:
a value of type supported by LibraryLink (String, Integer, NumericArray, etc.)
a Rule with the LHS being a String and RHS of the form described in the previous point
For example:
Developer`DataStore["node_name1" -> 42, NumericArray[{1,2,3,4}, "Integer8"], "node_name3" -> "node_value3"]
MImage π
A structure corresponding to Wolfram Language expressions Image and Image3D. Documented in LibraryLink Β» MImage.
MNumericArray π
A structure corresponding to Wolfram Language expressions NumericArray. Documented in LibraryLink Β» MNumericArray.
MTensor π
A structure corresponding to packed arrays in the Wolfram Language. Documented in LibraryLink Β» MTensor.
MSparseArray π
A structure corresponding to Wolfram Language expressions SparseArray. Documented in LibraryLink Β» MSparseArray.
Generic Wrappers π
These are datatype-unaware wrappers that offer automatic memory management and basic interface-like access to metadata (dimensions, rank, etc).
They do not provide direct access to the underlying data except via a void*
(or via a generic node type LLU::NodeType::Any
in case of a
GenericDataList).
Tip
All generic and strongly-typed wrappers are movable but non-copyable, instead they provide a clone()
method for performing deep copies.
This is in accordance with rule C.67
from the C++ Core Guidelines but most of all preventing accidental deep copies of containers is beneficial in terms of performance.
LLU::GenericDataList
π
GenericDataList is a light-weight wrapper over DataStore. It offers access to the underlying nodes via iterators and a
push_back
method for appending new nodes. You can also get the length of the list.
Here is an example of GenericDataList in action:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /* Reverse each string in a list of strings using GenericDataList */
LIBRARY_LINK_FUNCTION(ReverseStrings) {
LLU::MArgumentManager mngr {libData, Argc, Args, Res};
// read the input GenericDataList
auto dsIn = mngr.get<LLU::GenericDataList>(0);
// create new GenericDataList to store reversed strings
LLU::GenericDataList dsOut;
for (auto node : dsIn) {
// GenericDataList may store nodes of arbitrary type, so we need to explicitly ask to get the string value from the node
std::string_view s = node.as<LLU::NodeType::UTF8String>();
std::string reversed {s.rbegin(), s.rend()}; // create reversed copy
// we push back the reversed string via a string_view, this is safe because GenericDataList will immediately copy the string
dsOut.push_back(std::string_view(reversed));
}
// set the GenericDataList as the result of the library function
mngr.set(dsOut);
return LLU::ErrorCode::NoError;
}
|
Technically, GenericDataList is an alias:
-
using
LLU
::
GenericDataList
= MContainer<MArgumentType::DataStore> π MContainer specialization for DataStore is called GenericDataList.
-
template<>
classLLU
::
MContainer
<MArgumentType::DataStore> : public LLU::MContainerBase<MArgumentType::DataStore> π MContainer specialization for DataStore, provides basic list interface for the underlying raw DataStore.
Subclassed by LLU::DataList< T >
Public Types
-
using
iterator
= DataStoreIterator π GenericDataList iterator is DataStoreIterator.
-
using
const_iterator
= iterator π Const iterator over GenericDataList is the same as regular iterator - DataStoreIterator, because it is a proxy iterator.
-
using
Container
= Argument::CType<Type> π The type of underlying LibraryLink structure (e.g. MTensor, MImage, etc.) will be called βContainerβ.
Public Functions
-
MContainer
() π Default constructor, creates empty DataStore owned by the Library.
-
MContainer
clone
() const π Clone this MContainer, performs a deep copy of the underlying DataStore.
-
mint
length
() const π Get the length of the DataStore.
-
DataStoreNode
front
() const π Get the first node of the DataStore.
-
DataStoreNode
back
() const π Get the last node of the DataStore.
-
const_iterator
cbegin
() const π Proxy iterator to the first element of the DataStore.
-
const_iterator
cend
() const π Proxy iterator past the last element of the DataStore.
-
template<typename
T
, EnableIfArgumentType<T> = 0>
voidpush_back
(T nodeValue) π Add new nameless node at the end of the underlying DataStore.
-
template<typename
T
, EnableIfArgumentType<T> = 0>
voidpush_back
(std::string_view name, T nodeValue) π Add new named node at the end of the underlying DataStore.
-
template<MArgumentType
Type
, EnableIfUnambiguousWrapperType<Type> = 0>
voidpush_back
(Argument::WrapperType<Type> nodeValue) π Add new nameless node at the end of the underlying DataStore.
-
template<MArgumentType
Type
, EnableIfUnambiguousWrapperType<Type> = 0>
voidpush_back
(std::string_view name, Argument::WrapperType<Type> nodeValue) π Add new named node at the end of the underlying DataStore.
-
template<MArgumentType
Type
>
voidpush_back
(Argument::CType<Type> nodeValue) π Add new nameless node at the end of the underlying DataStore.
-
template<MArgumentType
Type
>
voidpush_back
(std::string_view name, Argument::CType<Type> nodeValue) π Add new named node at the end of the underlying DataStore.
-
void
push_back
(const Argument::Typed::Any &node) π Add new nameless node at the end of the underlying DataStore.
-
void
push_back
(std::string_view name, const Argument::Typed::Any &node) π Add new named node at the end of the underlying DataStore.
-
Container
abandonContainer
() const noexcept π Give a handle to internal container and stop owning it.
Should be used with caution as it may potentially result with resource leak.
Return share count of internal container, if present and 0 otherwise.
-
void
pass
(MArgument &res) const π Pass the internal container as result of a LibraryLink function.
-
using
LLU::GenericImage
π
GenericImage is a light-weight wrapper over MImage. It offers the same API as LibraryLink has for MImage, except for access to the image data, because GenericImage is not aware of the image data type. Typically one would use GenericImage to take an Image of unknown type from LibraryLink, investigate image properties and data type and then upgrade the GenericImage to the strongly-typed one in order to perform operations on the image data.
Here is an example of GenericImage in action:
1 2 3 4 5 6 7 | /* Get the number of columns in the input Image */
LIBRARY_LINK_FUNCTION(GetColumnCount) {
LLU::MArgumentManager mngr {libData, Argc, Args, Res};
const auto image = mngr.getGenericImage<LLU::Passing::Constant>(0);
mngr.setInteger(image.columns());
return LLU::ErrorCode::NoError;
}
|
-
using
LLU
::
GenericImage
= MContainer<MArgumentType::Image> π MContainer specialization for MImage is called GenericImage.
-
template<>
classLLU
::
MContainer
<MArgumentType::Image> : public LLU::ImageInterface, public LLU::MContainerBase<MArgumentType::Image> π MContainer specialization for MImage.
Subclassed by LLU::Image< T >
Public Types
-
using
Container
= Argument::CType<Type> π The type of underlying LibraryLink structure (e.g. MTensor, MImage, etc.) will be called βContainerβ.
Public Functions
-
MContainer
() = default π Default constructor, the MContainer does not manage any instance of MImage.
-
MContainer
(mint width, mint height, mint channels, imagedata_t type, colorspace_t colorSpace, mbool interleaving) π Create new 2D MImage based on given parameters.
-
MContainer
(mint slices, mint width, mint height, mint channels, imagedata_t type, colorspace_t colorSpace, mbool interleaving) π Create new 2D or 3D MImage based on given parameters.
-
GenericImage
convert
(imagedata_t t, mbool interleavingQ) const π Convert this object to a new GenericImage of given datatype, optionally changing interleaving.
-
GenericImage
convert
(imagedata_t t) const π Convert this object to a new GenericImage of given datatype.
-
MContainer
clone
() const π Clone this MContainer, performs a deep copy of the underlying MImage.
-
colorspace_t
colorspace
() const override π Get colorspace which describes how colors are represented as numbers.
-
mint
rows
() const override π Get number of rows.
-
mint
columns
() const override π Get number of columns.
-
mint
slices
() const override π Get number of slices.
-
mint
channels
() const override π Get number of channels.
-
bool
alphaChannelQ
() const override π Check if there is an alpha channel in the image.
-
bool
interleavedQ
() const override π Check if the image is interleaved.
-
bool
is3D
() const override π Check if the image is 3D.
-
mint
getRank
() const override π Get rank.
-
mint
getFlattenedLength
() const override π Get the total number of pixels in the image.
-
imagedata_t
type
() const override π Get the data type of the image.
-
void *
rawData
() const override π Get access to raw image data.
Use with caution.
-
Container
abandonContainer
() const noexcept π Give a handle to internal container and stop owning it.
Should be used with caution as it may potentially result with resource leak.
Return share count of internal container, if present and 0 otherwise.
-
void
pass
(MArgument &res) const π Pass the internal container as result of a LibraryLink function.
-
using
LLU::GenericNumericArray
π
GenericNumericArray is a light-weight wrapper over MNumericArray. It offers the same API as LibraryLink has for MNumericArray, except for access to the underlying array data, because GenericNumericArray is not aware of the array data type. Typically on would use GenericNumericArray to take a NumericArray of unknown type from LibraryLink, investigate its properties and data type and then upgrade the GenericNumericArray to the strongly-typed one in order to perform operations on the underlying data.
Here is an example of GenericNumericArray in action:
1 2 3 4 5 6 7 8 9 10 | /* Return the largest dimension of the input NumericArray */
LIBRARY_LINK_FUNCTION(GetLargestDimension) {
LLU::MArgumentManager mngr {libData, Argc, Args, Res};
const auto numericArray = mngr.getGenericNumericArray<LLU::Passing::Constant>(0);
// The list of dimensions of the NumericArray will never be empty because scalar NumericArrays are forbidden
auto maxDim = *std::max_element(numericArray.getDimensions(), std::next(numericArray.getDimensions(), numericArray.getRank()));
mngr.setInteger(maxDim);
return LLU::ErrorCode::NoError;
}
|
-
using
LLU
::
GenericNumericArray
= MContainer<MArgumentType::NumericArray> π MContainer specialization for MNumericArray is called GenericNumericArray.
-
template<>
classLLU
::
MContainer
<MArgumentType::NumericArray> : public LLU::NumericArrayInterface, public LLU::MContainerBase<MArgumentType::NumericArray> π MContainer specialization for MNumericArray.
Subclassed by LLU::NumericArray< T >
Public Types
-
using
Container
= Argument::CType<Type> π The type of underlying LibraryLink structure (e.g. MTensor, MImage, etc.) will be called βContainerβ.
Public Functions
-
MContainer
() = default π Default constructor, the MContainer does not manage any instance of MNumericArray.
-
MContainer
(numericarray_data_t type, mint rank, const mint *dims) π Create GenericNumericArray of given type and shape.
-
GenericNumericArray
convert
(numericarray_data_t t, NA::ConversionMethod method, double param) const π Convert this object to a new GenericNumericArray of given datatype, using specified conversion method.
-
MContainer
clone
() const π Clone this MContainer, performs a deep copy of the underlying MNumericArray.
-
mint
getRank
() const override π Get rank.
-
mint const *
getDimensions
() const override π Get dimensions.
-
mint
getFlattenedLength
() const override π Get length.
-
numericarray_data_t
type
() const override π Get the data type of this array.
-
void *
rawData
() const noexcept override π Get access to the raw data.
Use with caution.
-
Container
abandonContainer
() const noexcept π Give a handle to internal container and stop owning it.
Should be used with caution as it may potentially result with resource leak.
Return share count of internal container, if present and 0 otherwise.
-
void
pass
(MArgument &res) const π Pass the internal container as result of a LibraryLink function.
-
using
LLU::GenericTensor
π
GenericTensor is a light-weight wrapper over MTensor. It offers the same API that LibraryLink has for MTensor, except for access to the underlying array data because GenericTensor is not aware of the array data type. Typically on would use GenericTensor to take a Tensor of an unknown type from LibraryLink, investigate its properties and data type, then upgrade the GenericTensor to the strongly-typed one in order to perform operations on the underlying data.
-
using
LLU
::
GenericTensor
= MContainer<MArgumentType::Tensor> π MContainer specialization for MTensor is called GenericTensor.
-
template<>
classLLU
::
MContainer
<MArgumentType::Tensor> : public LLU::TensorInterface, public LLU::MContainerBase<MArgumentType::Tensor> π MContainer specialization for MTensor.
Subclassed by LLU::Tensor< T >, LLU::Tensor< double >
Public Types
-
using
Container
= Argument::CType<Type> π The type of underlying LibraryLink structure (e.g. MTensor, MImage, etc.) will be called βContainerβ.
Public Functions
-
MContainer
() = default π Default constructor, the MContainer does not manage any instance of MTensor.
-
MContainer
(mint type, mint rank, const mint *dims) π Create GenericTensor of given type and shape.
-
MContainer
clone
() const π Clone this MContainer, performs a deep copy of the underlying MTensor.
-
mint
getRank
() const override π Get rank.
-
mint const *
getDimensions
() const override π Get dimensions.
-
mint
getFlattenedLength
() const override π Get total length.
-
mint
type
() const override π Get the data type of this tensor.
-
void *
rawData
() const override π Get raw pointer to the data of this tensor.
-
Container
abandonContainer
() const noexcept π Give a handle to internal container and stop owning it.
Should be used with caution as it may potentially result with resource leak.
Return share count of internal container, if present and 0 otherwise.
-
void
pass
(MArgument &res) const π Pass the internal container as result of a LibraryLink function.
-
using
LLU::GenericSparseArray
π
GenericSparseArray is a light-weight wrapper over MSparseArray. It offers the same API that LibraryLink has for MSparseArray, except for access to the underlying array data because GenericSparseArray is not aware of the array data type. Typically one would use GenericSparseArray to take an MSparseArray of an unknown type from LibraryLink, investigate its properties and data type, then upgrade the GenericSparseArray to the strongly-typed one in order to perform operations on the underlying data.
-
using
LLU
::
GenericSparseArray
= MContainer<MArgumentType::SparseArray> π MContainer specialization for MSparseArray is called GenericSparseArray.
-
template<>
classLLU
::
MContainer
<MArgumentType::SparseArray> : public LLU::MContainerBase<MArgumentType::SparseArray> π MContainer specialization for MSparseArray.
Subclassed by LLU::SparseArray< T >
Public Types
-
using
Container
= Argument::CType<Type> π The type of underlying LibraryLink structure (e.g. MTensor, MImage, etc.) will be called βContainerβ.
Public Functions
-
MContainer
() = default π Default constructor, the MContainer does not manage any instance of MSparseArray.
-
MContainer
(const GenericTensor &positions, const GenericTensor &values, const GenericTensor &dimensions, const GenericTensor &implicitValue) π Create a new SparseArray from positions, values, dimensions and an implicit value.
-
MContainer
(const GenericTensor &data, const GenericTensor &implicitValue) π Create a new SparseArray from data array and an implicit value.
-
MContainer
(const GenericSparseArray &s, const GenericTensor &implicitValue) π Create a copy of given SparseArray with different implicit value.
-
MContainer
clone
() const π Clone this MContainer, performs a deep copy of the underlying MSparseArray.
-
GenericTensor
getImplicitValueAsTensor
() const π Get the implicit value of this sparse array.
-
void
setImplicitValueFromTensor
(const GenericTensor &implicitValue) π Change the implicit value of this array.
-
mint
getRank
() const π Get the rank (number of dimensions) of this sparse array.
-
mint const *
getDimensions
() const π Get dimensions of this sparse array.
-
GenericTensor
getExplicitValues
() const π Get a tensor with the values corresponding to the explicitly stored positions in the sparse array.
-
GenericTensor
getRowPointers
() const π Get a row pointer array for this sparse array.
The values returned are the cumulative number of explicitly represented elements for each row, so the values will be non-decreasing.
-
GenericTensor
getColumnIndices
() const π Get the column indices for the explicitly stored positions in this sparse array.
The first dimension of the resulting tensor is the number of explicit positions, and the second dimension is equal to getRank() - 1.
-
GenericTensor
getExplicitPositions
() const π Get the explicitly specified positions in this sparse array.
The first dimension of the resulting tensor is the number of explicit positions, and the second dimension is equal to getRank().
-
GenericTensor
toGenericTensor
() const π Expand this sparse array to a regular tensor.
-
void
resparsify
() π Use current implicit value to recalculate the sparse array after the data has been modified.
-
mint
type
() const π Get the data type of this MSparseArray.
-
Container
abandonContainer
() const noexcept π Give a handle to internal container and stop owning it.
Should be used with caution as it may potentially result with resource leak.
Return share count of internal container, if present and 0 otherwise.
-
void
pass
(MArgument &res) const π Pass the internal container as result of a LibraryLink function.
-
using
Typed Wrappers π
Typed wrappers are full-fledged wrappers with automatic memory management (see section below), type-safe data access, iterators, etc.
All typed wrappers are movable but non-copyable, instead they provide a clone()
method for performing deep copies.
LLU::DataList<T>
π
DataList is a strongly-typed wrapper derived from GenericDataList in which all nodes must be of the same type and be known at compile time. Template parameter
T
denotes the value type of nodes. Supported node value types are shown below with corresponding types of raw DataStore nodes and with underlying C++ types:
Node Type Name |
Underlying Type |
Raw DataStoreNode Type |
---|---|---|
NodeType::Boolean |
bool |
mbool |
NodeType::Integer |
mint |
mint |
NodeType::Real |
double |
mreal |
NodeType::Complex |
std::complex<double> |
mcomplex |
NodeType::Tensor |
LLU::GenericTensor |
MTensor |
NodeType::SparseArray |
LLU::GenericSparseArray |
MSparseArray |
NodeType::NumericArray |
LLU::GenericNumericArray |
MNumericArray |
NodeType::Image |
LLU::GenericImage |
MImage |
NodeType::UTF8String |
std::string_view |
char* |
NodeType::DataStore |
LLU::GenericDataList |
DataStore |
LLU::NodeType
is a namespace alias for LLU::Argument::Typed
which is defined as follows:
-
namespace
LLU::Argument
::
Typed
π Namespace defining C++ types corresponding to primitive LibraryLink argument types.
Mainly used for node types in DataList.
Typedefs
-
using
Boolean
= bool π Boolean type, corresponds to True or False in the Wolfram Language.
-
using
Integer
= mint π Machine integer type.
-
using
Real
= double π Double precision floating point type.
-
using
Complex
= std::complex<double> π Complex number type, bitwise-compatible with mcomplex defined in WolframLibrary.h.
-
using
Tensor
= MContainer<MArgumentType::Tensor> π Tensor stands for a GenericTensor - type agnostic wrapper over MTensor.
-
using
SparseArray
= MContainer<MArgumentType::SparseArray> π SparseArray stands for a GenericSparseArray - type agnostic wrapper over MSparseArray.
-
using
NumericArray
= MContainer<MArgumentType::NumericArray> π NumericArray stands for a GenericNumericArray - type agnostic wrapper over MNumericArray.
-
using
Image
= MContainer<MArgumentType::Image> π Image stands for a GenericImage - type agnostic wrapper over MImage.
-
using
UTF8String
= std::string_view π String values from LibraryLink (char*) are wrapped in std::string_view.
-
using
DataStore
= MContainer<MArgumentType::DataStore> π DataStore stands for a GenericDataList - type agnostic wrapper over DataStore.
-
using
Any
= TypedArgument π Any is a union of all supported types. Typed::Any can be used as a template parameter for DataList to get a heterogeneous DataList.
-
using
Notice that LLU::NodeType::Any
(or equivalently LLU::Argument::Typed::Any
) is a special type which is a union of all other types
from its namespace. In a way it corresponds to MArgument
type in LibraryLink. A DataList with node type LLU::NodeType::Any
can store
nodes of any types so it is quite similar to LLU::GenericDataList
but it has the interface of DataList, meaning that it offers more advanced
iterators and more constructors.
Here is an example of the DataList class in action:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* Take a list of named nodes with complex numbers and create two new lists: a list of node names and a list of node values */
LIBRARY_LINK_FUNCTION(SeparateKeysAndValues) {
LLU::MArgumentManager mngr {libData, Argc, Args, Res};
auto dsIn = mngr.getDataList<LLU::NodeType::Complex>(0);
LLU::DataList<LLU::NodeType::UTF8String> keys;
LLU::DataList<LLU::NodeType::Complex> values;
// For each node in the input DataList push its name to "keys" and its value to "values"
for (auto [name, value] : dsIn) {
keys.push_back(name);
values.push_back(value);
}
LLU::DataList<LLU::GenericDataList> dsOut;
dsOut.push_back("Keys", std::move(keys));
dsOut.push_back("Values", std::move(values));
mngr.set(dsOut);
return LLU::ErrorCode::NoError;
}
|
On the Wolfram Language side, we can load and use this function as follows:
`LLU`PacletFunctionSet[SeparateKeysAndValues, "SeparateKeysAndValues", {"DataStore"}, "DataStore"];
SeparateKeysAndValues[Developer`DataStore["a" -> 1 + 2.5 * I, "b" -> -3. - 6.I, 2I]]
(* Out[] = Developer`DataStore["Keys" -> Developer`DataStore["a", "b", ""], "Values" -> Developer`DataStore[1. + 2.5 * I, -3. - 6.I, 2.I]] *)
-
template<typename
T
>
classLLU
::
DataList
: public LLU::MContainer<MArgumentType::DataStore> π Top-level wrapper over LibraryLinkβs DataStore.
Designed to be strongly typed i.e. to wrap only homogeneous DataStores but by passing NodeType::Any as template parameter it will work with arbitrary DataStores.
- Template Parameters
T
: - type of data stored in each node, see theNodeType
namespace for possible node types
Public Types
-
using
iterator
= NodeIterator<T> π Default DataList iterator is NodeIterator<T>
-
using
const_iterator
= iterator π All DataList iterators are proxy iterators so in a way they are all const, therefore
const_iterator
is the same asiterator
.
-
using
value_iterator
= NodeValueIterator<T> π To iterate over node values use a proxy iterator NodeValueIterator<T>
-
using
name_iterator
= NodeNameIterator π To iterate over node names use a proxy iterator NodeNameIterator.
-
using
Container
= Argument::CType<Type> π The type of underlying LibraryLink structure (e.g. MTensor, MImage, etc.) will be called βContainerβ.
Public Functions
-
DataList
(GenericDataList gds) π Create DataList wrapping around an existing GenericDataList.
-
DataList
(std::initializer_list<value_type> initList) π Create DataList from list of values.
Keys will be set to empty strings.
-
DataList
(std::initializer_list<std::pair<std::string, value_type>> initList) π Create DataList from list of keys and corresponding values.
-
DataList
clone
() const π Clone this DataList, performing a deep copy of the underlying DataStore.
-
const_iterator
cbegin
() const π Get constant iterator at the beginning of underlying data.
-
const_iterator
cend
() const π Get constant reverse iterator after the end of underlying data.
-
value_iterator
valueBegin
() const π Get proxy iterator over node values pointing to the first node.
-
value_iterator
valueEnd
() const π Get proxy iterator over node values pointing past the last node.
-
name_iterator
nameBegin
() const π Get proxy iterator over node names (keys) pointing to the first node.
-
name_iterator
nameEnd
() const π Get proxy iterator over node names (keys) pointing past the last node.
-
void
push_back
(value_type nodeData) π Add new node to the DataList.
-
void
push_back
(std::string_view name, value_type nodeData) π Add new named node to the DataList.
-
mint
length
() const π Get the length of the DataStore.
-
DataStoreNode
front
() const π Get the first node of the DataStore.
-
DataStoreNode
back
() const π Get the last node of the DataStore.
-
template<MArgumentType
Type
, EnableIfUnambiguousWrapperType<Type> = 0>
voidpush_back
(Argument::WrapperType<Type> nodeValue) π Add new nameless node at the end of the underlying DataStore.
-
template<MArgumentType
Type
, EnableIfUnambiguousWrapperType<Type> = 0>
voidpush_back
(std::string_view name, Argument::WrapperType<Type> nodeValue) π Add new named node at the end of the underlying DataStore.
-
template<MArgumentType
Type
>
voidpush_back
(Argument::CType<Type> nodeValue) π Add new nameless node at the end of the underlying DataStore.
-
template<MArgumentType
Type
>
voidpush_back
(std::string_view name, Argument::CType<Type> nodeValue) π Add new named node at the end of the underlying DataStore.
-
void
push_back
(const Argument::Typed::Any &node) π Add new nameless node at the end of the underlying DataStore.
-
void
push_back
(std::string_view name, const Argument::Typed::Any &node) π Add new named node at the end of the underlying DataStore.
-
Container
abandonContainer
() const noexcept π Give a handle to internal container and stop owning it.
Should be used with caution as it may potentially result with resource leak.
Return share count of internal container, if present and 0 otherwise.
-
void
pass
(MArgument &res) const π Pass the internal container as result of a LibraryLink function.
LLU::Image<T>
π
Image is a strongly-typed wrapper derived from GenericImage, where the underlying data type is known at compile time and encoded in the template parameter. The table below shows the correspondence between Image data types in LLU, plain LibraryLink and in the Wolfram Language:
LLU (C++) type |
LibraryLink type |
Wolfram Language type |
---|---|---|
std::int8_t |
MImage_Type_Bit |
βBitβ |
std::uint8_t |
MImage_Type_Bit8 |
βByteβ |
std::int16_t |
MImage_Type_Bit16 |
βBit16β |
float |
MImage_Type_Real32 |
βReal32β |
double |
MImage_Type_Real |
βReal64β |
Here is an example of the Image class in action:
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* Take a constant "Byte" image and return a copy with negated pixel values */
LIBRARY_LINK_FUNCTION(NegateImage) {
LLU::MArgumentManager mngr {libData, Argc, Args, Res};
const auto image = mngr.getImage<uint8_t, LLU::Passing::Constant>(0);
LLU::Image<uint8_t> outImage {image.clone()};
constexpr uint8_t negator = (std::numeric_limits<uint8_t>::max)();
std::transform(std::cbegin(in), std::cend(in), std::begin(outImage), [](T inElem) { return negator - inElem; });
mngr.setImage(outImage);
return LLU::ErrorCode::NoError;
}
|
On the Wolfram Language side, we can load and use this function as follows:
`LLU`PacletFunctionSet[NegateImage, "NegateImage", {{Image, "Constant"}}, Image];
NegateImage[Image[RandomImage[ColorSpace -> "RGB"], "Byte"]]
(* Out[] = [--Image--] *)
This is only an example, Wolfram Language already has a built-in function for negating images: ImageNegate.
In the example above we simply assumed that the Image we use will be of type βByteβ, so we could simply write LLU::Image<uint8_t>
in the C++ code.
In the next example letβs consider a function that takes two images from LibraryLink of arbitrary types and converts the second one to the data type of the
first one. In this case we cannot simply read arguments from MArgumentManager because we donβt know what template arguments should be passed to LLU::Image.
Instead, we call a function LLU::MArgumentManager::operateOnImage()
which lets us evaluate a function template on an input image without knowing its data type.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | LIBRARY_LINK_FUNCTION(UnifyImageTypes) {
LLU::MArgumentManager mngr {libData, Argc, Args, Res};
// Take an image passed to the library function as the first argument, deduce its data type, create a corresponding LLU::Image wrapper and evaluate
// given generic lambda function on this image
mngr.operateOnImage(0, [&mngr](auto&& firstImage) {
// T is the data type of the first image
using T = typename std::remove_reference_t<decltype(firstImage)>::value_type;
// Similarly, read the second image and create a properly typed LLU::Image wrapper
mngr.operateOnImage(1, [&mngr](auto&& secondImage) {
// Convert the second image to the data type of the first one and return as the library function result
LLU::Image<T> out {secondImage.template convert<T>()};
mngr.setImage(out);
});
});
return LLU::ErrorCode::NoError;
}
|
-
template<typename
T
>
classLLU
::
Image
: public LLU::TypedImage<T>, public LLU::MContainer<MArgumentType::Image> π This is a class template, where template parameter T is the type of data elements. Image is derived from MArray.
Image<> classes automate creation and deletion of MImages. They are strongly typed (no void* to underlying memory) and almost all functions from <algorithms> can be used on Image.
- Template Parameters
T
: - type of underlying data
Public Types
-
using
iterator
= value_type* π Iterator type.
-
using
const_iterator
= const value_type* π Constant iterator type.
-
using
const_reverse_iterator
= std::reverse_iterator<const_iterator> π Constant reverse iterator type.
-
using
reference
= value_type& π Reference type.
-
using
const_reference
= const value_type& π Constant reference type.
-
using
Container
= Argument::CType<Type> π The type of underlying LibraryLink structure (e.g. MTensor, MImage, etc.) will be called βContainerβ.
Public Functions
-
Image
(mint w, mint h, mint channels, colorspace_t cs, bool interleavingQ) π Constructs new 2D Image.
-
Image
(mint nFrames, mint w, mint h, mint channels, colorspace_t cs, bool interleavingQ) π Constructs new 3D Image.
-
Image
(GenericImage im) π Create new Image from a GenericImage.
-
Image
() = default π Default constructor - creates an empty wrapper.
-
template<typename
U
>
Image<U>convert
(bool interleaved) const π Copy this image with type conversion and explicitly specified interleaving.
-
template<typename
U
>
Image<U>convert
() const π Copy this image with type conversion and other properties (dimensions, interleaving, color space, etc.) untouched.
-
T
get
(mint row, mint col, mint channel) const π Get channel value at specified position in 2D image.
-
T
get
(mint slice, mint row, mint col, mint channel) const π Get channel value at specified position in 3D image.
-
void
set
(mint row, mint col, mint channel, T newValue) π Set channel value at specified position in 2D image.
-
void
set
(mint slice, mint row, mint col, mint channel, T newValue) π Set channel value at specified position in 3D image.
-
mint
rank
() const noexcept π Get container rank.
-
bool
empty
() const noexcept π Check whether container is empty.
-
mint
dimension
(mint index) const π Get dimension value at position
index
.
-
const MArrayDimensions &
dimensions
() const π Get a const reference to dimensions object.
-
T &
operator[]
(const std::vector<mint> &indices) π Get a reference to the data element at given position in a multidimensional container.
-
const T &
operator[]
(const std::vector<mint> &indices) const π Get a constant reference to the data element at given position in a multidimensional container.
-
const_reference
operator[]
(mint index) const π Get a constant reference to the data element at given position.
-
const T &
at
(mint index) const π Get a constant reference to the data element at given position with bound checking.
-
T &
at
(const std::vector<mint> &indices) π Get a reference to the data element at given position in a multidimensional container.
-
const T &
at
(const std::vector<mint> &indices) const π Get a constant reference to the data element at given position in a multidimensional container.
-
value_type *
data
() noexcept π Get raw pointer to underlying data.
-
const value_type *
data
() const noexcept π Get raw pointer to const underlying data.
-
mint
size
() const noexcept π Get total number of elements in the container.
-
const_iterator
begin
() const noexcept π Get constant iterator at the beginning of underlying data.
-
const_iterator
cbegin
() const noexcept π Get constant iterator at the beginning of underlying data.
-
const_iterator
end
() const noexcept π Get constant iterator after the end of underlying data.
-
const_iterator
cend
() const noexcept π Get constant iterator after the end of underlying data.
-
reverse_iterator
rbegin
() noexcept π Get iterator at the beginning of underlying data.
-
const_reverse_iterator
rbegin
() const noexcept π Get constant iterator at the beginning of underlying data.
-
const_reverse_iterator
crbegin
() const noexcept π Get constant iterator at the beginning of underlying data.
-
reverse_iterator
rend
() noexcept π Get iterator after the end of underlying data.
-
const_reverse_iterator
rend
() const noexcept π Get constant iterator after the end of underlying data.
-
const_reverse_iterator
crend
() const noexcept π Get constant iterator after the end of underlying data.
-
const_reference
front
() const π Get constant reference to the first element.
-
const_reference
back
() const π Get constant reference to the last element.
-
std::vector<value_type>
asVector
() const π Copy contents of the data to a std::vector of matching type.
-
GenericImage
convert
(imagedata_t t, mbool interleavingQ) const π Convert this object to a new GenericImage of given datatype, optionally changing interleaving.
-
GenericImage
convert
(imagedata_t t) const π Convert this object to a new GenericImage of given datatype.
-
colorspace_t
colorspace
() const override π Get colorspace which describes how colors are represented as numbers.
-
mint
rows
() const override π Get number of rows.
-
mint
columns
() const override π Get number of columns.
-
mint
slices
() const override π Get number of slices.
-
mint
channels
() const override π Get number of channels.
-
bool
alphaChannelQ
() const override π Check if there is an alpha channel in the image.
-
bool
interleavedQ
() const override π Check if the image is interleaved.
-
bool
is3D
() const override π Check if the image is 3D.
-
mint
getRank
() const override π Get rank.
-
mint
getFlattenedLength
() const override π Get the total number of pixels in the image.
-
imagedata_t
type
() const override π Get the data type of the image.
-
void *
rawData
() const override π Get access to raw image data.
Use with caution.
-
Container
abandonContainer
() const noexcept π Give a handle to internal container and stop owning it.
Should be used with caution as it may potentially result with resource leak.
Return share count of internal container, if present and 0 otherwise.
-
void
pass
(MArgument &res) const π Pass the internal container as result of a LibraryLink function.
LLU::NumericArray<T>
π
NumericArray<T> is an extension of GenericNumericArray which is aware that it holds data of type T and therefore can provide an API to iterate over the data and modify it. The table below shows the correspondence between NumericArray C++ types and Wolfram Language types:
C++ type |
Wolfram Language type |
---|---|
std::int8_t |
βInteger8β |
std::uint8_t |
βUnsignedInteger8β |
std::int16_t |
βInteger16β |
std::uint16_t |
βUnsignedInteger16β |
std::int32_t |
βInteger32β |
std::uint32_t |
βUnsignedInteger32β |
std::int64_t |
βInteger64β |
std::uint64_t |
βUnsignedInteger64β |
float |
βReal32β |
double |
βReal64β |
std::complex<float> |
βComplexReal32β |
std::complex<double> |
βComplexReal64β |
Here is an example of the NumericArray class in action:
1 2 3 4 5 6 7 8 | /* Take a NumericArray of type "Integer32" and make a copy with reversed order of elements */
LIBRARY_LINK_FUNCTION(ReverseNumericArray) {
LLU::MArgumentManager mngr {libData, Argc, Args, Res};
auto inputNA = mngr.getNumericArray<std::int32_t, LLU::Passing::Constant>(0);
LLU::NumericArray<std::int32_t> outNA { std::crbegin(inputNA), std::crend(inputNA), inputNA.dimensions() };
mngr.set(outNA);
return LLU::ErrorCode::NoError;
}
|
On the Wolfram Language side, we can load and use this function as follows:
`LLU`PacletFunctionSet[ReverseNumericArray, "ReverseNumericArray", {{NumericArray, "Constant"}}, NumericArray];
ReverseNumericArray[NumericArray[{{2, 3, 4}, {5, 6, 7}}, "Integer32"]]
(* Out[] = NumericArray[{{7, 6, 5}, {4, 3, 2}}, "Integer32"] *)
-
template<typename
T
>
classLLU
::
NumericArray
: public LLU::TypedNumericArray<T>, public LLU::MContainer<MArgumentType::NumericArray> π This is a class template, where template parameter T is the type of data elements. NumericArray is derived from MArray.
NumericArray<> classes automate creation and deletion of MNumericArrays. They are strongly typed (no void* to underlying memory) and almost all functions from <algorithms> can be used on NumericArray.
- Template Parameters
T
: - type of underlying data
Public Types
-
using
iterator
= value_type* π Iterator type.
-
using
const_iterator
= const value_type* π Constant iterator type.
-
using
const_reverse_iterator
= std::reverse_iterator<const_iterator> π Constant reverse iterator type.
-
using
reference
= value_type& π Reference type.
-
using
const_reference
= const value_type& π Constant reference type.
-
using
Container
= Argument::CType<Type> π The type of underlying LibraryLink structure (e.g. MTensor, MImage, etc.) will be called βContainerβ.
Public Functions
-
NumericArray
(std::initializer_list<T> v) π Constructs flat NumericArray based on a list of elements.
-
template<class
Container
, typename = std::enable_if_t<is_iterable_container_with_matching_type_v<Container, T> && has_size_v<Container>>>NumericArray
(const Container &c) π Constructs flat NumericArray with contents copied from a given collection of data.
-
template<class
Container
, typename = std::enable_if_t<is_iterable_container_with_matching_type_v<Container, T>>>NumericArray
(const Container &c, MArrayDimensions dims) π Constructs a NumericArray with contents copied from a given collection of data and dimensions passed as parameter.
-
template<class
InputIt
, typename = enable_if_input_iterator<InputIt>>NumericArray
(InputIt first, InputIt last) π Constructs flat NumericArray with elements from range [first, last)
-
NumericArray
(T init, MArrayDimensions dims) π Constructs the NumericArray of given shape with all elements initialized to given value.
-
template<class
InputIt
, typename = enable_if_input_iterator<InputIt>>NumericArray
(InputIt first, InputIt last, MArrayDimensions dims) π Constructs the NumericArray of given shape with elements from range [first, last)
-
NumericArray
(MNumericArray na, Ownership owner) π Constructs NumericArray based on MNumericArray.
-
NumericArray
(GenericNumericArray na) π Create new NumericArray from a GenericNumericArray.
-
NumericArray
(const GenericNumericArray &other, NA::ConversionMethod method, double param = 0.0) π Create NumericArray from generic NumericArray.
-
NumericArray
() = default π Default constructor, creates a βhollowβ NumericArray that does not have underlying MNumericArray.
-
NumericArray
clone
() const π Clone this NumericArray, performing a deep copy of the underlying MNumericArray.
-
mint
rank
() const noexcept π Get container rank.
-
bool
empty
() const noexcept π Check whether container is empty.
-
mint
dimension
(mint index) const π Get dimension value at position
index
.
-
const MArrayDimensions &
dimensions
() const π Get a const reference to dimensions object.
-
T &
operator[]
(const std::vector<mint> &indices) π Get a reference to the data element at given position in a multidimensional container.
-
const T &
operator[]
(const std::vector<mint> &indices) const π Get a constant reference to the data element at given position in a multidimensional container.
-
const_reference
operator[]
(mint index) const π Get a constant reference to the data element at given position.
-
const T &
at
(mint index) const π Get a constant reference to the data element at given position with bound checking.
-
T &
at
(const std::vector<mint> &indices) π Get a reference to the data element at given position in a multidimensional container.
-
const T &
at
(const std::vector<mint> &indices) const π Get a constant reference to the data element at given position in a multidimensional container.
-
value_type *
data
() noexcept π Get raw pointer to underlying data.
-
const value_type *
data
() const noexcept π Get raw pointer to const underlying data.
-
mint
size
() const noexcept π Get total number of elements in the container.
-
const_iterator
begin
() const noexcept π Get constant iterator at the beginning of underlying data.
-
const_iterator
cbegin
() const noexcept π Get constant iterator at the beginning of underlying data.
-
const_iterator
end
() const noexcept π Get constant iterator after the end of underlying data.
-
const_iterator
cend
() const noexcept π Get constant iterator after the end of underlying data.
-
reverse_iterator
rbegin
() noexcept π Get iterator at the beginning of underlying data.
-
const_reverse_iterator
rbegin
() const noexcept π Get constant iterator at the beginning of underlying data.
-
const_reverse_iterator
crbegin
() const noexcept π Get constant iterator at the beginning of underlying data.
-
reverse_iterator
rend
() noexcept π Get iterator after the end of underlying data.
-
const_reverse_iterator
rend
() const noexcept π Get constant iterator after the end of underlying data.
-
const_reverse_iterator
crend
() const noexcept π Get constant iterator after the end of underlying data.
-
const_reference
front
() const π Get constant reference to the first element.
-
const_reference
back
() const π Get constant reference to the last element.
-
std::vector<value_type>
asVector
() const π Copy contents of the data to a std::vector of matching type.
-
GenericNumericArray
convert
(numericarray_data_t t, NA::ConversionMethod method, double param) const π Convert this object to a new GenericNumericArray of given datatype, using specified conversion method.
-
mint
getRank
() const override π Get rank.
-
mint const *
getDimensions
() const override π Get dimensions.
-
mint
getFlattenedLength
() const override π Get length.
-
numericarray_data_t
type
() const override π Get the data type of this array.
-
void *
rawData
() const noexcept override π Get access to the raw data.
Use with caution.
-
Container
abandonContainer
() const noexcept π Give a handle to internal container and stop owning it.
Should be used with caution as it may potentially result with resource leak.
Return share count of internal container, if present and 0 otherwise.
-
void
pass
(MArgument &res) const π Pass the internal container as result of a LibraryLink function.
LLU::Tensor<T>
π
In the same way as MTensor is closely related to MNumericArray, LLU::Tensor
has almost exactly the same interface as LLU::NumericArray
.
Tensor supports only 3 types of data, meaning that template<typename T> LLU::Tensor
class template can be instantiated with only 3 types T
:
mint
double
std::complex<double>
Here is an example of the Tensor class in action:
1 2 3 4 5 6 7 8 9 10 11 12 | /* Take a Tensor of real numbers and return the mean value */
LIBRARY_LINK_FUNCTION(GetMeanValue) {
LLU::MArgumentManager mngr {libData, Argc, Args, Res};
auto t = mngr.getTensor<double>(0);
auto total = std::accumulate(t.begin(), t.end(), 0.0);
auto result = total / t.size();
mngr.set(result);
return LLU::ErrorCode::NoError;
}
|
On the Wolfram Language side, we can load and use this function as follows:
`LLU`PacletFunctionSet[MeanValue, "MeanValue", {{Real, _}}, Real];
MeanValue[N @ {{Pi, Pi, Pi}, {E, E, E}}]
(* Out[] = 2.9299372 *)
-
template<typename
T
>
classLLU
::
Tensor
: public LLU::TypedTensor<T>, public LLU::MContainer<MArgumentType::Tensor> π This is a class template, where template parameter T is the type of data elements. Tensor is derived from MArray.
Tensor<> classes automate creation and deletion of MTensors. They are strongly typed (no void* to underlying memory) and almost all functions from <algorithms> can be used on Tensor.
- Template Parameters
T
: - type of underlying data
Public Types
-
using
iterator
= value_type* π Iterator type.
-
using
const_iterator
= const value_type* π Constant iterator type.
-
using
const_reverse_iterator
= std::reverse_iterator<const_iterator> π Constant reverse iterator type.
-
using
reference
= value_type& π Reference type.
-
using
const_reference
= const value_type& π Constant reference type.
-
using
Container
= Argument::CType<Type> π The type of underlying LibraryLink structure (e.g. MTensor, MImage, etc.) will be called βContainerβ.
Public Functions
-
template<class
Container
, typename = std::enable_if_t<is_iterable_container_with_matching_type_v<Container, T> && has_size_v<Container>>>Tensor
(const Container &c) π Constructs flat Tensor with contents copied from a given collection of data.
-
template<class
Container
, typename = std::enable_if_t<is_iterable_container_with_matching_type_v<Container, T>>>Tensor
(const Container &c, MArrayDimensions dims) π Constructs a Tensor with contents copied from a given collection of data and dimensions passed as parameter.
-
template<class
InputIt
, typename = enable_if_input_iterator<InputIt>>Tensor
(InputIt first, InputIt last) π Constructs flat Tensor with elements from range [first, last)
-
Tensor
(T init, MArrayDimensions dims) π Constructs the Tensor of given shape with all elements initialized to given value.
-
template<class
InputIt
, typename = enable_if_input_iterator<InputIt>>Tensor
(InputIt first, InputIt last, MArrayDimensions dims) π Constructs the Tensor of given shape with elements from range [first, last)
-
Tensor
(GenericTensor t) π Create new Tensor from a GenericTensor.
-
Tensor
() = default π Default constructor, creates a Tensor that does not wrap over any raw MTensor.
-
mint
rank
() const noexcept π Get container rank.
-
bool
empty
() const noexcept π Check whether container is empty.
-
mint
dimension
(mint index) const π Get dimension value at position
index
.
-
const MArrayDimensions &
dimensions
() const π Get a const reference to dimensions object.
-
T &
operator[]
(const std::vector<mint> &indices) π Get a reference to the data element at given position in a multidimensional container.
-
const T &
operator[]
(const std::vector<mint> &indices) const π Get a constant reference to the data element at given position in a multidimensional container.
-
const_reference
operator[]
(mint index) const π Get a constant reference to the data element at given position.
-
const T &
at
(mint index) const π Get a constant reference to the data element at given position with bound checking.
-
T &
at
(const std::vector<mint> &indices) π Get a reference to the data element at given position in a multidimensional container.
-
const T &
at
(const std::vector<mint> &indices) const π Get a constant reference to the data element at given position in a multidimensional container.
-
value_type *
data
() noexcept π Get raw pointer to underlying data.
-
const value_type *
data
() const noexcept π Get raw pointer to const underlying data.
-
mint
size
() const noexcept π Get total number of elements in the container.
-
const_iterator
begin
() const noexcept π Get constant iterator at the beginning of underlying data.
-
const_iterator
cbegin
() const noexcept π Get constant iterator at the beginning of underlying data.
-
const_iterator
end
() const noexcept π Get constant iterator after the end of underlying data.
-
const_iterator
cend
() const noexcept π Get constant iterator after the end of underlying data.
-
reverse_iterator
rbegin
() noexcept π Get iterator at the beginning of underlying data.
-
const_reverse_iterator
rbegin
() const noexcept π Get constant iterator at the beginning of underlying data.
-
const_reverse_iterator
crbegin
() const noexcept π Get constant iterator at the beginning of underlying data.
-
reverse_iterator
rend
() noexcept π Get iterator after the end of underlying data.
-
const_reverse_iterator
rend
() const noexcept π Get constant iterator after the end of underlying data.
-
const_reverse_iterator
crend
() const noexcept π Get constant iterator after the end of underlying data.
-
const_reference
front
() const π Get constant reference to the first element.
-
const_reference
back
() const π Get constant reference to the last element.
-
std::vector<value_type>
asVector
() const π Copy contents of the data to a std::vector of matching type.
-
mint
getRank
() const override π Get rank.
-
mint const *
getDimensions
() const override π Get dimensions.
-
mint
getFlattenedLength
() const override π Get total length.
-
mint
type
() const override π Get the data type of this tensor.
-
void *
rawData
() const override π Get raw pointer to the data of this tensor.
-
Container
abandonContainer
() const noexcept π Give a handle to internal container and stop owning it.
Should be used with caution as it may potentially result with resource leak.
Return share count of internal container, if present and 0 otherwise.
-
void
pass
(MArgument &res) const π Pass the internal container as result of a LibraryLink function.
LLU::SparseArray<T>
π
LLU::SparseArray
is a wrapper over an MSparseArray which holds elements of type T
. MSparseArray supports only 3 types of data,
meaning that template<typename T> LLU::SparseArray
class template can be instantiated with only 3 types T
:
mint
double
std::complex<double>
Here is an example of the SparseArray class in action:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | template<typename T>
void sparseModifyValues(LLU::SparseArray<T>& sa, LLU::TensorTypedView<T> newValues) {
// extract a Tensor with explicit values of the SparseArray
// this does not make a copy of the values so modifying the Tensor will modify the values in the SparseArray
auto values = sa.explicitValues();
if (values.size() < newValues.size()) {
throw std::runtime_error {"Too many values provided."};
}
// copy new values in place of the old ones
std::copy(std::cbegin(newValues), std::cend(newValues), std::begin(values));
// Recompute explicit positions (necessary since one of the new values might be equal to the implicit value of the SparseArray)
sa.resparsify();
}
LLU_LIBRARY_FUNCTION(ModifyValues) {
auto sp = mngr.getGenericSparseArray<LLU::Passing::Shared>(0);
auto values = mngr.getGenericTensor<LLU::Passing::Constant>(1);
// Operate on the GenericSparseArray as if its type was known
LLU::asTypedSparseArray(sp, [&values](auto&& sparseArray) {
using T = typename std::remove_reference_t<decltype(sparseArray)>::value_type;
sparseModifyValues(sparseArray, LLU::TensorTypedView<T> {values});
});
}
|
On the Wolfram Language side, we can load and use this function as follows:
(* Our function takes a shared SparseArray to modify it in-place. The SparseArray can be of any type. *)
`LLU`PacletFunctionSet[$ModifyValues, {{LibraryDataType[SparseArray, _, _], "Shared"}, {_, _, "Constant"}}, "Void"];
sparse = SparseArray[{{3.5, 0., 0., 0.}, {.5, -7., 0., 0.}, {4., 0., 3., 0.}, {0., 0., 0., 1.}}];
$ModifyValues[sparse, {3.5, .5, -7.}];
Normal[sparse]
(* Out[] = {{3.5, 0., 0., 0.}, {.5, -7., 0., 0.}, {4., 0., 3., 0.}, {0., 0., 0., 1.}} *)
-
template<typename
T
>
classLLU
::
SparseArray
: public LLU::MContainer<MArgumentType::SparseArray> π Strongly typed wrapper for MSparseArray.
- Template Parameters
T
: - any type supported by MSparseArray (mint, double or std::complex<double>)
Public Types
-
using
Container
= Argument::CType<Type> π The type of underlying LibraryLink structure (e.g. MTensor, MImage, etc.) will be called βContainerβ.
Public Functions
-
SparseArray
() = default π Default constructor, creates a SparseArray that does not wrap over any raw MSparseArray.
-
SparseArray
(MSparseArray t, Ownership owner) π Constructs SparseArray based on MSparseArray.
-
SparseArray
(GenericSparseArray t) π Create new SparseArray from a GenericSparseArray.
-
SparseArray
(const Tensor<mint> &positions, const Tensor<T> &values, const Tensor<mint> &dimensions, T implicitValue) π Create a new SparseArray from positions, values, dimensions and an implicit value.
-
SparseArray
(const Tensor<T> &data, T implicitValue) π Create a new SparseArray from data array and an implicit value.
-
SparseArray
(const SparseArray &s, T implicitValue) π Create a copy of given SparseArray with different implicit value.
-
mint
rank
() const π Get the rank (number of dimensions) of this sparse array.
-
Tensor<T>
explicitValues
() const π Get a tensor with the values corresponding to the explicitly stored positions in the sparse array.
-
Tensor<mint>
rowPointers
() const π Get a row pointer array for this sparse array.
The values returned are the cumulative number of explicitly represented elements for each row, so the values will be non-decreasing.
-
Tensor<mint>
columnIndices
() const π Get the column indices for the explicitly stored positions in this sparse array.
The first dimension of the resulting tensor is the number of explicit positions, and the second dimension is equal to rank() - 1.
-
Tensor<mint>
explicitPositions
() const π Get the explicitly specified positions in this sparse array.
The first dimension of the resulting tensor is the number of explicit positions, and the second dimension is equal to rank().
-
MContainer
clone
() const π Clone this MContainer, performs a deep copy of the underlying MSparseArray.
-
GenericTensor
getImplicitValueAsTensor
() const π Get the implicit value of this sparse array.
-
void
setImplicitValueFromTensor
(const GenericTensor &implicitValue) π Change the implicit value of this array.
-
mint
getRank
() const π Get the rank (number of dimensions) of this sparse array.
-
mint const *
getDimensions
() const π Get dimensions of this sparse array.
-
GenericTensor
getExplicitValues
() const π Get a tensor with the values corresponding to the explicitly stored positions in the sparse array.
-
GenericTensor
getRowPointers
() const π Get a row pointer array for this sparse array.
The values returned are the cumulative number of explicitly represented elements for each row, so the values will be non-decreasing.
-
GenericTensor
getColumnIndices
() const π Get the column indices for the explicitly stored positions in this sparse array.
The first dimension of the resulting tensor is the number of explicit positions, and the second dimension is equal to getRank() - 1.
-
GenericTensor
getExplicitPositions
() const π Get the explicitly specified positions in this sparse array.
The first dimension of the resulting tensor is the number of explicit positions, and the second dimension is equal to getRank().
-
GenericTensor
toGenericTensor
() const π Expand this sparse array to a regular tensor.
-
void
resparsify
() π Use current implicit value to recalculate the sparse array after the data has been modified.
-
mint
type
() const π Get the data type of this MSparseArray.
-
Container
abandonContainer
() const noexcept π Give a handle to internal container and stop owning it.
Should be used with caution as it may potentially result with resource leak.
Return share count of internal container, if present and 0 otherwise.
-
void
pass
(MArgument &res) const π Pass the internal container as result of a LibraryLink function.
Iterators π
All container classes in LLU are equipped with iterators. For Image, Tensor and NumericArray we get random-access iterators similar to those of, for instance,
std::vector
, because these containers also allocate space for their data as a contiguous piece of memory. Reverse and constant iterators are
available as well.
Warning
Bear in mind that iterators for Image, Tensor and NumericArray are not aware of the container dimensions in the sense that the iteration happens in the order in which data is laid out in memory. For 2D arrays this is often row-major order but it gets more complicated for multidimensional arrays and for Images.
DataStore wrappers have different iterators, because DataStore has a list-like structure with nodes of type DataStoreNode
. The list is
unidirectional, so reverse iterator is not available. The default iterator over GenericDataList, obtained with
begin
and end
, is a proxy
iterator of type DataStoreIterator
.
-
class
LLU
::
DataStoreIterator
π Proxy input iterator over DataStoreNodes, when dereferenced yields GenericDataNode proxy objects.
Public Types
-
using
value_type
= GenericDataNode π This iterator returns proxy objects of type GenericDataNode.
-
using
reference
= value_type π DataStoreIterator is a proxy iterator and so the reference type is the same as value_type.
-
using
iterator_category
= std::input_iterator_tag π As with all proxy iterators, DataStoreIterator is only an input iterator.
-
using
pointer
= value_type π DataStoreIterator is a proxy iterator and so the pointer type is the same as value_type.
-
using
difference_type
= mint π Provide difference_type as required for input iterators.
Public Functions
-
DataStoreIterator
(DataStoreNode n) π Create a DataStoreIterator pointing to a given node.
-
DataStoreIterator &
operator++
() π Pre-increment operator.
-
DataStoreIterator
operator++
(int) π Post-increment operator.
Friends
-
friend bool
operator==
(const DataStoreIterator &lhs, const DataStoreIterator &rhs) π βEqual toβ operator for DataStoreIterators
-
friend bool
operator!=
(const DataStoreIterator &lhs, const DataStoreIterator &rhs) π βNot equal toβ operator for DataStoreIterators
-
using
The object obtained by dereferencing a DataStoreIterator
is of type GenericDataNode
.
-
struct
LLU
::
GenericDataNode
π Basic wrapper over DataStoreNode, provides class-like interface and conversion of the underlying value from MArgument to TypedArgument.
Public Functions
-
GenericDataNode
next
() const noexcept π Get GenericDataNode wrapper over the next node.
-
MArgumentType
type
() const noexcept π Get type of the node value.
-
std::string_view
name
() const noexcept π Get node name.
-
Argument::TypedArgument
value
() const π Get value of the node as the variant type.
-
template<typename
T
>
Tas
() const π Get node value if it is of type T, otherwise throw an exception.
-
operator bool
() const π Bool conversion operator.
-
GenericDataNode *
operator->
() π Member of pointer operator, used by DataList iterators.
Public Members
-
DataStoreNode
node
π Raw DataStore node.
-
GenericDataNode
LLU::DataList<T>
offers more types of iterators but again all of them are proxy iterators.
The default one is NodeIterator
-
template<typename
T
>
structLLU
::
NodeIterator
: public LLU::Detail::DataListIteratorPrimitive π Simple proxy input iterator that goes over a DataStore and returns proxy DataNodes when dereferenced.
- Template Parameters
T
: - data node type, see LLU::NodeType namespace for supported node types
Public Types
-
using
reference
= value_type π NodeIterator is a proxy iterator and so the reference type is the same as value_type.
Public Functions
-
NodeIterator &
operator++
() π Pre-increment operator.
-
NodeIterator
operator++
(int) π Post-increment operator.
The object obtained by dereferencing a NodeIterator
is of type DataNode
.
-
template<typename
T
>
classLLU
::
DataNode
π Wrapper over DataStoreNode structure from LibraryLink.
Public Functions
-
DataNode
(GenericDataNode gn) π Create DataNode from raw GenericDataNode.
-
std::string_view
name
() const π Get node name.
-
bool
hasNext
() const π Check if this node has a successor.
-
GenericDataNode
next
() const π Get next node as GenericDataNode (because the next node may not necessarily have value of type T)
-
Every data node has a (possibly empty) name and a value. Sometimes you might only be interested in node values, or only in names; DataList provides
specialized iterators for this. You may obtain them with valueBegin()
and
nameBegin()
, respectively.
To get those specialized iterators in a range-based for loop, where you cannot directly choose which variant of begin()
method to use, you can
utilize one of the iterator adaptors that LLU defines. For instance,
// Get a DataList of complex numbers as argument to the library function
auto dataList = manager.getDataList<LLU::NodeType::Complex>(0);
// Create a new DataList to store node names of the original DataList as node values in the new list
DataList<LLU::NodeType::UTF8String> keys;
for (auto name : LLU::NameAdaptor {dataList}) {
keys.push_back(name);
}
// Create a new DataList to store node values of the original DataList, without node names
DataList<LLU::NodeType::Complex> values;
for (auto value : LLU::ValueAdaptor {dataList}) {
values.push_back(value);
}
It is possible to write the same code using the default iterator (NodeIterator
) and structured bindings:
// Get a DataList of complex numbers as argument to the library function
auto dataList = manager.getDataList<LLU::NodeType::Complex>(0);
DataList<LLU::NodeType::UTF8String> keys;
DataList<LLU::NodeType::Complex> values;
// Iterate over the dataList once, accessing both node name and value
for (auto [name, value] : dataList) {
keys.push_back(name);
values.push_back(value);
}