Now that using C is less daunting for the average Tcl programmer, "plug-in replacement by C code" becomes a valid strategy. But sometimes we can't use a compiled version on a given platform - e.g. if there is no compiler available on the platform, or if the C code uses features that are not portable. In this case it is important to be able to fall back to a pure-Tcl implementation - even if it is slower, or perhaps missing some features. The aim is to always end up with some working code, even if that code is nothing more than a clearly expressed failure explanation.
To see how this is done, let's look again at the first example, and extended the mymath example so that it will fall back to a Tcl implementation if a C compiler isn’t available, or if the compile fails for any reason:
package provide mymath 1.0 package require critcl
proc fallback {} { proc ::noop {} {} proc ::add {x y} { return [expr {$x + $y}] } proc ::cube {x} { return [expr {$x*$x*$x}] } if {[critcl::scripting]} { fallback } else { critcl::cproc noop {} void {} critcl::cproc add {int x int y} int { return x + y; } critcl::cproc cube {int x} int { return x * x * x; } if {[critcl::failed]} { fallback } }
The critcl::failed procedure can be called once within a Critcl script. It forces a compilation of the generated C code and returns true if the compilation (or link) fails. This is typically used at the end of a Critcl script to either fall back to Tcl code and/or to issue a warning message. Note that invoking critcl::failed stops any compiler errors being displayed (they can still be viewed in the Critcl log file under ~/.critcl/platform.
This fallback approach could be useful in projects such as Tcllib[11], which must be guaranteed to work on platforms without a C compiler but that contain modules which could benefit from the speed of a C implementation.