cxtend -Tk 1 -name mywish -cc gcc -ccflags {-s -Wall} -cmd { plus1 {int i} {i++;} {int i} strrev {char* s} { char *cp0, *cp1, t; for(cp0=s, cp1=s+strlen(s)-1; cp1>cp0; cp0++, cp1--) { t=*cp0; *cp0=*cp1; *cp1=t; } } {char* s} } -dir .and get a new wish that also understands the plus1 command to increment a numeric value (example from Brent Welch's book), and strrev to revert an 8-bit string in place. In contrast to the earlier cproc and cserver, the specification of a new command is now in four parts:
- name inparameters cbody outparameter
proc cxtend {args} { array set a { -name {} -Tk 1 -cc gcc -ccflags {-Wall -s -pedantic} -dir . -cmd {} } array set a $args if $a(-Tk) { if {$a(-name) eq {}} {set a(-name) cxwish} set i tk; set main Tk_Main } else { if {$a(-name) eq {}} {set a(-name) cxtclsh} set i tcl; set main Tcl_Main } set nname [file nativename [file join $a(-dir) $a(-name)]] set fp [open $nname.c w] puts $fp "/* $a(-name).c - Generated by cxtend */" puts $fp "#include <$i.h>" set cmds [list] foreach {cname cin cbody cout} $a(-cmd) { puts $fp [genCmd $cname $cin $cbody $cout] lappend cmds $cname } puts $fp "int AppInit(Tcl_Interp *interp) \{ if(Tcl_Init(interp) == TCL_ERROR) return TCL_ERROR;" if $a(-Tk) { puts $fp "\t\tif(Tk_Init(interp) == TCL_ERROR) return TCL_ERROR;" } foreach i $cmds { puts $fp "\t\tTcl_CreateObjCommand(interp,\"$i\",${i}cmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);" } puts $fp " Tcl_SetVar(interp,\"tcl_rcFileName\",\"~/.wishrc\",TCL_GLOBAL_ONLY); return TCL_OK; \} int main(int argc, char *argv\[\]) { ${main}(argc, argv, AppInit); return 0; }" close $fp puts "$a(-cc) $a(-ccflags) [list $nname.c -o $nname]" eval exec $a(-cc) $a(-ccflags) [list $nname.c -o $nname] set nname } proc genCmd {cname cin cbody cout} { array set what {char* String double Double int Int long Long} set res "int ${cname}cmd(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv\[\]) \{ Tcl_Obj *optr; " set nargs 1; set names [list] foreach {type name} $cin { if {![info exists what($type)]} {error "bad type $type"} append res "$type $name; " lappend names $name incr nargs } append res "\n\tif(objc!=$nargs) \{ Tcl_WrongNumArgs(interp,1,objv,\"Usage: $cname $names\"); return TCL_ERROR; \} " set i 0 foreach {type name} $cin { if ![regexp {[*]$} $type] { append res " if(Tcl_Get$what($type)FromObj(interp,objv\[[incr i]\],&$name)!=TCL_OK) return TCL_ERROR; " } else { append res " if(!(s=Tcl_GetStringFromObj(objv\[[incr i]\],NULL))) return TCL_ERROR; " } } foreach {type name} $cout break if {$type=="char*"} {set name $name,-1} append res " {$cbody} optr = Tcl_GetObjResult(interp); Tcl_Set$what($type)Obj(optr, $name); return TCL_OK; \} " }Disclaimer: Holidays are over, and on the last evening I brought this to generate a nice-looking and well-compiling source, but linking was only possible under bash (makes a slim 3.5K executable with the -s option), not from inside Tcl. Lib-path specification problems. Worked alright on Sun after I added platform-specific defaults:
if {$::tcl_platform(os)=="SunOS"} { append a(-ccflags) " -I/tools/RC/include/ -I/usr/openwin/include \ -L/tools/RC/lib -ltcl -ltk" }Afterthought: To extend a running wish application with compiled C code, it would be smarter to make a shared lib/DLL from the generated source and load that. Hmm - more to learn...
The Embedded C application [1] was designed to allow you to include C code in your scripts. It worked on OSF and SunOS.