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:

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.

enumerator Shared πŸ”—

When the container is shared LLU only needs to decrease share count when it’s done. Only used for arguments passed as β€œShared”.

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.

See

https://reference.wolfram.com/language/LibraryLink/tutorial/InteractionWithWolframLanguage.html#97446640

Values:

enumerator Automatic πŸ”—
enumerator Constant πŸ”—
enumerator Manual πŸ”—
enumerator Shared πŸ”—

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<>
class LLU::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(Container c, Ownership owner) πŸ”—β–Ό

Create new MContainer wrapping a given raw DataStore.

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.

iterator begin() const πŸ”—

Proxy iterator to the first element of the DataStore.

iterator end() const πŸ”—

Proxy iterator past the last element 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>
void push_back(T nodeValue) πŸ”—β–Ό

Add new nameless node at the end of the underlying DataStore.

template<typename T, EnableIfArgumentType<T> = 0>
void push_back(std::string_view name, T nodeValue) πŸ”—β–Ό

Add new named node at the end of the underlying DataStore.

template<MArgumentType Type, EnableIfUnambiguousWrapperType<Type> = 0>
void push_back(Argument::WrapperType<Type> nodeValue) πŸ”—β–Ό

Add new nameless node at the end of the underlying DataStore.

template<MArgumentType Type, EnableIfUnambiguousWrapperType<Type> = 0>
void push_back(std::string_view name, Argument::WrapperType<Type> nodeValue) πŸ”—β–Ό

Add new named node at the end of the underlying DataStore.

template<MArgumentType Type>
void push_back(Argument::CType<Type> nodeValue) πŸ”—β–Ό

Add new nameless node at the end of the underlying DataStore.

template<MArgumentType Type>
void push_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 getContainer() const noexcept πŸ”—β–Ό

Get internal container.

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.

mint shareCount() const noexcept πŸ”—

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.

Ownership getOwner() const noexcept πŸ”—β–Ό

Get ownership information.

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<>
class LLU::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 getContainer() const noexcept πŸ”—β–Ό

Get internal container.

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.

mint shareCount() const noexcept πŸ”—

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.

Ownership getOwner() const noexcept πŸ”—β–Ό

Get ownership information.

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<>
class LLU::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 getContainer() const noexcept πŸ”—β–Ό

Get internal container.

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.

mint shareCount() const noexcept πŸ”—

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.

Ownership getOwner() const noexcept πŸ”—β–Ό

Get ownership information.

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<>
class LLU::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 getContainer() const noexcept πŸ”—β–Ό

Get internal container.

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.

mint shareCount() const noexcept πŸ”—

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.

Ownership getOwner() const noexcept πŸ”—β–Ό

Get ownership information.

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<>
class LLU::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 getContainer() const noexcept πŸ”—β–Ό

Get internal container.

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.

mint shareCount() const noexcept πŸ”—

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.

Ownership getOwner() const noexcept πŸ”—β–Ό

Get ownership information.

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.

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>
class LLU::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 the NodeType 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 as iterator.

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 value_type = T πŸ”—

Value of a node is of type T.

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.

iterator begin() const πŸ”—

Get iterator at the beginning of underlying data.

const_iterator cbegin() const πŸ”—

Get constant iterator at the beginning of underlying data.

iterator end() const πŸ”—

Get iterator after the end 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.

std::vector<T> values() const πŸ”—β–Ό

Return a vector of DataList node values.

std::vector<std::string> names() const πŸ”—β–Ό

Return a vector of DataList node names.

std::vector<DataNode<T>> toVector() const πŸ”—β–Ό

Return a vector of DataList nodes.

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>
void push_back(Argument::WrapperType<Type> nodeValue) πŸ”—β–Ό

Add new nameless node at the end of the underlying DataStore.

template<MArgumentType Type, EnableIfUnambiguousWrapperType<Type> = 0>
void push_back(std::string_view name, Argument::WrapperType<Type> nodeValue) πŸ”—β–Ό

Add new named node at the end of the underlying DataStore.

template<MArgumentType Type>
void push_back(Argument::CType<Type> nodeValue) πŸ”—β–Ό

Add new nameless node at the end of the underlying DataStore.

template<MArgumentType Type>
void push_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 getContainer() const noexcept πŸ”—β–Ό

Get internal container.

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.

mint shareCount() const noexcept πŸ”—

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.

Ownership getOwner() const noexcept πŸ”—β–Ό

Get ownership information.

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>
class LLU::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 value_type = T πŸ”—

Type of elements stored.

using iterator = value_type* πŸ”—

Iterator type.

using const_iterator = const value_type* πŸ”—

Constant iterator type.

using reverse_iterator = std::reverse_iterator<iterator> πŸ”—

Reverse 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(MImage mi, Ownership owner) πŸ”—β–Ό

Constructs Image based on MImage.

Image() = default πŸ”—

Default constructor - creates an empty wrapper.

Image clone() const πŸ”—β–Ό

Clone this Image, performing a deep copy of the underlying MImage.

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.

reference operator[](mint index) πŸ”—β–Ό

Get a reference to the data element at given position.

const_reference operator[](mint index) const πŸ”—β–Ό

Get a constant reference to the data element at given position.

T &at(mint index) πŸ”—β–Ό

Get a reference to the data element at given position with bound checking.

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.

iterator begin() noexcept πŸ”—

Get iterator at the beginning of underlying data.

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.

