Interaction with the Wolfram Language

Wolfram LibraryLink allows dynamic libraries to be directly loaded into the Wolfram Language kernel so that functions in the libraries can be immediately called from the Wolfram Language. You can exchange not only C-like data types such as integers, reals, packed arrays, and strings, but also arbitrary Wolfram Language expressions. In addition, there are useful functions such as sending errors and calling back to the Wolfram Language.

This section describes the functions that the Wolfram Language provides for working with Wolfram Libraries.

Library Specification

The first argument to LibraryFunctionLoad is the library to load. This can be given as an absolute file name such as /Library/myLibrary.dylib. However, it is often more convenient to give a relative specification to the library, finding it from a path. Another problem is that different platforms use different file name extensions for libraries. The conventions are summarized in the following table.

Windows

dll

Unix and Linux

so

Mac OS X

dylib

Extensions for dynamic libraries on different systems.

The Wolfram Language provides a solution to this with FindLibrary and $LibraryPath. FindLibrary can take a platform-independent specification of a library, look for it on $LibraryPath, and, if it finds the library, return the actual file for your system.

The following searches on $LibraryPath and finds the library suitable for your platform; in this example it is Windows.

Function Name

The second argument to LibraryFunctionLoad gives the name of the function to load. This function should be exported from the library, as described in "Library Structure". Note that if you compile the library as C++, you should probably export it with a C naming convention; this is also described in "Library Structure".

Type Specification

The third and fourth arguments to LibraryFunctionLoad specify the types of the arguments and the return type.

The types that specify tensors are designed to map directly to Wolfram Language packed arrays and also to use tensors in the Wolfram Language compiler. This gives the system great efficiency and allows libraries access to many tensor operations. Tensors can specify the type of each element and the rank explicitly, or they can be left unspecified. Unspecified types and rank give a lot of flexibility for applications that work with tensors. Note that tensors are only supported for Integer, Real, and Complex.

{Integer, 1}

tensor of specified base type and rank

{Real, _}

tensor with specified base type and arbitrary rank

{_,_}

tensor with arbitrary base type and arbitrary rank

Tensor type specification.

When a tensor is passed as an argument, you can also specify how its memory is handled. This is discussed in detail in the next section.

In the code of your library function, you collect the data from each type from the argument array. Macros for MArgument have been provided to make this collection easy and straightforward. Sample code is shown below.

Memory Management of MTensors

Types such as mint, double, and mcomplex are passed to and returned from libraries by value, as is common for calling C functions. The usage in the library is really independent of that in the Wolfram Language.

By contrast, an MTensor is a pointer to a data structure, and thus it is passed by reference. A consequence is that you have to think about how its memory is managed. The Wolfram Language chooses a default technique that is safe and simple, but if you want to pass in large amounts of data or save it for future usage, then you need to think about its management.

MTensor Input Arguments

When you pass an MTensor to a library function, you have a number of options that determine how this is done.

{Integer, 1}

pass in a copy of the MTensor and automatically clean

{Integer, 1, Automatic}

pass in a copy of the MTensor and automatically clean

{Integer, 1, "Constant"}

pass in a reference to the MTensor that should not be modified

{Integer, 1, "Manual"}

pass in a copy of the MTensor and do not automatically clean

{Integer, 1, "Shared"}

pass in a reference to the MTensor shared between a library and the Wolfram Language

Automatic Passing

If you select automatic passing, this means that the MTensor is copied before the function is called and it will be cleaned when the function returns. This would be suitable for a function such as the following.

In this function there is no need to free the MTensor. However, you could not save a reference to the MTensor and use it once the function has finished.

An MTensor passed with automatic passing is owned for both read and write access by the library function only as long as the function call is active.

Constant Passing

If you select constant passing, this means that a reference to the MTensor is passed in and it is assumed that your function will not modify the MTensor in any way. This effectively gives you very fast read-only access to the MTensor data. If your code breaks this assumption and does modify the data, a grave error could result in your Wolfram Language session.

An MTensor passed with constant passing is owned by the library function for read access only as long as the function call is active.

Manual Passing

If you select manual passing, this means that the MTensor is copied before the function is called and it is not cleaned when the function returns. This would be suitable for a function such as the following.

Note how the function frees the MTensor. If it were not freed, the memory would be lost. However, instead of freeing the tensor, you could save the tensor and use it in some other part of the library. Finally, if you finish with the tensor, you should call MTensor_free. An alternative would be to return it from a library function to the Wolfram Language in which ownership would pass back to the Wolfram Language.

