The Standalone Applications SDK =============================== In order to understand the standalone applications SDK, the Wolfram Language Runtime must first be introduced. The runtime makes Wolfram functionality available to your application. Specifically, it is a library that the application will link against. This library is the core component of the Wolfram system, and it is the same component that is used when you evaluate an expression in a Wolfram notebook. The interface discussed in this document makes it possible to access the runtime directly. Note that there is no interprocess communication cost with this interface (unlike most usages of WSTP). As a result, the runtime offers a high-speed interface to the Wolfram Language. The standalone applications SDK is a static library that your application will use to access the runtime. The SDK automatically handles loading the runtime dynamic library and its dependencies. The SDK is prepackaged in the Wolfram Language layout under ``SystemFiles/Components/StandaloneApplicationsSDK``. The SDK (and runtime dynamic library) make available a C function interface that allows you to have the Wolfram Language kernel running in your application's process. These functions enable developers to build, evaluate, and deconstruct Wolfram Language expressions within their applications. For this reason, another name for this interface is the expression API. Individual documentation for expression API functions are available :ref:`here<refinfo>`. Information regarding general principles of the interface are available here :ref:`here<topics>`. Quick Check: Is the SDK right for you? -------------------------------------- ☑ You are distributing an application to a platform in https://www.wolfram.com/mathematica/system-requirements/ ☑ Your application will not require a Wolfram notebook interface. However, your application can use the frontend internally (e.g., to generate an image of a ``Plot[]`` using ``Rasterize``). .. _refinfo: Reference Information --------------------- .. contents:: :local: :depth: 2 Macros ^^^^^^ .. doxygengroup:: defines :content-only: Types ^^^^^ .. doxygengroup:: typedefs :content-only: .. _startingstopping: Starting and stopping the runtime ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. doxygengroup:: startruntime :content-only: Handling error expressions ^^^^^^^^^^^^^^^^^^^^^^^^^^ .. doxygengroup:: error :content-only: Managing memory ^^^^^^^^^^^^^^^ .. doxygengroup:: memory :content-only: Evaluating expressions ^^^^^^^^^^^^^^^^^^^^^^ .. doxygengroup:: eval :content-only: Creating and interacting with expressions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. doxygengroup:: misc :content-only: Interacting with the MNumericArray type ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. doxygengroup:: wlr_MNumericArray :content-only: .. _topics: Topics ------ Thread Safety ^^^^^^^^^^^^^ .. warning:: The expression API is not thread-safe. All calls to the expression API must be serialized. If an application uses multiple threads, it is your responsibility to ensure that only one thread is interacting with the runtime at any given time. A common pattern is to use a dedicated thread for all expression API calls or to protect all sections of code that call the expression API with a global mutex. This warning does not apply to ``wlr_Abort`` and ``wlr_ClearAbort``. These functions are safe to call from any thread or from a signal handler. Licensing ^^^^^^^^^ .. (AI generated) When starting the runtime with ``wlr_StartRuntime``, ``WLR_START_RUNTIME``, ``wlr_sdk_StartRuntime``, or ``WLR_SDK_START_RUNTIME``, a choice must be made between two licensing mode arguments. This choice affects how the application is licensed and what code it is permitted to run. WLR_LICENSE_OR_SIGNED_CODE_MODE """"""""""""""""""""""""""""""" This mode first checks if a standard Wolfram license is available on the system. If a license is found, the runtime will use it, and no license signature restrictions will be in effect. If no license is found, it will fall back to using the license signatures available to the application. * **When to Use It:** During development and debugging * **Why:** This mode offers the most flexibility. A developer running the application on their own machine can use their existing Wolfram license to call any function and test code interactively without needing to constantly re-generate license signatures. WLR_SIGNED_CODE_MODE """""""""""""""""""" This mode exclusively uses available license signatures and will never search for or use a Wolfram license. In this mode, only Wolfram Language code that has been explicitly signed can be evaluated. * **When to Use It:** For all distributed and released applications. * **Why:** It guarantees that the application is self-contained and will not accidentally consume a license belonging to an end-user who also happens to have a Wolfram product installed Summary of Best Practices """"""""""""""""""""""""" A typical development cycle involves: 1. Using ``WLR_LICENSE_OR_SIGNED_CODE_MODE`` during development for rapid iteration. 2. Switching to ``WLR_SIGNED_CODE_MODE`` before packaging the final application for release. For more on license signatures and what needs to be covered by them, see :ref:`this document<licenseapi>` and relevant notes in the API reference above. The Expression Type ^^^^^^^^^^^^^^^^^^^ .. (AI generated) The fundamental data type in the expression API is the ``wlr_expr`` type. It serves as an opaque reference to a Wolfram Language expression managed by the runtime. All interactions with ``wlr_expr`` objects performed through expression API functions. A ``wlr_expr`` can represent any Wolfram Language expression. The lifetime of ``wlr_expr`` objects can include the following patterns: * **Creation**: An expression is generated using a creation function, such as ``wlr_Integer``, ``wlr_Symbol``, or the macro ``wlr_E`` * **Interaction**: The expression can be interacted with using functions like ``wlr_ExpressionType`` and ``wlr_Part`` * **Evaluation**: The expression can be evaluated by the runtime using functions like ``wlr_Eval``, which returns a new ``wlr_expr`` handle for the result * **Release**: The memory for an expression is managed by expression pools. An expression is automatically deallocated when its managing pool is released. The following snippet demonstrates these patterns:: /* Create expression pool */ wlr_CreateExpressionPool(); /* Create the necessary component expressions */ wlr_expr head = wlr_SystemSymbol("Plus"); wlr_expr arg1 = wlr_Integer(1); wlr_expr arg2 = wlr_Integer(2); /* Construct the full expression: 1+2 */ wlr_expr full = wlr_E(head, arg1, arg2); /* Evaluate the expression */ wlr_expr result = wlr_Eval(full); /* Interact with the result */ mint resultValue; if (wlr_IntegerData(result_expr, &resultValue) == WLR_SUCCESS) { /* result will be 3 */ } /* Memory for all created expressions is managed by the active expression pool. This will release them. */ wlr_ReleaseExpressionPool(); Error Handling ^^^^^^^^^^^^^^ .. (AI generated) The expression API has two primary mechanisms for reporting errors: a status code of type ``wlr_err_t`` and a special ``wlr_expr`` known as an error expression. Error expresions are special ``wlr_expr`` objects that are treated differently by the expression API. They signify that an operation has failed. The function ``wlr_ErrorQ`` can be used to test if a given ``wlr_expr`` is an error expression. If it is, its specific error type can be retrieved as a ``wlr_err_t`` value using ``wlr_ErrorType``. Functions that return a ``wlr_expr`` are designed for error propagation. Before executing, these functions check if any of their ``wlr_expr`` arguments are already error expressions. If so, the function forgoes its own logic and immediately returns the error expression it received. This design allows for chaining multiple API calls together and checking for an error only once at the end of the sequence. Functions that do not return a ``wlr_expr`` handle errors differently. They do not propagate an error expression, and typically they return a ``wlr_err_t`` value instead. If an error expression is passed as an argument to one of these functions, the function will return the specific status code ``WLR_ERROR_EXPRESSION``. The ``wlr_err_t`` enum is also used to return status codes for operations that may fail for reasons other than receiving an error expression, such as ``WLR_OUT_OF_BOUNDS`` when an invalid index is provided to ``wlr_Part``. This provides a consistent way to check for errors across the entire API. The following snnipet demonstrates these patterns:: /* Create an error expression by calling a function that fails. wlr_Rational will not allow a denominator of zero. */ divisionByZero = wlr_Rational(wlr_Integer(1), wlr_Integer(0)); if (wlr_ErrorQ(divisionByZero)) { /* Check the error and get its type. This will print the code for WLR_MISCELLANEOUS_ERROR. */ printf("wlr_Rational failed with code: %d\n", wlr_ErrorType(divisionByZero)); } /* Demonstrate propagation. wlr_E receives the error expression and immediately returns it without creating a new expression. */ propagatedExpr = wlr_E(wlr_Symbol("Wrapper"), divisionByZero); if(wlr_SameQ(divisionByZero, propagatedExpr)) { printf("The error was correctly propagated.\n"); } /* Demonstrate an error type-returning function handling an error expression. wlr_IntegerData returns a wlr_err_t, not an expression. */ errCode = wlr_IntegerData(propagatedExpr, &integerValue); if(errCode == WLR_ERROR_EXPRESSION) { printf("wlr_IntegerData correctly reported it received an error.\n"); } /* Demonstrate an error type-returning function failing for a different reason. A string cannot be converted to an integer. */ validString = wlr_String("hello"); errCode = wlr_IntegerData(validString, &integerValue); if(errCode == WLR_UNEXPECTED_TYPE) { printf("wlr_IntegerData correctly reported an unexpected type.\n"); }