LibraryLink Utilities π
LibraryLink Utilities (abbr. LLU) is an open-source library consisting of modern C++ wrappers over most parts of LibraryLink - the Wolfram Language framework for connecting to C and C++ libraries. The official documentation including use instructions can be found here:
https://wolframresearch.github.io/LibraryLinkUtilities/
This documentation is split into modules. Each describes a core part of LLU providing general overview, examples and detailed description of related C++ entities.
Please use GitHub to report bugs, make suggestions and request features.
Motivation π
LibraryLink is a great tool for connecting Wolfram Language to external C/C++ libraries and is widely used by Wolfram Research and Wolfram Language developers for developing paclets. LibraryLink Utilities makes it even easier to use LibraryLink by providing features such as:
automatic resource management
exception handling
container iterators
class-like interface for data structures, for example
rank()
as member function ofTensor
class instead of separate functionmint MTensor_getRank(MTensor)
type safety
lazy loading of library functions
progress monitoring of library functions
standardized approach to exchange custom data types between C++ and WL code
Example π
Letβs demonstrate some advantages of LLU by comparing the same function written with and without
LLU.
Below we will implement a simple function repeatCharacters
that takes a string s
and a tensor t
and returns a new string s2
that consists of each
character s[i]
from original string but repeated t[i]
times. So, for example
repeatCharacters("abc", {3, 2, 1})
gives
"aaabbc"
This is the C - style implementation:
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | // global variable which is the buffer for strings returned to LibraryLink
char* outString = NULL;
EXTERN_C DLLEXPORT int repeatCharacters(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res) {
char* string = NULL;
MNumericArray counts;
uint8_t* countsData = NULL;
size_t outStringIndex = 0;
size_t len, j;
mint sum = 0;
mint c;
string = MArgument_getUTF8String(Args[0]);
counts = MArgument_getMNumericArray(Args[1]);
// check NumericArray type
if (libData->numericarrayLibraryFunctions->MNumericArray_getType(counts) != MNumericArray_Type_UBit8) {
libData->UTF8String_disown(string);
return LIBRARY_TYPE_ERROR;
}
// check NumericArray rank
if (libData->numericarrayLibraryFunctions->MNumericArray_getRank(counts) != 1) {
libData->UTF8String_disown(string);
return LIBRARY_RANK_ERROR;
}
// check if NumericArray length is equal to input string length
len = strlen(string);
if (libData->numericarrayLibraryFunctions->MNumericArray_getFlattenedLength(counts) != len) {
libData->UTF8String_disown(string);
return LIBRARY_DIMENSION_ERROR;
}
// before we allocate memory for the output string, we have to sum all NumericArray elements
// to see how many bytes are needed
countsData = (uint8_t*) libData->numericarrayLibraryFunctions->MNumericArray_getData(counts);
for (j = 0; j < len; j++) {
sum += countsData[j];
}
// free memory owned by global buffer, if any (for example from the previous call to this function)
free(outString);
outString = NULL;
// allocate memory for output string, outString has to be a global variable,
// because it will be returned to LibraryLink
outString = (char*) malloc(sum + 1);
if (!outString) {
libData->UTF8String_disown(string);
return LIBRARY_FUNCTION_ERROR;
}
// populate output string
for (j = 0; j < len; j++) {
for (c = 0; c < countsData[j]; c++) {
outString[outStringIndex++] = string[j];
}
}
// add null terminator
outString[sum] = '\0';
// clean up and set result
libData->UTF8String_disown(string);
MArgument_setUTF8String(Res, outString);
return LIBRARY_NO_ERROR;
}
|
and this is the corresponding C++ version written with LibraryLink Utilities:
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 28 29 30 31 32 33 34 35 36 37 38 39 40 | EXTERN_C DLLEXPORT int repeatCharactersLLU(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res) {
auto err = LLU::ErrorCode::NoError;
try {
// Create manager object
LLU::MArgumentManager mngr {libData, Argc, Args, Res};
// Read string and NumericArray arguments
auto string = mngr.getString(0);
auto counts = mngr.getNumericArray<std::uint8_t>(1);
// check NumericArray rank
if (counts.rank() != 1) {
LLU::ErrorManager::throwException(LLU::ErrorName::RankError);
}
// check if NumericArray length is equal to input string length
if (counts.size() != string.size()) {
LLU::ErrorManager::throwException(LLU::ErrorName::DimensionsError);
}
// before we allocate memory for the output string, we have to sum all NumericArray elements
// to see how many bytes are needed
auto sum = std::accumulate(std::cbegin(counts), std::cend(counts), static_cast<size_t>(0));
// allocate memory for the output string
std::string outString;
outString.reserve(sum);
// populate the output string
for (mint i = 0; i < counts.size(); i++) {
outString.append(std::string(counts[i], string[i]));
}
// clean up and set the result
mngr.set(std::move(outString));
} catch (const LLU::LibraryLinkError& e) {
err = e.which();
}
return err;
}
|
Limitations with respect to LibraryLink π
There are a few LibraryLink features currently not covered by LLU, most notably:
Tensor subsetting:
MTensor_getTensor
Callbacks
Wolfram IO Library (asynchronous tasks)
Glossary π
- LibraryLink
The framework to connect external code to the Wolfram Language. See https://reference.wolfram.com/language/guide/LibraryLink.html
- LibraryLink Utilities
- LLU
A set of modern C++ wrappers over different parts of LibraryLink.
- library function
A C/C++ function with special signature
int f (WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res);
or
int f (WolframLibraryData libData, WSLINK mlp);
Such functions can later be loaded into the Wolfram Language using LibraryFunctionLoad.
- paclet
A unit of the Wolfram Language functionality implemented in one or more files which can be auto-discovered, installed, loaded, etc. and must be accompanied by a PacletInfo.wl file. See the official guide and even more in-depth tutorial on paclet development.
- LibraryLink paclet
A paclet that has some of its functionality implemented in C or C++ using LibraryLink. Such paclets usually have a layer of the Wolfram Language code responsible at least for loading functions from the shared C/C++ library.
- LLU paclet
A LibraryLink paclet that additionally uses LibraryLink Utilities.
Contributors π
RafaΕ Chojna (rafalc) - main developer
Sean Cheren (scheren) - top-level code for error handling, CMake improvements
Rebecca Frederick (rebeccaf) - CMake improvements