Library Structure and Life Cycle
Wolfram
LibraryLink allows dynamic libraries to be directly loaded into the
Mathematica kernel so that functions in the libraries can be immediately called from
Mathematica. You can exchange not only C-like data types such as integers, reals, packed arrays, and strings, but also arbitrary
Mathematica expressions. In addition, there are useful functions such as sending errors and calling back to
Mathematica.
This section describes the structure of a Wolfram Library and how they are written. It also covers the different parts of the life cycle of a library, from initialization through usage to uninitialization.
The first time that a function is loaded from a Wolfram Library with
LibraryFunctionLoad, its
initialization function is called. This sets up a structure to support callback functions; it can also be used to carry out any initialization specific to the library itself.
Once a library has been loaded, you can call its functions through the
LibraryFunction returned by
LibraryFunctionLoad. You can also load additional functions.
When the library is no longer required it can be unloaded. This happens when all the functions are unloaded with
LibraryFunctionUnload, or when the entire library is unloaded with
LibraryUnload. At this point the
uninitialize function is called.
Note that unloading a library completely is not supported on all platforms. The
uninitialize function will always be called and functions from the library will not be usable from
Mathematica. However, only platforms that fully support dynamic library unloading will let you modify the library and then reload it. When this documentation was written, Windows and Mac OS X 10.5 fully supported unloading a library.
Initialization
When a library is loaded for the first time using
LibraryFunctionLoad, some important initialization is carried out. This sets up the structure that allows the library function to call back to
Mathematica; it also takes care of versioning. The library allows initialization by providing a function called
WolframLibrary_initialize. Typically, the first part of the library is similar to that shown below.
#include "WolframLibrary.h"
DLLEXPORT mint WolframLibrary_getVersion( ) {
return WolframLibraryVersion;
}
DLLEXPORT int WolframLibrary_initialize( WolframLibraryData libData) {
return 0;
}
Here the header
"WolframLibrary.h", which sets up various important definitions, is loaded. This is followed by the function
WolframLibrary_getVersion, which returns the version of the WolframLibrary header.
Mathematica then calls
WolframLibrary_initialize, passing in a data structure that matches the version of the WolframLibrary header used to build the library. Since the function structure is adapted for your library, this means that you do not have to recompile your library when it is loaded into a new version of
Mathematica. However, you should note that you cannot use a library built with a newer version of the header into an older version of
Mathematica. The
WolframLibrary_initialize function can contain other initializations that you might need to run functions in the library. If it returns a nonzero value, that will be considered a fatal error and the library file will not be loaded.
If your library is going to use
MathLink for communicating with
Mathematica, the
"mathlink.h" header must be included before the
"WolframLibrary.h" header. This is shown below.
#include "mathlink.h"
#include "WolframLibrary.h"
Functions, Arguments, and Results
The library provides functions that are called from
Mathematica. These functions are named in the call to
LibraryFunctionLoad. Because the interface uses C calling conventions, each function has to have the same signature, namely
int demoI_I(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res). A sample is shown below.
DLLEXPORT int demo_I_I(WolframLibraryData libData,
mint Argc, MArgument *Args, MArgument Res) {
mint I0;
mint I1;
I0 = MArgument_getInteger(Args[0]);
I1 = I0 + 1;
MArgument_setInteger(Res, I1);
return LIBRARY_NO_ERROR;
}
MArgument is a typedef for a union of pointers of the supported basic types defined in WolframLibrary.h.
typedef union {
mbool *boolean;
mint *integer;
double *real;
mcomplex *cmplex;
MTensor *tensor;
char **utf8string;
} MArgument;
The function must have the signature
int demo_I_I(mint Argc, MArgument *Args, MArgument Res), it must return
int, and must take three arguments. The first is a machine integer giving the argument count, the second is an array of
MArgument, and the third is an
MArgument. These hold the input and the result, respectively. You have to extract the actual value from the argument array and assign the result. Several macros with names starting with
MArgument are defined for convenience for this extraction and assignment to
MArgument. The returned int value returned is used as an error code: any nonzero return will be considered an error and the evaluation will return a
LibraryFunctionError result. More details about error codes are found in the section
"Error Result Codes".
More details about how to work with types are found in the section
"Type Specification".
In addition, it is typical to add
DLLEXPORT; this is a macro defined in WolframLibrary.h. It is needed on Windows where it declares the function as an export from a Windows DLL, but using it will make your library easier to move from one platform to another.
If you want to send arguments or get results from the library function that are not covered by the basic numerical types or an
MTensor, you can use a
LinkObject for arguments and the result. This will let you send the structure of any
Mathematica expression to your library and get back any
Mathematica expression as a result. A sample
LinkObject function is shown below. Note that you can work directly with strings without using
MathLink.
DLLEXPORT int reverseString( WolframLibraryData libData, MLINK mlp)
{
int res = LIBRARY_FUNCTION_ERROR;
long len;
const char *inStr = NULL;
char* outStr = NULL;
if ( !MLCheckFunction( mlp, "List", &len))
goto retPt;
if ( len != 1)
goto retPt;
if(! MLGetString(mlp, &inStr))
goto retPt;
if ( ! MLNewPacket(mlp) )
goto retPt;
outStr = reverseStringImpl(inStr);
if ( !MLPutString( mlp,outStr))
goto retPt;
res = LIBRARY_NO_ERROR
retPt:
if ( inStr != NULL)
MLReleaseString(mlp, inStr);
if ( outStr != NULL)
free((void*)outStr);
return res;
}
To use
MathLink for communicating with
Mathematica, the
"mathlink.h" header must be included before the
"WolframLibrary.h" header.
More information on using
MathLink to communicate with
Mathematica is found in the section
LinkObject Arguments and Result.
Naming Conventions
Note that the name of the function is used to load it into
Mathematica. If you compile your library as C++ then you will either have to export the name as a C name or use the C++ modified name in
LibraryFunctionLoad. The former can be done by a declaration such as the following.
extern "C" {
DLLEXPORT int funname(WolframLibraryData libData,
mint Argc, MArgument *Args, MArgument Res);
}
This makes sure that
funname can be used to load the function even though the library is compiled as C++.
Alternatively, you can use a macro defined in WolframLibrary.h to make the code more portable.
EXTERN_C DLLEXPORT int funname(WolframLibraryData libData,
mint Argc, MArgument *Args, MArgument Res);
Types
In your library function you can use any types that you want, except for arguments from
Mathematica and results that go to
Mathematica. These are described below.
| mbool | Boolean |
| mint | machine integer |
| double | machine double |
| mcomplex | machine complex |
| MTensor | packed array |
| UTF8String | UTF8 string |
There are definitions for
mbool,
mint,
mcomplex, and
MTensor in WolframLibrary.h.
mint is an integral type that matches the machine integer type in
Mathematica; note that this might be 32 or 64 bits.
Callbacks
Often you may need extra functionality that is present in
Mathematica. For example, you might want to create a packed array and then work with its elements. This is done via the
WolframLibraryData object that is passed in as an argument to the function. In the following example, a packed array is created, and then its elements are filled in.
T0 = funStruct->MTensor_new(MType_Integer, 1, dims);
for ( i = 1; i <= I0; i++) {
libData->MTensorVector_setInteger( T0, i, i*2);
}
Callback Evaluations in Mathematica
If you want to call
Mathematica from your library function to carry out some other task, for example, carrying out some computation or calling some input/output function, you can do this with
getMathLink and
processMathLink.
The following example shows how you get a
MathLink connection and then write an expression inside
EvaluatePacket on the link using functions from the
MathLink API. When you have finished, you call
processMathLink and
Mathematica will carry out the evaluation.
MLINK link = libData->getMathLink(libData);
MLPutFunction( link, "EvaluatePacket", 1);
MLPutFunction( link, "Message", 2);
MLPutFunction( link, "MessageName", 2);
MLPutSymbol( link, "MyFunction");
MLPutString( link, "info");
MLPutString( link, "Message called from within Library function.");
libData->processMathLink( link);
pkt = MLNextPacket( link);
if ( pkt == RETURNPKT) {
MLNewPacket(link);
}
The result from
Mathematica will follow the standard packet rules for
Mathematica; for example, an
EvaluatePacket will return a result in
ReturnPacket.
Errors
If an error is found in a library function and you want to report this to a user, you can use the callback function
Message. For example, the following will cause the message
LibraryFunction
to be emitted. You would have to supply text for the message.
if (libData->MTensor_getRank(T0) != 1) {
libData->Message("rankerror");
return LIBRARY_FUNCTION_ERROR;
}
Note that returning
LIBRARY_FUNCTION_ERROR from the library will cause a
LibraryFunctionError result to be returned.
A more flexible way to send a message would be to use the c
allback evaluation technique described previously.
Error Result Codes
The result of a library function is expected to be an int and is used to determine if an error took place. If a result of 0 or
LIBRARY_NO_ERROR is returned the function is taken to have completed correctly. Alternately, if a different result is returned this indicates an error and the result of the function call is a
LibraryFunctionError expression.
There are a number of error codes that you can return. They are listed in the following table.
| LIBRARY_NO_ERROR | correct result from the function (value of 0) |
| LIBRARY_TYPE_ERROR | unexpected type encountered |
| LIBRARY_RANK_ERROR | unexpected rank encountered |
| LIBRARY_DIMENSION_ERROR | inconsistent dimensions encountered |
| LIBRARY_NUMERICAL_ERROR | error in numerical computation |
| LIBRARY_MEMORY_ERROR | problem allocating memory |
| LIBRARY_FUNCTION_ERROR | generic error from a function |
Error codes.
Each nonzero error code leads to a specific type of message being issued when the function call has finished, except for
LIBRARY_FUNCTION_ERROR, which issues no message, letting the application making the function call issue its own message.
In the following example a demonstration function is loaded. This function returns its input which lets you see the effect of different codes.
| Out[1]= |  |
This returns 6 from the library function which is the value of
LIBRARY_FUNCTION_ERROR. The result of the evaluation is a
LibraryFunctionError expression, which contains the name of the error code and its numerical value.
| Out[2]= |  |
Errors in Callbacks
If an error arises in a callback function it will return an error code.
For example, the
MTensor_getInteger callback gets an element from an
MTensor of integer type. But, if it is called on an
MTensor that does not contain integers or the element does not exist, then an error code is returned.
int err;
...
err = libData->MTensor_getInteger( T0, lens, &res);
If you are certain that the
MTensor is of the correct type and has sufficient elements then you do not need to inspect the result.
Uninitialization
When the library is no longer used by
Mathematica it will call a function to shut down the library; a sample is shown below. The function receives a
WolframLibraryData object and it can use callback functions if required.
DLLEXPORT void WolframLibrary_uninitialize( WolframLibraryData libData) {
return;
}