An MTensor passed with manual passing is completely owned by the library; this continues until the MTensor is freed or passed back to the Wolfram Language.

Shared Passing

If you select shared passing, this means that the MTensor is not copied before the function is called; it is just passed directly to the library function. This would be suitable for a function such as the following.

When you use shared pass in arguments, the MTensor is shared between the Wolfram Language and your library. The Wolfram Language keeps the MTensor in a table to make sure that its memory does not get collected. When your library no longer wants to use the MTensor, you should call MTensor_disown.

An MTensor passed with shared passing is shared between the library and the Wolfram Language. It will be kept alive until the library and the Wolfram Language have finished all usages.

However, if you call the function with an argument that is not a packed array, the call will work, but you will get a warning message. This is because the Wolfram Language had to convert the input into a packed array, thereby copying the data and losing one of the advantages of using shared passing.

If the MTensor is owned by the library, i.e. it was created in the library or passed in with manual passing, then once it has been returned to the Wolfram Language it is no longer owned by the library, which should not make any usage of it whatsoever. This is shown in the function above.

If the MTensor is shared between the library and the Wolfram Language, then automatic return does not change anything in the ownership of the MTensor. However, it is rather strange to return a shared MTensor from a library, since the Wolfram Language already has a reference to it.

Shared Return

If you select shared return this means that the MTensor will be shared between the library and the Wolfram Language. Technically this is implemented by adding it to the sharing table when the function returns. This makes sure the MTensor does not get collected. When your library no longer wants to use the MTensor, you should call MTensor_disown (or MTensor_disownAll).

Note that if you call MTensor_disown (or MTensor_disownAll) before the MTensor has been added to the sharing table, it will have no effect and a warning message will be issued.

MTensor Memory Management Summary

One way to understand the three different types of memory management for an MTensor is to consider how the MTensor is "owned" between the different components.

For automatic passing, the MTensor is owned by the library function only when that call to the library is active. The function can return the MTensor whether it has been modified or not, but whatever happens, the MTensor cannot be used after the function has returned.

For manual passing, the MTensor is owned by the library after the function has been called. The MTensor can be used at any time after the function, for example, in another function. The MTensor will continue to be owned by the library until either it is returned by a library function or it is passed to a call to MTensor_free. If you want to return an MTensor that was passed in with manual passing but keep ownership in the library, you will have to copy it with a call to MTensor_clone or return it from a function that uses shared return.

For shared passing and return, ownership of the MTensor is shared between the Wolfram Language and the library. If the Wolfram Language expression that holds the packed array for the MTensor can no longer be reached, the Wolfram Language will no longer have ownership. The library will also keep ownership until you have called MTensor_disown on the MTensor. Note that you have to call MTensor_disown on an MTensor as many times as you have passed it into and returned from a function. For example, if you pass the same packed array into a library three times, then you need to call MTensor_disown three times. The function MTensor_disownAll can be useful to remove all references, and the function MTensor_shareCount gives the actual number of times the MTensor is shared.

A final thing to remember is that if any errors arise, these might transfer control away from the parts of your library function that free memory. In this case you might want to insert your own error handler; this is discussed in the section on errors.

String Arguments

String arguments are passed in with characters encoded using UTF8. When a string is passed as an argument, memory management for the string is left completely up to the program, similarly to the manual passing for an MTensor.

Note how the memory for both string arguments is released using UTF8String_disown. If this is not done and you do not otherwise keep a reference to the string argument in your program, the memory will simply be lost. When a string is returned as a result, the Wolfram Language accesses the memory to convert it to its own internal string format, but does not attempt to free the memory. Thus, if your program allocates a string for a result, it also needs to free that memory, but in a separate function from the one setting the string result, since the Wolfram Language needs to access the string memory after the library function has returned.

MSparseArray

A SparseArray in the Wolfram Language can be passed to or from a LibraryFunction where it appears as an argument or result of type MSparseArray. MSparseArray is a pointer to a data structure, and just like with MTensor they are passed by reference. A consequence is that you have to think about how its memory is managed. The Wolfram Language chooses a default technique that is safe and simple, but if you want to pass in large amounts of data or save it for future usage then you need to think about its management. For the most part, memory management for MSparseArray is very much like memory management for MTensor.

