''Source code is now at http://www.equi4.com/critlib/ - [JCW]'' * Version 0.18 now runs on Linux and Windows (MinGW) * As of 0.22, you can store the binaries generated in a specific directory with a more meaningful name * source is now up to 0.28 (2001-Nov-21) ---- See also Richard Suchenwirth's [C code generators] page. ---- The '''C Runtime In Tcl''' is a self-contained package to build C code into an extension on the fly. It is somewhat inspired by Brian Ingerson's Inline for Perl, but CriTcl is considerably more lightweight. [[While [Python] also has an Inline [http://pyinline.sourceforge.net/], it apparently has no direct genetic connection to CriTcl.] The idea is to wrap C code into something that will compile into a Tcl extension, and then also fire up the compiler. Compiled chunks will be cached in your ~/.critcl/ directory, so subsequent use will be instant. The main definition is "critcl::cproc", which lets you define a (surprise) C proc, with C code as body. Args and return values must be typed. There are no default args or ways to pass more sophisticated data items than int/long/float/double/char* for now. There is also a "critcl::ccode" call, to inject C code as is, and a "ccommand" call, which ties code to the Tcl_CreateObjCommand without further wrapping. The use of Tcl stubs, and the fact that this extension has all include files it needs to make compilation self-contained, means that this is a pure Tcl package, which should work with any (8.1 and up) installation of Tcl. Most importantly, CriTcl does not care a bit where Tcl was installed, nor even whether it was built as a static or as a dynamic executable. This is a working demo, but it is still young. It will for now blindly use "gcc" to do the compile and has somewhat rudimentary error handling. CriTcl has been verified to work on Linux and Win NT4 (MinGW) so far. Oh, one more thing: this code assumes the md5 command is available. ---- ''There's a new CritLib package with several extensions (blowfish, hexdump, ihash, lazy, lzrw1, md5c, mvector, noop, rchan, zlib) - see http://www.equi4.com/critlib/'' ---- Source: ---- package require critcl namespace import critcl::* cproc noop {} void {} cproc add {int x int y} int { return x + y; } cproc cube {int x} int { return x * x * x; } puts "add 123 456 : [add 123 456]" catch {add 1} err; puts "add 1 : $err" catch {add 1 2 3} err; puts "add 1 2 3 : $err" catch {add 0 zero} err; puts "add 0 zero : $err" proc sum {a b} { return [expr {$a+$b}] } proc pow3 {a} { return [expr {$a*$a*$a}] } proc ntimes {n cmd t} { set on $n set r {} while {[incr n -1] >= 0} { lappend r $cmd } set v [uplevel 1 [list time [join $r {; }] $t]] return [lreplace $v 0 0 [expr {[lindex $v 0]/(1.0*$on)}]] } puts "" puts "Tcl noop: [ntimes 100 {} 1000]" puts " C noop: [ntimes 100 {noop} 1000]" set a 1 set b 2 puts "" puts "Tcl expr: [ntimes 100 {expr {1+2}} 1000]" puts "Tcl vars: [ntimes 100 {expr {$a+$b}} 1000]" puts "Tcl sum: [ntimes 100 {sum 1 2} 1000]" puts " C add: [ntimes 100 {add 1 2} 1000]" puts " C vars: [ntimes 100 {add $a $b} 1000]" puts "" puts "Tcl expr: [ntimes 100 {expr {2*2*2}} 1000]" puts "Tcl vars: [ntimes 100 {expr {$b*$b*$b}} 1000]" puts "Tcl pow3: [ntimes 100 {pow3 2} 1000]" puts " C cube: [ntimes 100 {cube 2} 1000]" puts " C vars: [ntimes 100 {cube $b} 1000]" ---- Output (SuSE 7.1 Linux, PIII/650): ---- add 123 456 : 579 add 1 : wrong # args: should be "add x y" add 1 2 3 : wrong # args: should be "add x y" add 0 zero : expected integer but got "zero" Tcl noop: 0.01 microseconds per iteration C noop: 0.67 microseconds per iteration Tcl expr: 0.36 microseconds per iteration Tcl vars: 1.92 microseconds per iteration Tcl sum: 2.51 microseconds per iteration C add: 1.06 microseconds per iteration C vars: 2.66 microseconds per iteration Tcl expr: 0.7 microseconds per iteration Tcl vars: 2.83 microseconds per iteration Tcl pow3: 2.78 microseconds per iteration C cube: 0.96 microseconds per iteration C vars: 1.81 microseconds per iteration ---- [AK] Ideas * Replace the current blind usage of gcc with calls into a tcl based build system to make this code platform independent. * Make code in tcllib conditional on the availability of such a build system. I.e. Pure Tcl if there is no build system, and compiled code else. * With an omni-present build system we can do away with the restriction of not using C code / extensions in Tcllib. -- Later -- But the build system is not omnipresent, unfortunately. So scratch this idea. * [deployment] of C extensions in wrapped application becomes easy. Code is deployed in source and compiled and cached on site. But relies on presence of a compiler on the site. -- Later -- Which makes this a not so good idea after all. * Alternative is to compile before deployment and use the existing methods to load the code dynamically. * The ideas above require that Build system and CriTcl are distributed along with the core itself, like http, dde, and reg. ---- I don't follow most of the AK ideas above. My impression was that the reason for the "no compiled code" rule in tcllib was because we did not want to require a compiler on the user's machine in order for the user to use all of tcllib. There's nothing here that provides a compiler, so I don't see how the nifty features provided by CriTcl change anything about whether or not to include C-coded packages in tcllib. '''DGP''' (Please read the above as "AK's ideas", not mine) I am not sure that extending CriTcl to create a build system of some sort would be my first goal. The model I have is a group of developers, all fed up with build issues (and with a standard C compiler within reach at all times), and the rest of the world (trying to never have to find out what a "compiler" is, let alone allow one on their machine). In this context, CriTcl is way cool for developers IMO, yet useless for the majority of people on this planet. For deployment, I always use TclKit. Once CriTcl gains the capability to build all C packages into either a shared or a static lib, it will be helpful for any developer with the same type of machine as their clients to press a button, wrap all C code up, package the app into some standalone system (not necessarily TclKit of course), and never look back. From where I stand, the best delivery mode '''to''' developers is source code (unless it's so standard that a binary is more convenient and is supplied by a trusted source), while the best deployment mechanism to reach everyone else is always: portable scripts + binaries, usually wrapped up in some way. Does CriTcl need a build system? I'm not convinced... - '''JCW''' [AK] Clarifications. * What Donal says is essentially correct and I have now added clarification to the items which were not so good ideas after all. * I am not sure how this thought came to be, but I did not advocate to use CriTcl to create a build system. What I did thought about was to take whatever build system will come out of TIP 34 and 59 and sucessors and let CriTcl use that instead of hardcoding the usage of gcc into it. ---- Has someone looked at http://root.cern.ch/root/Cint.html as a possible way to integrate C with Tcl without having to compile C... ---- [AK]: [Larry Virden] provided this pointer: What: Embedded C (EC) Where: Description: Allows you to include C code in your Tcl scripts, compiling and dynamically loading it on the fly. The code will also be cached so the next time you run the program, you don't wait for it to compile. Known to work on DEC OSF/1 V3.2 and SunOS 5.5. Not yet ported to Macintosh or Windows. Updated: 09/1996 Contact: (Grant Reaber) ---- [JH]: In order to have this work with ActiveTcl (Tcl with tcllib), I had to change the first few lines of the critcl.tcl code to: catch {package require md5} if {[llength [info commands ::md5::md5]]} { interp alias {} ::md5 {} ::md5::md5 } and it provided some very nice performance numbers on just the md5 stuff: (jeffh) 60 % time {md5 hello} 1000 801 microseconds per iteration (jeffh) 61 % time {md5c hello} 1000 4 microseconds per iteration ''The catch... would break if there was already an "md5" command, wouldn't it? In fact, while I think I understand why that code is written in the way it is, is there a way to do things simpler? - JCW'' Maybe the following is a solution: if {[catch md5]} { catch {package require md5} } if {[catch md5]} { interp alias {} ::md5 {} ::md5::md5 } It's still extremely messy and weak (brittle) IMO, and illustrates how we seem to be making it harder and harder to write readable, clean, solid code... ''':-(''' ---- A dated article [http://www.itworld.com/AppDev/710/swol-0818-regex/] has background information on interpreted C.