Running the Wolfram System from within an External Program

To run the Wolfram System from within an external program requires making use of many general features of WSTP (Wolfram Symbolic Transfer Protocol). The first issue is how to establish a WSTP connection to the Wolfram System.

When you use WSTP templates to create external programs that can be called from the Wolfram Language, source code to establish a WSTP connection is automatically generated, and all you have to do in your external program is to call (NonWLLink[WSMain, paclet:ref/c/WSMain])(argc,argv). But in general you need to call several functions to establish a WSTP connection.

Opening and closing WSTP connections.

Include the standard WSTP header file.

#include "wstp.h"

int main(int argc, char *argv[]) {

WSENV env;
   WSLINK link;
   int errno;

This initializes WSTP library functions.

env = WSInitialize(0);

This opens a WSTP connection, using the same arguments as were passed to the main program.

link = WSOpenArgcArgv(env, argc, argv, &errno);

This activates the connection, waiting for the other program to respond.

WSActivate(link);

...
}

Often the argv that you pass to (NonWLLink[WSOpenArgcArgv(), paclet:ref/c/WSOpenArgcArgv]) will come directly from the argv that is passed to main() when your whole program is started.

The elements in the argv array are character strings that mirror the arguments and options used in the Wolfram Language functions (RefLink[LinkLaunch, paclet:ref/LinkLaunch]), (RefLink[LinkCreate, paclet:ref/LinkCreate]), and (RefLink[LinkConnect, paclet:ref/LinkConnect]).

Possible elements of the argv array passed to (NonWLLink[WSOpenArgcArgv(), paclet:ref/c/WSOpenArgcArgv]).

As an alternative to (NonWLLink[WSOpenArgcArgv(), paclet:ref/c/WSOpenArgcArgv]) you can use (NonWLLink[WSOpenString(), paclet:ref/c/WSOpenString]), which takes parameters concatenated into a single character string with spaces in between.

Once you have successfully opened a WSTP connection to the Wolfram Language kernel, you can then use standard WSTP functions to exchange data with it.

Functions often used in communicating with the Wolfram Language kernel.

Once you have sent all the pieces of a packet using (NonWLLink[WSPutFunction(), paclet:ref/c/WSPutFunction]) etc., WSTP requires you to call (NonWLLink[WSEndPacket(), paclet:ref/c/WSEndPacket]) to ensure synchronization and consistency.

One of the main issues in writing an external program that communicates directly with the Wolfram Language kernel is handling all the various kinds of packets that the kernel can generate.

The function (NonWLLink[WSNextPacket(), paclet:ref/c/WSNextPacket]) finds the head of the next packet that comes from the kernel, and returns a constant that indicates the type of the packet.

Some packets recognized by (NonWLLink[WSNextPacket(), paclet:ref/c/WSNextPacket]).

This keeps on reading data from a link, discarding it until an error or a (RefLink[ReturnPacket, paclet:ref/ReturnPacket]) is found.

while ((p = WSNextPacket(link)) && p != RETURNPKT)
    WSNewPacket(link);

If you want to write a complete front end to the Wolfram System, you will need to handle all of the possible types of packets that the kernel can generate. Typically you can do this by setting up an appropriate switch on the value returned by (NonWLLink[WSNextPacket(), paclet:ref/c/WSNextPacket]).

The WSTP Developer Kit contains sample source code for several simple but complete front ends.

Flow of data on links.

One feature of more sophisticated external programs such as front ends is that they may need to perform operations while they are waiting for data to be sent to them by the Wolfram Language. When you call a standard WSTP library function such as (NonWLLink[WSNextPacket(), paclet:ref/c/WSNextPacket]), your program will normally block until all the data needed by this function is available.

You can avoid blocking by repeatedly calling (NonWLLink[WSReady(), paclet:ref/c/WSReady]), and only calling functions like (NonWLLink[WSNextPacket(), paclet:ref/c/WSNextPacket]) when (NonWLLink[WSReady(), paclet:ref/c/WSReady]) no longer returns 0. (NonWLLink[WSReady(), paclet:ref/c/WSReady]) is the analog of the Wolfram Language function (RefLink[LinkReadyQ, paclet:ref/LinkReadyQ]).

Note that WSTP sometimes buffers the data that you tell it to send. To make sure that all necessary data has been sent you should call (NonWLLink[WSFlush(), paclet:ref/c/WSFlush]). Only after doing this does it make sense to call (NonWLLink[WSReady(), paclet:ref/c/WSReady]) and wait for data to be sent back.