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 ofTensorclass 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
