Executable Modules provide additional functionality to a Tcl interpreter by means of a command that when called, communicates with an external program and then returns a result.
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:
Con:
/* * By George Peter Staplin * This is version 2 of a simple executable-module written in C. * This is simply a demonstration. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <limits.h> #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