SparseArray Type Specification

Arguments or results of type MSparseArray are specified using LibraryDataType.

An argument of type MSparseArray is managed by converting the actual argument passed to a LibraryFunction into a Wolfram Language SparseArray object if it is not already one. If the conversion is successful and the rank is allowed by the argument specification, then type coercion is done. If no type is specified, coercion is done so that the explicit and implicit values are of the same machine number type. If type is specified, then coercion of both explicit and implicit values to the specified type is attempted.

A result of type MSparseArray will always be returned to the Wolfram Language as a SparseArray object unless the rank or one of the dimensions is 0. In these cases, the result is converted to a number or an empty list, respectively.

MSparseArray Data Structure

An MSparseArray is a reflection of the internal structure of SparseArray in the Wolfram Language. While you typically do not need to know much about this structure within the Wolfram Language, it can sometimes be very useful to have an understanding of it for uses in functions.

MSparseArray is stored as an extension of the compressed sparse row (CSR) matrix storage format. In an × matrix with positions that are explicitly stored, values corresponding to each position are stored in a rank 1 MTensor of length . The positions are encoded as follows. A rank 2 MTensor with dimensions and integer type is used to store the column indices starting from row 1 through row . A rank 1 MTensor of length and integer type is used to store the cumulative number of positions in each row, or "row pointers". The last element of this MTensor is always equal to .

For example, consider the matrix where all nonzero values are stored explicitly.

The nonzero values are .

The column MTensor contains .

The row pointers MTensor contains .

The format is extended to arrays of any rank as follows. If the rank is 1, a vector of length is effectively stored as a 1× matrix. If the rank is greater than 2, the columns are stored as an MTensor with dimensions .

You can see the storage format by looking at the last two parts of the InputForm of a SparseArray object in the Wolfram Language.

Even though the implicit value is 1 in the above example, the data structure is effectively the same. In MSparseArray the implicit value is always stored as a rank 0 MTensor. The type of this MTensor is the type of the data in the MSparseArray; the values, if any, have type consistent with the implicit values.

The MTensors used in the data structure can be accessed using the library callback functions.

Note that all of these callback functions return pointers to MTensor. They belong to the MSparseArray data structure and should not be freed using MTensor_free. They will be freed when the MSparseArray is freed. Accessing the data inside of the MTensor allows you to change in place the values in the data structure. The explicit values can be changed fairly safely as long as the memory for the MSparseArray is owned by the library. Changing the implicit value can be done, but often a change in the implicit value implies a different sparse structure, as in the example above, so there is a callback function MSparseArray_resetImplicitValues that will recompute the CSR data structure for the new value. Changing the column indices or the row pointers needs to be done with extreme care—these need to be consistent and any incorrect change could lead to severe problems down the line.

It can be difficult to construct the or modify CSR storage directly, and it is typically easier to work with the actual positions in the array that are represented.

Automatic Passing

If you select automatic passing, this means that the MSparseArray is copied before the function is called and it will be cleaned when the function returns.

An MSparseArray passed with automatic passing is owned for both read and write access by the library function only as long as the function call is active.

Constant Passing

If you select constant passing, this means that a reference to the MSparseArray is passed in and it is assumed that your function will not modify the MSparseArray in any way. This effectively gives you very fast read-only access to the MSparseArray data. If your code breaks this assumption and does modify the data, a grave error could result in your Wolfram Language session.

An MSparseArray passed with constant passing is available to the library function for read access only as long as the function call is active.

Manual Passing

If you select manual passing, this means that the MSparseArray is copied before the function is called and it is not cleaned when the function returns.

An MSparseArray passed with manual passing is completely owned by the library; this continues until the MSparseArray is freed or passed back to the Wolfram Language.

Shared Passing

If you select shared passing, this means that the MSparseArray is not copied before the function is called; it is just passed directly to the library function.

When you use shared pass in arguments, the MTensor is shared between the Wolfram Language and your library. The Wolfram Language keeps the MSparseArray in a table to make sure that its memory does not get collected. When your library no longer wants to use the MSparseArray, you should call MSparseArray_disown.

An MSparseArray passed with shared passing is shared between the library and the Wolfram Language. It will be kept alive until the library and the Wolfram Language have finished all usages.

Similarly to the case with MTensor, the data is only shared if the Wolfram Language argument given is a SparseArray and no coercion of either the explicit or implicit values is necessary to make an MSparseArray.

