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 WSMain(argc,argv). But in general you need to call several functions to establish a WSTP connection.
WSENV WSInitialize(0) | initialize WSTP library functions |
WSLINK WSOpenArgcArgv(WSENV env,int argc,char**argv,int*errno) | |
open a WSTP connection taking parameters from an argv array | |
WSLINK WSOpenString(WSENV env,char*string,int*errno) | |
open a WSTP connection taking parameters from a single character string | |
int WSActivate(WSLINK link) |
activate a WSTP connection, waiting for the program at the other end to respond
|
void WSClose(WSLINK link) | close a WSTP connection |
void WSDeinitialize(WSENV env) | deinitialize WSTP library functions |
#include "wstp.h"
int main(int argc, char *argv[]) {
WSENV env;
WSLINK link;
int errno;
env = WSInitialize(0);
link = WSOpenArgcArgv(env, argc, argv, &errno);
WSActivate(link);
...
}
Often the argv that you pass to 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 LinkLaunch, LinkCreate, and LinkConnect.
"-linklaunch" | operate like LinkLaunch["name"] |
"-linkcreate" | operate like LinkCreate["name"] |
"-linkconnect" | operate like LinkConnect["name"] |
"-linkname","name" | give the name to use |
"-linkprotocol","protocol" |
give the link protocol to use (
TCPIP
,
Pipes
, etc.)
|
As an alternative to WSOpenArgcArgv() you can use 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.
int WSEndPacket(WSLINK link) | indicate the end of a packet |
int WSNextPacket(WSLINK link) | find the head of the next packet |
int WSNewPacket(WSLINK link) | skip to the end of the current packet |
Once you have sent all the pieces of a packet using WSPutFunction() etc., WSTP requires you to call 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 WSNextPacket() finds the head of the next packet that comes from the kernel, and returns a constant that indicates the type of the packet.
Wolfram Language packet | constant | |
ReturnPacket[expr] | RETURNPKT | result from a computation |
ReturnTextPacket["string"] | RETURNTEXTPKT | textual form of a result |
InputNamePacket["name"] | INPUTNAMEPKT | name of an input line |
OutputNamePacket["name"] | OUTPUTNAMEPKT | name of an output line |
TextPacket["string"] | TEXTPKT | textual output from functions like Print |
MessagePacket[symb,"tag"] | MESSAGEPKT | name of a message generated by the Wolfram Language |
InputPacket["prompt"] | INPUTPKT | request for a response to an Input function |
CallPacket[i,list] | CALLPKT | request for a call to an external function |
Some packets recognized by WSNextPacket().
This keeps on reading data from a link, discarding it until an error or a 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 WSNextPacket().
int WSReady(WSLINK link) | test whether there is data waiting to be read on a link |
int WSReadyParallel (WSENV e, WSLINK *links, int n, mltimeval t) | |
test in parallel whether there is data to be read from a list of links | |
int WSFlush(WSLINK link) | flush out buffers containing data waiting to be sent on a link |
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 WSNextPacket(), your program will normally block until all the data needed by this function is available.
You can avoid blocking by repeatedly calling WSReady(), and only calling functions like WSNextPacket() when WSReady() no longer returns 0. WSReady() is the analog of the Wolfram Language function LinkReadyQ.