'''Executable Modules''' provide additional functionality to a Tcl interpreter by means of a procedure that when called, [ipc%|%communicates] with an external program and then returns a result. ** Executable Modules ** The external program component of an '''executable module''' can be implemented in any language. Sometimes pre-existing programs can be put to use in an executable model. All that is required is a procedure that manages the communication with that program. An executable module typically communicates via Tcl command channel created via `[open]`. To communicate binary data, use `[chan configure] $comechan -translation binary`. Alternatively, shared memory could be used. '''Pro:''' 1. Any language may be used for an executable module. 2. Debugging may be easier, because programs have an extra memory boundary. 3. Testing may be easier, because the program can be tested alone (interactively in many cases). '''Con:''' 1. Performance may be an issue if lots of I/O is required. 2. Shared state can be a problem. A possible solution is shared memory. 3. Complex/structured data can be a problem. Tcl handles serializing very well, but other languages are often weak in this area. ======c /* * By George Peter Staplin * This is version 2 of a simple executable-module written in C. * This is simply a demonstration. */ #include #include #include #include #include #define SEPFMT " \t\n" void get_input (char *buf, size_t s) { if (NULL == fgets (buf, s, stdin)) { if (feof (stdin)) { exit (EXIT_SUCCESS); } exit (EXIT_FAILURE); } } int expect_int (void) { char *t = strtok (NULL, SEPFMT); char *end; long l; if (NULL == t) { fprintf (stderr, "premature end of token stream.\n"); exit (EXIT_FAILURE); } l = strtol (t, &end, 10); if ((LONG_MIN == l && ERANGE == errno) || (end < (t + (strlen (t) - 1)))) { fprintf (stderr, "invalid integer '%s'\n", t); exit (EXIT_FAILURE); } return (int) l; } int main (int argc, char *argv[]) { char buf[1024]; char *tok; again: get_input (buf, sizeof (buf)); tok = strtok (buf, SEPFMT); if (NULL == tok || 1 != strlen (tok)) { fprintf (stderr, "invalid operator: '%s'\n", tok); exit (EXIT_FAILURE); } switch (tok[0]) { case '+': printf ("%d\n", expect_int () + expect_int ()); break; case '-': { int a, b; a = expect_int (); b = expect_int (); printf ("%d\n", a - b); } break; default: fprintf (stderr, "unknown operator: '%c'\n", tok[0]); exit (EXIT_FAILURE); } goto again; return EXIT_FAILURE; } ====== To use the code above as a module we will use a two-way pipe. Tcl uses the `|` character in `[open]` to create channel between to the program. For two-way communication, use the '''`w+`''' flag. `[gets]` is used to retrieve the result. `[read]` could also be used, but it would require a fixed-length format, non-blocking I/O, or a read of a single character until `\n` is reached. ====== #! /bin/env tclsh set ::mathModule [open {|./modmath} w+] proc modMath {args} { puts -nonewline "$args is " puts $::mathModule $args flush $::mathModule return [gets $::mathModule] } proc main {} { puts [modMath + 200 300] puts [modMath - 5600 1243] puts Done } main ====== <> distributed computing