MSparseArray Return

When you return an MSparseArray from a library function, you also control how its memory is managed.

Automatic Return

If you select automatic return, this means that the MSparseArray goes directly back from the library to the Wolfram Language, which will use it as the result of the library function.

If the MSparseArray is owned by the library, i.e. it was created in the library or passed in with manual passing, then once it has been returned to the Wolfram Language it is no longer owned by the library, which should not make any usage of it whatsoever.

If the MSparseArray is shared between the library and the Wolfram Language, then automatic return does not change anything in the ownership of the MSparseArray. However, it is rather strange to return a shared MSparseArray from a library, since the Wolfram Language already has a reference to it.

Shared Return

If you select shared return, this means that the MSparseArray will be shared between the library and the Wolfram Language. Technically this is implemented by adding it to the sharing table when the function returns. This makes sure the MSparseArray does not get collected. When your library no longer wants to use the MSparseArray, you should call MSparseArray_disown (or MSparseArray_disownAll).

MImage

An Image or Image3D in the Wolfram Language can be passed to or from a LibraryFunction where it appears as an argument or result of type MImage. MImage is a pointer to a data structure, and just like with MTensor, they are passed by reference. A consequence is that you have to think about how its memory is managed. The Wolfram Language chooses a default technique that is safe and simple, but if you want to pass in large amounts of data or save it for future usage then you need to think about its management. For the most part, memory management for MImage is very much like memory management for MTensor.

An argument arg of expression type MImage is managed by checking if the arg is a Wolfram Language Image or Image3D object consistent with the dimensionality is allowed by the argument specification. If an image type is specified and the actual type does not match the specified type, the equivalent of Image[arg,type] is used to coerce the image type. Then a reference to the MImage data structure contained in the image expression is passed to the function.

A result of type MImage will always be returned to the Wolfram Language as a Image or Image3D object.

MImage Data Structure

An MImage is a reflection of the internal structure of Image and Image3D in the Wolfram Language. The MImage data structure holds information about the image, and much of this can be accessed through callback functions.

The most important part of the MImage data structure is an array of data of type corresponding to the image type.

Type

C type

description

"Bit"

raw_t_bit

integer 0 or 1 (single bit)

"Byte"

raw_t_ubit8

integer 0 through 255 (8 bits)

"Bit16"

raw_t_ubit16

integer 0 through 65535 (16 bits)

"Real32"

raw_t_real32

single-precision real (32-bit)

"Real"

raw_t_real64

double-precision real (32-bit)

Image types and the corresponding C types.

The arrangement of this array can depend on dimensionality, channels, and interleaving. Callback functions are provided that give access to the array to single pixels in the array that do not require you to know how the channels are interleaved.

Automatic Passing

If you select automatic passing, this means that the MImage is copied before the function is called and it will be cleaned when the function returns.

An MImage passed with automatic passing is owned for both read and write access by the library function only as long as the function call is active.

Constant Passing

If you select constant passing, this means that a reference to the MImage is passed in and it is assumed that your function will not modify the MImage in any way. This effectively gives you very fast read-only access to the MImage data. If your code breaks this assumption and does modify the data, a grave error could result in your Wolfram Language session.

An MImage passed with constant passing is available to the library function for read access only as long as the function call is active.

Manual Passing

If you select manual passing, this means that the MImage is copied before the function is called and it is not cleaned when the function returns.

An MImage passed with manual passing is completely owned by the library; this continues until the MImage is freed or passed back to the Wolfram Language.

Shared Passing

If you select shared passing, this means that the MImage is not copied before the function is called; it is just passed directly to the library function.

When you use shared pass in arguments, the MImage is shared between the Wolfram Language and your library. The Wolfram Language keeps the MImage in a table to make sure that its memory does not get collected. When your library no longer wants to use the MImage, you should call MImage_disown.

An MImage passed with shared passing is shared between the library and the Wolfram Language. It will be kept alive until the library and the Wolfram Language have finished all usages.

Similarly to the case with MTensor, the data is only shared if the Wolfram Language argument given is a Image and no coercion of either the explicit or implicit values is necessary to make an MImage.

MImage Return

When you return an MImage from a library function, you also control how its memory is managed.

Automatic Return

If you select automatic return, this means that the MImage goes directly back from the library to the Wolfram Language, which will use it as the result of the library function.

