Richard Suchenwirth - For combining Tcl and C code, two ways are well-known:
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
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:
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 <stdio.h>
#include <stdlib.h>
#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<n; i++) { results(s); }
} -with {
void results(char *x) {printf("%s", x); /* just to show a function */ }
}
foo grill 5
#foo bar -1 => should raise an errorThis is what was generated - file foo.c:
/* foo.c - Generated by cproc */
#include <stdio.h>
#include <stdlib.h>
#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<n; i++) { results(s); }
}
return 0;
}And on the Tcl side:
proc foo {s {count 2}} {exec {C:\WINDOWS\TEMP\foo} $s $count}
#--------------------------------------------------------- Another example:
cproc strrev s {
/* Revert a string */
char *cp = s+strlen(s);
while(cp > 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
}