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.
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
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:
// 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:
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;
}
There are a few LibraryLink features currently not covered by LLU, most notably:
- Tensor subsetting:
MTensor_getTensor
- Callbacks
- Wolfram IO Library (asynchronous tasks)
- Rafał Chojna (rafalc) - main developer
- Sean Cheren (scheren) - top-level code for error handling, CMake improvements
- Rebecca Frederick (rebeccaf) - CMake improvements