If the MImage is owned by the library, i.e. it was created in the library or passed in with manual passing, then once it has been returned to the Wolfram Language it is no longer owned by the library, which should not make any usage of it whatsoever.

If the MImage is shared between the library and the Wolfram Language, then automatic return does not change anything in the ownership of the MImage. However, it is rather strange to return a shared MImage from a library since the Wolfram Language already has a reference to it.

Shared Return

If you select shared return, this means that the MImage will be shared between the library and the Wolfram Language. Technically this is implemented by adding it to the sharing table when the function returns. This makes sure the MImage does not get collected. When your library no longer wants to use the MImage, you should call MImage_disown (or MImage_disownAll).

Note that if you call MImage_disown (or MImage_disownAll) before the MImage has been added to the sharing table, it will have no effect and a warning message will be issued.

Managed Library Expressions

There may be cases where you may want to use a Wolfram Language expression as a handle for an instance of an object or some data where the memory is managed by the library. By using managed library expressions to access these instances, it is possible to have the instance automatically freed when the expression is no longer referenced in a Wolfram Language session.

Typically it is best to register the library expression manager in library initialization and unregister the manager in library uninitialization so that the manager is active as long as the library is loaded.

The manger function mfun is of type void (*mfun)(WolframLibraryData libData, mboolmode, mint id). If mname has been registered with the manager function mfun, then evaluating CreateManagedLibraryExpression[mname,f] will generate a positive integer ID mid that is unique for the manager with name mname. First is evaluated and then the function mfun is called with mode 0 and id equal to mid, and finally the evaluation is returned as the result. If at some point during the Wolfram Language session there are no longer any references to the result , the function mfun is called with mode 1 and id equal to mid. Typically the mode 0 will indicate data creation and mode 1 data freeing.

It is sometimes possible to have hidden references to Wolfram Language expressions, so when it is certain that the library data is no longer needed, releaseManagedLibraryExpression can be used to release the expression. In releasing the expression, releaseManagedLibraryExpression(mname, mid) calls the manager function fun associated with mname with mode 1 and id equal to mid.

An example of using this to set up different instances of simple linear congruential generators is implemented in demo_managed.cxx and shown in the LibraryLinkexamples under demo_managed.

Library Callback Functions

Sometimes a function needs to repeatedly evaluate another function to do its task. Rootfinding and optimization functions are typical examples.

Callbacks for very general expressions can always be done using the Wolfram Symbolic Transfer Protocol (WSTP) argument interface, but this comes at a cost of some communication overhead. For the repeated evaluation of relatively simple functions it is important to have minimal overhead. This can be achieved by having a limited set of argument types.

Typically, it is best to register the library callback manager in library initialization and unregister the manager in library uninitialization so that the manager is active as long as the library is loaded.

The manger function mfun is of type mbool (*mfun)(WolframLibraryData libData, mintid, MTensor argtypes). If mname has been registered with the manager function mfun, then evaluating ConnectLibraryCallbackFunction[mname,cf] will generate a unique positive integer ID mid; then the function mfun is called with id equal to mid and argtypes a rank 2 MTensor, with each row having the type and rank of the corresponding argument. If there are n arguments, argtypes has length n+1 and the last row is the type and rank of the result. The types are encoded using the macros MType_Integer etc. as defined in WolframLibrary.h. Rank 0 corresponds to scalars. ConnectLibraryCallbackFunction[mname,cf] returns True only mfun returns True. The CompiledFunctioncf will be saved until releaseLibraryCallbackFunction(mid) is called, and then, if there are no other references to the CompiledFunction, it will be freed up.

An example that demonstrates how the callback functionality can be used is implemented in demo_callback.c and shown in the LibraryLinkexamples under demo_callback.

LinkObject Arguments and Result

If you want to send arguments or get results from the library function that are not covered by the MTensor basic numerical types, you can use a LinkObject for arguments and the result. This will let you send the structure of any Wolfram Language expression to your library.

For LinkObject arguments and the result, the library is called in a different way than for basic numerical types. When the function is called, the Wolfram Language writes a list with all the arguments onto a WSTP connection; this is done with LinkWrite, the way the Wolfram Language writes expressions over WSTP. Then it calls the library function. The library function must read the arguments from the link, do its work, and then write its result back onto the link. The Wolfram Language then reads the result from the link with LinkRead, the way that the Wolfram Language reads expressions from WSTP.

An example of a function that works for LinkObject arguments and result is shown below.

