Two-Way Communication with External Programs

When you install a Wolfram Symbolic Transfer Protocol (WSTP)-compatible external program using (RefLink[Install, paclet:ref/Install]), the program is set up to behave somewhat like a simplified Wolfram Language kernel. Every time you call a function in the external program, a (RefLink[CallPacket, paclet:ref/CallPacket]) is sent to the program, and the program responds by sending back a result wrapped in a (RefLink[ReturnPacket, paclet:ref/ReturnPacket]).

This installs an external program, returning the (RefLink[LinkObject, paclet:ref/LinkObject]) used for the connection to that program:

link=Install["bitsprog"]

The function ExternalCall sends a (RefLink[CallPacket, paclet:ref/CallPacket]) to the external program:

?bits

You can send the (RefLink[CallPacket, paclet:ref/CallPacket]) explicitly using (RefLink[LinkWrite, paclet:ref/LinkWrite]). The first argument of the (RefLink[CallPacket, paclet:ref/CallPacket]) specifies which function in the external program to call:

LinkWrite[link,CallPacket[0,{67}]]

Here is the response to the (RefLink[CallPacket, paclet:ref/CallPacket]) from the external program:

LinkRead[link]

If you use (RefLink[Install, paclet:ref/Install]) several times on a single external program, the Wolfram System will open several WSTP connections to the program. Each connection will however always correspond to a unique (RefLink[LinkObject, paclet:ref/LinkObject]).

Identifying different instances of a single external program.

:Begin:
:Function:      addto

This gives (RefLink[$CurrentLink, paclet:ref/$CurrentLink]) as an argument to addto.

:Pattern:       addto[$CurrentLink, n_Integer]

:Arguments:     {n}
:ArgumentTypes: {Integer}
:ReturnType:    Integer
:End:

This zeros the global variable counter every time the program is started.

int counter = 0;

int addto(int n) {
    counter += n;
    return counter;
}

This installs one instance of the external program containing addto:

ct1=Install["addtoprog"]

This installs another instance:

ct2=Install["addtoprog"]

This adds 10 to the counter in the first instance of the external program:

addto[ct1,10]

This adds 15 to the counter in the second instance of the external program:

addto[ct2,15]

This operates on the first instance of the program again:

addto[ct1,20]

If an external program maintains information about its state then you can use different instances of the program to represent different states. (RefLink[$CurrentLink, paclet:ref/$CurrentLink]) then provides a way to refer to each instance of the program.

The value of (RefLink[$CurrentLink, paclet:ref/$CurrentLink]) is temporarily set every time a particular instance of the program is called, as well as when each instance of the program is first installed.

Sending a string for evaluation by the Wolfram System.

The two-way nature of WSTP connections allows you not only to have the Wolfram System call an external program, but also to have that external program call back to the Wolfram System.

In the simplest case, you can use the WSTP function WSEvaluateString() to send a string to the Wolfram System. The Wolfram System will evaluate this string, producing whatever effects the string specifies, but it will not return any results from the evaluation back to the external program.

To get results back you need explicitly to send an (RefLink[EvaluatePacket, paclet:ref/EvaluatePacket]) to the Wolfram System, and then read the contents of the (RefLink[ReturnPacket, paclet:ref/ReturnPacket]) that comes back.

This starts an (RefLink[EvaluatePacket, paclet:ref/EvaluatePacket]).

WSPutFunction(stdlink, "EvaluatePacket", 1);

This constructs the expression (RefLink[Factorial, paclet:ref/Factorial])[7] or 7!.

WSPutFunction(stdlink, "Factorial", 1);
    WSPutInteger32(stdlink, 7);

This specifies that the packet you are constructing is finished.

WSEndPacket(stdlink);

This checks the (RefLink[ReturnPacket, paclet:ref/ReturnPacket]) that comes back.

WSCheckFunction(stdlink, "ReturnPacket", &n);

This extracts the integer result for 7! from the packet.

WSGetInteger32(stdlink, &ans);

Sending a packet to the Wolfram System.

When you can send the Wolfram System an (RefLink[EvaluatePacket, paclet:ref/EvaluatePacket])[input], it may in general produce many packets in response, but the final packet should be (RefLink[ReturnPacket, paclet:ref/ReturnPacket])[output]. "Manipulating Expressions in External Programs" discusses how to handle sequences of packets and expressions whose structure you do not know in advance.