iterator end() noexcept πŸ”—

Get iterator after the end 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.

reference front() πŸ”—β–Ό

Get reference to the first element.

const_reference front() const πŸ”—β–Ό

Get constant reference to the first element.

reference back() πŸ”—β–Ό

Get reference to the last 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 getContainer() const noexcept πŸ”—β–Ό

Get internal container.

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.

mint shareCount() const noexcept πŸ”—

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.

Ownership getOwner() const noexcept πŸ”—β–Ό

Get ownership information.

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>
class LLU::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 value_type = T πŸ”—

Type of elements stored.

using iterator = value_type* πŸ”—

Iterator type.

using const_iterator = const value_type* πŸ”—

Constant iterator type.

using reverse_iterator = std::reverse_iterator<iterator> πŸ”—

Reverse 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.

reference operator[](mint index) πŸ”—β–Ό

Get a reference to the data element at given position.

const_reference operator[](mint index) const πŸ”—β–Ό

Get a constant reference to the data element at given position.

T &at(mint index) πŸ”—β–Ό

Get a reference to the data element at given position with bound checking.

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.

iterator begin() noexcept πŸ”—

Get iterator at the beginning of underlying data.

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.

iterator end() noexcept πŸ”—

Get iterator after the end 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.

reference front() πŸ”—β–Ό

Get reference to the first element.

const_reference front() const πŸ”—β–Ό

Get constant reference to the first element.

reference back() πŸ”—β–Ό

Get reference to the last 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 getContainer() const noexcept πŸ”—β–Ό

Get internal container.

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.

mint shareCount() const noexcept πŸ”—

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.

Ownership getOwner() const noexcept πŸ”—β–Ό

Get ownership information.

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>
class LLU::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 value_type = T πŸ”—

Type of elements stored.

using iterator = value_type* πŸ”—

Iterator type.

using const_iterator = const value_type* πŸ”—

Constant iterator type.

using reverse_iterator = std::reverse_iterator<iterator> πŸ”—

Reverse 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

Tensor(std::initializer_list<T> v) πŸ”—β–Ό

Constructs flat Tensor 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>>>
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(MTensor t, Ownership owner) πŸ”—β–Ό

Constructs Tensor based on MTensor.

Tensor(GenericTensor t) πŸ”—β–Ό

Create new Tensor from a GenericTensor.

Tensor() = default πŸ”—

Default constructor, creates a Tensor that does not wrap over any raw MTensor.

Tensor clone() const πŸ”—β–Ό

Clone this Tensor, performing a deep copy of the underlying 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.

reference operator[](mint index) πŸ”—β–Ό

Get a reference to the data element at given position.

const_reference operator[](mint index) const πŸ”—β–Ό

Get a constant reference to the data element at given position.

T &at(mint index) πŸ”—β–Ό

Get a reference to the data element at given position with bound checking.

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.

iterator begin() noexcept πŸ”—

Get iterator at the beginning of underlying data.

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.

iterator end() noexcept πŸ”—

Get iterator after the end 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.

reference front() πŸ”—β–Ό

Get reference to the first element.

const_reference front() const πŸ”—β–Ό

Get constant reference to the first element.

reference back() πŸ”—β–Ό

Get reference to the last 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 getContainer() const noexcept πŸ”—β–Ό

Get internal container.

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.

mint shareCount() const noexcept πŸ”—

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.

Ownership getOwner() const noexcept πŸ”—β–Ό

Get ownership information.

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>
class LLU::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.

T implicitValue() const πŸ”—β–Ό

Get the implicit value of this sparse array.

void setImplicitValue(T newImplicitValue) πŸ”—β–Ό

Change the implicit value of this 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().

Tensor<T> toTensor() const πŸ”—β–Ό

Expand this sparse array to a regular tensor.

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 getContainer() const noexcept πŸ”—β–Ό

Get internal container.

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.

mint shareCount() const noexcept πŸ”—

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.

Ownership getOwner() const noexcept πŸ”—β–Ό

Get ownership information.

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.

reference operator*() const πŸ”—β–Ό

Get proxy object of the current node.

pointer operator->() const πŸ”—β–Ό

Get proxy object of the current 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

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>
T as() 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.

LLU::DataList<T> offers more types of iterators but again all of them are proxy iterators. The default one is NodeIterator

template<typename T>
struct LLU::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 value_type = DataNode<T> πŸ”—

This iterator iterates over values of type DataNode<T>

using reference = value_type πŸ”—

NodeIterator is a proxy iterator and so the reference type is the same as value_type.

Public Functions

reference operator*() const πŸ”—β–Ό

Get current proxy DataNode.

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>
class LLU::DataNode πŸ”—

Wrapper over DataStoreNode structure from LibraryLink.

Public Functions

DataNode(DataStoreNode dsn) πŸ”—β–Ό

Create DataNode from raw DataStoreNode structure.

DataNode(GenericDataNode gn) πŸ”—β–Ό

Create DataNode from raw GenericDataNode.

T &value() πŸ”—β–Ό

Get node value.

const T &value() const πŸ”—β–Ό

Get node value.

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)

MArgumentType type() noexcept πŸ”—β–Ό

Get the actual type of node value.

This is useful when working on a β€œgeneric” DataList.

template<std::size_t N>
decltype(auto) get() πŸ”—β–Ό

Get N-th element of DataNode in a tuple-like way.

This function enables structured bindings to DataNodes.

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);
}