[Richard Suchenwirth] - For combining Tcl and C code, two ways are well-known: * '''Embedding''' (a Tcl interpreter into a C app) * '''Extending''' (a Tcl interpreter statically, or with dynamic libs) Besides, there are ''[[exec $x]]'' resp. ''[[open |$x r+]]'' to use external executables inside Tcl. In this Christmas fun project, I extended that most simple approach by defining a Tcl proc which * allows to define a C "proc" (in fact, the central part of a main program), * writes a C source file to disk ('''"outsourcing"'''), * compiles that to an executable in the same directory, and * creates a Tcl wrapper proc that in turn calls the executable "C helper". Sounds complicated, but the implementation was quite simple, and the time consumption is not that terrible: on my P200/W95 box, compiling the sample ''foo'' below took 2.4 sec; each invocation of ''foo'' took about 160 msec. The following model is applied: * Tcl arguments are passed in via ''argv''; each is mapped to a ''char'' pointer with the same name. You may convert other representations from that (e.g. ''atoi'' in the example below) * The helper C code is spliced into a ''int main(int argc, char *argv[[]])'' frame, stdio.h and stlib.h are included already. Other #includes can be done early in the helper code. * Additional C functions can be defined completely in the ''-with'' argument. Other arguments allow to override the defaults for compiler name, flags, or generation directory. * The helper puts its regular output to ''stdout''. This is where ''exec'' and ''open'' read from, and you don't have to bother with allocating memory for the result. * In error cases just put a message to ''stderr'', so it is seen from inside Tcl (I hate empty strings as error messages ;-) This will cause ''exec'' and the wrapper proc to error out. A FATAL macro which takes a constant string is pre-defined (see example). * Errors in your C code will cause an error in ''cproc'', where the compiler's error message (at least with gcc) is reported to Tcl. This started out as a holiday braintwister (surfing between Tcl and C in the same procedure is quite a thrill), but could be useful for tasks where C code can process a limited input (8-bit strings only) more efficiently than pure Tcl code. ---- For more powerful code generators, see [Pipe servers in C from Tcl] - [Extending Tcl in C from Tcl] proc cproc {name argl cbody args} { if [llength [info command $name]] {error "$name exists"} array set a [list -cc gcc -ccflags {-s -Wall -W -ansi -pedantic}\ -dir $::env(TEMP) -with {}] array set a $args set cargs "" set narg 0 foreach i $argl { append cargs "\n\t\t char *[lindex $i 0] = argv\[[incr narg]\];" } set nname [file nativename [file join $a(-dir) $name]] set fp [open $nname.c w] puts $fp "/* $name.c - Generated by cproc */ #include #include #define MAXLINE 256 #define FATAL(_s) {fprintf(stderr,\"error: %s\",_s); return -1;} $a(-with) int main(int argc, char *argv\[\]) { $cargs if(argc!=[incr narg]) FATAL(\"usage: $name $argl\"); {$cbody } return 0; }" close $fp eval exec $a(-cc) $a(-ccflags) [list $nname.c -o $nname] set body "exec [list $nname]" foreach i $argl {append body " \$[lindex $i 0]"} proc $name $argl $body } # That's all, now for some usage examples: cproc foo {s {count 2}} { /* repeat a string s n times, where count holds the string rep of n */ int n = atoi(count); int i; if(n<0) FATAL("count must be non-negative"); for(i=0; i should raise an error if 0 { This is what was generated - file ''foo.c'': /* foo.c - Generated by cproc */ #include #include #define MAXLINE 256 #define FATAL(_s) {fprintf(stderr,"error: %s",_s); return -1;} void results(char *x) {printf("%s", x); /* just to show a function */ } int main(int argc, char *argv[]) { char *s = argv[1]; char *count = argv[2]; if(argc!=3) FATAL("usage: foo s {count 2}"); { /* repeat a string s n times, where count holds the string rep of n */ int n = atoi(count); int i; if(n<0) FATAL("count must be non-negative"); for(i=0; i s) putchar(*--cp); } strrev "A man, a plan, a canal: Panama" ;#=> amanaP :lanac a ,nalp a ,nam A # The same in Tcl, for comparison - 410(Tcl) vs. 86000(cproc) microseconds: proc strrevert s { set res "" foreach i [split $s ""] {set res $i$res} set res } }