To use WSTP for communicating with the Wolfram Language, the mathlink.h header must be included before the WolframLibrary.h header.

Locating Libraries

The first argument to LibraryFunctionLoad is the library to load. This can be given as an absolute file name such as /Library/myLibrary.dylib. However, it is more convenient to give a relative specification to the library, finding it from a path. Also, since different platforms use different extensions for dynamic libraries, it can also be a problem to give the extension. The conventions for extensions for dynamic libraries are summarized in the following.

Windows

dll

Unix and Linux

so

Mac OS X

dylib

Extensions for libraries on different systems.

Functions in the Wolfram Language that work with dynamic libraries automatically solve this problem. If the input name does not have a file extension, then one suitable for your platform is added. This lets you work with libraries in ways that are independent of the machine on which you work. In addition, the Wolfram Language provides a path, $LibraryPath, to use to find libraries.

A sample setting for $LibraryPath is shown below. Note that it includes a number of Wolfram Language applications that contain libraries.

FindLibrary is the function that finds libraries. It is called by other commands, such as LibraryFunctionLoad. FindLibrary first fixes the extension of the library, then looks for the library as an absolute name, and finally looks for the library on $LibraryPath. It returns the file if it is found.

The following searches on $LibraryPath and finds the library suitable for your platform; in this example it is Windows.

You can add elements to $LibraryPath. However, a number of folders are automatically added; this includes any LibraryResources folder found in a Wolfram Language application in $UserBaseDirectory/Applications or $BaseDirectory/Applications. This is similar to the way that J/Link can load Java classes, and DatabaseLink can load database resources. It gives a convenient way to bundle libraries with your Wolfram Language work.

Installing Your Own Libraries

If you want to add dynamic libraries to use in your Wolfram Language session, you have a number of options. These are summarized here.

Absolute Pathname

You can assign an absolute pathname to a function such as LibraryFunctionLoad. This is easy to set up, but will cause problems if you move your work to a different computer.

Setting $LibraryPath

You can add the location of your libraries to $LibraryPath. This gives more abstraction over the use of an absolute pathname, since you can set the location once in a variable and use it many times.

In addition, you can use Block to temporarily change the setting of $LibraryPath. An example is shown below.

$BaseDirectory and $UserBaseDirectory

$LibraryPath always includes two locations, $UserBaseDirectory/SystemFiles/LibraryResources/$SystemID and $BaseDirectory/SystemFiles/LibraryResources/$SystemID. If you place your library here, it will be found by library loading functions.

Applications

If you deliver your code as a Wolfram Language application, you can also include libraries that will be placed on $LibraryPath. You should include a LibraryResources directory with a folder that matches $SystemID. A sample application is shown below.

MyApplication MyApplication.m Kernel init.m FrontEnd Documentation English LibraryResources Windowslibraries for use on Windows Windows-x86-64libraries for use on 64 bit Windows Linuxlibraries for use on Linux Linux-x86-64libraries for use on 64 bit Linux MacOSX-x86libraries for use on MacOSX MacOSX-x86-64libraries for use on 64 bit MacOSX

Library Dependencies

If your library depends on other libraries, you will need to make sure that the dependent libraries are available. You could do this by changing the environment in which the Wolfram Language runs to install these extra libraries in some system location or by changing a path environment variable such as LD_LIBRARY_PATH (for Linux) or PATH (for Windows).

An alternative is to use LibraryLoad to load the dependent libraries before loading your own library. LibraryLoad does not return a function (unlike LibraryFunctionLoad), it merely exists to load dependent libraries.

Under Mac OS X, dynamic libraries (which have a file extension of dylib) have a more complicated mechanism where they can be set up to search in particular places for dependent libraries. You should consult the description of commands such as otool and install_name_tool.

If you have trouble loading a library, you can use $LibraryError to find out more about why it did not load. In some cases this returns information about which dependent libraries cannot be loaded.

Library Version Information

You can get information about the version of a library with the LibraryLink Package. This supplies some extra tools for working with shared libraries.

Problems Loading a Library

If you have problems loading a library, you might want to use $LibraryError to find out more about why it did not load. This is found in the LibraryLink` context, but you do not have to load the package to use it.

The library has dependencies on other libraries. Normally, the Wolfram Language will load the other libraries first. However, if you just try to load the calendar library directly (and you have not already used a calendar function) this load instruction will fail, as in the following.