Critcl

Difference between version 156 and 159 - Previous - Next
[Critcl Logo]

'''[http://andreas-kupries.github.io/critcl/%|%Critcl]''', or '''Compiled
Runtime In Tcl''', is a [Tcl] [package] that provides on-the-fly compilation
and execution of [C] code.



** Attributes **

   website:   https://andreas-kupries.github.io/critcl

   website (previous):   http://www.equi4.com/starkit/critcl.html

   download:   https://github.com/andreas-kupries/critcl/releases
   current release:   3.1.178.1
   release time:   201720-102-139



** Documentation **

   * [http://andreas-kupries.github.io/critcl/doc/files/critcl_pkg.html %|% Critcl - Package Reference]
   * [http://andreas-kupries.github.io/critcl/doc/toc.html %|% Table of Contents]
   * [http://andreas-kupries.github.io/critcl/doc/files/critcl_usingit.html %|% Using it / Examples]



** Installation **

   * [http://andreas-kupries.github.io/critcl/doc/files/critcl_installer.html %|% The Installer's Guide]


To create a starkit from `critcl.vfs`:

======none
$ ln -s critcl critcl.vfs
$ sdx wrap critcl.kit
======



** Development **

   * [https://github.com/andreas-kupries/critcl%|%Github repository]:   


previous repository : `svn co svn://svn.equi4.com/critcl/trunk critcl.vfs`



** See also **


   [Critcl builds C extensions on-the-fly]:   

   [Critcl Examples]:    

   [Extending Tcl]:   The main entry point for information on extending Tcl.

   [Scripted Code Generation]:   

   [http://phaseit.net/claird/comp.lang.tcl/HowToC.html%|%Cameron Laird's personal notes on how to use C with Tcl]:   dated material

   [Critcl does C++]:   

   [photo image equality in Critcl]:   How to speed up Tcl code 1200 times :^)

   [tcltcc]:   Partially emulates the Critcl API.

   [C.tcl]:   "A simplified modularized reduced variant of Critcl".



** Tools **

   [critlib]:    A rather dated set of tools that includes an older version of Critcl.



** References **

Tcl 2016 [http://www.tcl.tk/community/tcl2016/assets/talk35/critcl-paper.pdf%|%Critcl - Developments of the past decade] (Paper)

Tcl 2016 [http://www.tcl.tk/community/tcl2016/assets/talk35/critcl-slides.pdf%|%Critcl - Developments of the past decade] (Slides)

[Critcl Manual] (Old) :   A first cut at trying to pull a manual together.

   [http://www.digital-smarties.com/Tcl2002/critcl.pdf%|%CriTcl - Beyond Stubs and Compilers] ([http://equi4.com/papers/ctpaper1.html%|%html] [http://www.equi4.com/docs/vancouver/pres2.htm%|%slides]), [Steve Landers] and [Jean-Claude Wippler], [Ninth Annual Tcl/Tk Conference] 2002:   And the [http://www.equi4.com/papers/ctpaper1.html%|%slides]) from [JCW]'s presentation.

   [http://collaboration.cmc.ec.gc.ca/science/rpn/biblio/ddj/Website/articles/CUJ/2002/0212/laird/laird.htm%|%How to Use Tcl and C Together] (alternates [http://www.drdobbs.com/how-to-use-tcl-and-c-together/184401591%|%1 (dead)]), [Cameron Laird], 2002:   Presents Critcl Critcl as a profoundly important innovation.
   
   [http://www.ibm.com/developerworks/linux/library/l-sc10/index.html%|%Server clinic: Xmingwin for cross-generating apps], [Cameron Laird], 2003-01-30:   Critcl makes a brief appearance in this article about [XMingwin]



** News **

Oct 14, 2017: '''Critcl 3.1.17''' released.

[tjk] 2011-10-20  :   '''Critcl3''' released.



** Description **

'''Critcl''' requires an external compiler, but could also support [tcc].
Prior to using Critcl, verify that you have a working [C] compiler environment.

Jean-Claude was aware of [Perl]'s Inline
[[...]]
but not (?)
of [Python]'s
Weave
[[...]]
in his design of Critcl.



** Example: Basic **

======
#! /bin/env tclsh

lappend auto_path .
package require critcl
critcl::cproc triple {int i} int {
    return i * 3;    /* this is C code */
}
puts "three times 123 is [triple 123]"
======



** Example: `args` **

Since '''Critcl 3.1.16''' `critcl::cproc` can take ''default'' and ''args''-style arguments:

Arbitrary mixing of required and optional (default) arguments:

======
critcl::cproc optional_middle {double a double {b 1} double {c 2} double d} void {
        printf  ("M|%f|%f|%f|%f|\n",a,b,c,d);
        fflush(stdout);
}

# notice the missing 'b' and 'c' values
optional_middle 1.23 4.56

# -> M|1.230000|1.000000|2.000000|4.560000|

======

''Args''-style argument:

======
critcl::cproc math {double args} double {
    double sum = 0;
    args.c --;
    while (args.c >= 0) {
        sum += args.v[args.c];
        args.c --;
    }
    return sum;
}
======


** Example:  Package Generation **


The following example uses a [tclkit] to create a share-library extension.
Create a test script called "four.tcl" with the following contents:

======
package provide four 1.0
package require critcl
critcl::cproc quadruple {int i} int {
    return i * 4;    /* this is C code */
}
======

you can get on-line help by doing "tclkit critcl"

Turn it into a shared-library extension:

======none
$ cd /path/where/four.tcl/lives/
$ tclkit /path/to/critcl -pkg four.tcl
Source: four.tcl 
Library: four.so
Package: /path/to/lib/four/
======

Test it:

======
#! /bin/env tclsh
lappend auto_path lib
package require four
puts "four times 123 is [quadruple 123]"
======

The "four" extension is [TIP]
[http://www.tcl.tk/cgi-bin/tct/tip/55.html%|%55]-compliant and can be used with
any stub-enabled release of Tcl



** Generate [TEA] package **

'''Critcl version 3''' accepts an option '''-tea''' which generates a [TEA]-like directory hierarchy and contents:

`critcl -tea test.tcl`




** Example:  Import Functions From Other Packages **

`[ycl] chan`
[http://chiselapp.com/user/pooryorick/repository/ycl/artifact?ln=38-43&name=d8f433a9e7b88d6c%|%exports]
a [C] function called `filter()` that makes it easy to code up an accelerated
version of any command that reads from one channel and writes to another:

======
::critcl::api header clib.h
::critcl::api function void filter {
    Tcl_Interp *interp Tcl_Channel inchan Tcl_Channel
    outchan ycl_chan_process process
}
::critcl::load
======


For example, `[ycl] parser xml`
[http://chiselapp.com/user/pooryorick/repository/ycl/artifact?ln=62&name=9c7d366b299e6611%|%imports]
`filter()` in order to provide an accelerated version of
`encodeInvalidCharacters`:

======
package require {ycl chan clib}
package require critcl
::critcl::api import ycl_chan_clib 0.1
======



** Cross compiling using [Xmingwin] **

Critcl supports cross-compiling libraries and packages for Windows on
Linux/Unix using the Xmingwin cross-compiler (based on
[http://www.mingw.org%|%mingw]).


Install [Xmingwin installation%|%Xmingwin], and `$PATH` to include the Xmingwin
bin directory.  One convenient way of doing this is to create a script called
''cross'' (in /usr/local/bin or ~/bin).

======none
$ cat /usr/local/bin/cross
PATH=/usr/local/Xmingwin/i386-mingw32msvc/bin:$PATH
export PATH
exec $@
======

    * Then, you can compile using the usual Critcl package (or library) building commansd

======none
$ cross critcl -pkg four.tcl
Cross compiling for Windows using Xmingwin  
Source: four.tcl
Library: four.dll
Package: /path/to/four/lib/four
 Critcl recognizes a cross compile environment, it manipulates the tcl_platform array so that it matches that found on Windows 2000. Specifically, the following values are set
tcl_platform(byteOrder) = littleEndian
tcl_platform(machine)   = intel
tcl_platform(os)        = Windows NT
tcl_platform(osVersion) = 5.0
tcl_platform(platform)  = windows
tcl_platform(wordSize)  = 4
======

Critcl provides the `'''[critcl::sharedlibext]''', which returns the shared
library extension for the target platform. If you plan on cross-compiling you
should use this variable in your Critcl scripts instead of  `'''[info
sharedlibextension]'''` (although overlaying `'''[info sharedlibextension]'''`
will probably happen at some stage).

    * Intermediate files are stored in `'''~/.critcl/Windows-x86'''`
    * irrespective of the platform on which cross compiling occurs

For an example, download [http://www.tcl.tk/starkits/critex.tar.gz%|%critex],
unload and change to the `ex2` directory, which contains a blowfish extension
for Tcl. To build on Linux/Unix, run

======none
$ critcl -pkg blowfish
Source: blowfish.tcl
Library: blowfish.so
Package: /path/to/ex2/lib/blowfish
======

Then, to cross compile (via the above '''cross''' script) run

======none
$ cross critcl -pkg blowfish
Cross compiling for Windows using Xmingwin
Source: blowfish.tcl
Library: blowfish.dll
Package: /path/to/ex2/lib/blowfish
======

The contents of `ex2/lib/blowfish` are 

======
lib/blowfish
lib/blowfish/critcl.tcl
lib/blowfish/pkgIndex.tcl
lib/blowfish/Windows-x86
lib/blowfish/Windows-x86/critcl.tcl
lib/blowfish/Windows-x86/blowfish.dll
lib/blowfish/Linux-x86
lib/blowfish/Linux-x86/critcl.tcl
lib/blowfish/Linux-x86/blowfish.so
======

The `pkgIndex.tcl` will autoload the correct binary for a particular platform.



** critcl::cdefines **

[stevel] 2005-12-17

critcl::cdefines allows C #defines and enums to be mapped from C into a Tcl
namespace.

For example, [Cryptkit] uses the following to map Cryptlib symbols (i.e.
#defines and enums) into the cryptkit namespace

======
# map Cryptlib #defines and enums into the current namespace
critcl::cdefines CRYPT_* [namespace current]

# other defines
critcl::cdefines {
    NULL
    TRUE
    FALSE
    TCL_OK
    TCL_ERROR
} [namespace current]
======



** [namespace%|%namespaces] **

[PYK] 2017-09-29:  When `critcl::cproc` is used to create a command, Critcl
registers the the fully-qualified name of the command '''without the initialsemicolon characters''' using `[auto_index]`.  To ensure that the command is
loaded, do this:

======
::critcl::load
======

or this:

======
auto_load [string trimleft ::path::to::some::command :]
======



** Misc **

This discussion was migrated from [Scripted Compiler].

[AM]: Because of Critcl I am working on a package that abstracts the concept of
a compiler and a linker away from the platform-dependencies.  This way Critcl
will be able to support "any" compiler/linker without the user (or the Critcl
programmer) having to jump through hoops.

[Vince]: That sounds great!

[Victor]: Without linker ??  How then I use gcc produced *.o or archives ?
That is Critcl libs should take this place. Are they encrypted ?  And it is
still a long way to have it accepted by many people.

[NEM]: There is [Babel], by Paul Duffin. However, like Feather, it may take a
while to get hold of any code from Paul.

----

[UKo]: How can I inject code into the package initialization section?  This is
necessary to build new canvas commands or new sound subcommands for [Snack].

[AM]: There is a command ''cinit'' that allows you to do this.

[UKo]: It isn't in the critcl wikit, is it?  Where can I find an up-to-date
reference?  Or do I have to RTSL (Read the source Luke!)

[AM]: I can send you the (informal) documentation I wrote ... just drop me a
mail

----

[jcs] 2003-04-29: Here's a trick to make a file work both as Tcl script and as
C source:

======
#undef _ /* this is mixed-mode C and Tcl source code
package provide mypkg 1
package require critcl
critcl::ccode { /* C code follows: */

    #ifndef _TCL
    #include <stdio.h> /* etc ... */
    #endif

    /* C code here ... */

    #ifndef _TCL
    int main(int argc, char** argv) {
        /* standalone code here ... */
    }
    #endif

#define _ };#/* Tcl code follows:

critcl::cproc proc1 {int v} int {
    return cdef1(v);
}     
critcl::cproc proc2 {char* s} int {
    return cdef2(s);
}     

# vim: set ft=c: */
======

That last line also makes the "vi" editor colorize the file as C code, which is
presumably the bulk.

The idea is that you can embed large chunks of C in Tcl, with some trivial
cproc definitions at the end calling that code, while keeping the file in a
form which can also be used in non-Tcl environments, e.g. building code as Tcl
extension *and* as a plain C application.

The other way to do this is to use `critcl::csources somefile.c` and then to
store all C code there, but this stops Critcl from auto-detecting source
changes made to such an external file, so its automatic recompile won't kick
in.  With the above approach, you can edit at will and Critcl will compile
(only) when needed, while you get Tcl's context, say for running test suites.
The end result can then be used in non-Tcl contexts.

----

[wcf3]: Can someone provide an example for using critcl on a MacOSX system as
described in '''Building an extension for general use''' above? I have the
AquaBI runtime installed and it appears to only find critcl 0.0 instead of
version 0.33 that is part of the critcl.kit. The error message I get is that
command ''critcl::crosscheck'' is not found (it's part of version 0.33, not 0.0
:-)

More info: The AquaBI install included a set of example critcl files called
''critlib'' that required '''critcl 0.30'' and forced the loading of the older
version 0.0. By removing the ''critlib'' stuff from /Library/Tcl I am now able
to get the proper '''critcl 0.33''' loaded. The problem now is the include
files used by '''critcl''' are for X windows and not Aqua...duh, I should have
seen that one coming. Any ideas?

[CMcC]: Critcl needs to allow you to specify arbitrary compiler arguments,
which would enable you to use the -I command to select your preferred inclusion
places.  At the moment, this is not possible.

About the only thing I can suggest is using the critcl::cheaders command to
specify, individually, the files you want to include (and the files *they* want
to include, in some cases) in the correct order.

It's quite annoying.

[stevel] I regularly do things like

======
critcl::cheaders -I[pwd]
critcl::cheaders -L[pwd] -lmylib
======

[Lars H] 2006-02-08: Some info on the Critcl/Aqua issue can be found in
[http://web.archive.org/web/20070612062552/http://aspn.activestate.com/ASPN/Mail/Message/2375128%|%Re:
[MACTCL] Using critcl to build a Tk extension for Aqua], mactcl mailing list, 2004-08-12. The important part for simple uses appears to be the command

======
critcl::cheaders -DMAC_OSX_TK -I/Library/Frameworks/Tk.framework/Headers
======

(The -I option can probably be changed so that one doesn't have to specify an explicit path to the Tk framework, but I haven't tried that yet.)

----


[RLH]: 2009-04-11: Is Critcl going to be in teapot (for OSX)? I remember there
being a problem with that platform. I think...


[AK]: Critcl is not in the teapot, for no platform. Are you possibly confusing
critcl with tcllibc ? That is a shared library created from a number of critcl
based code found Tcllib and accelerating a few packages of it.

[stevel]: further to that, Critcl works just fine on OSX - it even generates
universal binaries by default. 

[AK]: whereas some of the critcl code in Tcllib indeed fails on OS X, for ... I
do not remember the reason. Possibly header trouble.

[RLH] Maybe my old brain is failing me.  :-)

----

----


'''[CMcC] - 2010-05-05 20:13:12'''

I have sought to modularize and simplify critcl, and have denuded of features
in a work in progress I call [C.tcl]

In part, this is an attempt to open critcl up to critical appraisal by
simplifying it and making it more transparent.

[AMG]: I look forward to the completion of this project.

----

[AMG]: Critcl has an interesting [gotcha] that will bite you when you divide
your code into multiple Tcl scripts that are `[source]`d into the same
program.  Critcl uses `[info script]` to determine which cprocs, etc. to
compile in the same translation unit (compiler invocation).  If you have cprocs
across multiple scripts that depend on being compiled together (e.g. they share
a common ccode definition), you will have to use [[info script $filename]] to
force Critcl to think all cprocs are in the same script.  Alternately, instead
of using `[source]`, put your cprocs in files external to your Tcl code, and
`[read]` them to get the arguments to cproc.  This has the dubious advantage
of letting you separately specify a C syntax highlighting mode for your cproc
files and a Tcl syntax highlighting mode for your Tcl files.  You can also
implement a custom preprocessor this way.

[AK]: There are actually two more [gotcha]s in critcl. The first is about the
differences between the ''dynamic'' and ''pre-compiled'' modes (my names, not
official) of critcl. For context, critcl can be used in two ways. For one you
load your Tcl code with the embedded C as usual, this internally runs the
compiler and loads the object files into the system. This is the ''dynamic''
mode. The other way is to run the Tcl with embedded C through the critcl
application, this generates a shared library for the C commands, which can be
loaded later. This is ''pre-compiled''. The difference is in the Tcl code
between the embedded C sections. The dynamic mode runs them as usual. The
shared library which is the result of the ''pre-compile'' step does not have
them. If these commands just initialize the namespace the C commands will end
up in we are fine. Anything beyond that however meas that the result of the
pre-compile step behaves differently than the code run dynamically, with
essential/expected setup missing.

The other gotcha is again in the interaction of pre-compiled results with the
regular critcl package handling dynamic execution. First, pre-compiled results
define a rudimentary critcl package which has all the commands empty. That
isn't so bad. Load the actual package and it will overwrite the empty pieces
with the proper definitions. Except that you are not able to load your proper
package. Because the rudimentary package actually also runs 'package provide
critcl 0', and this code is run when the pkgIndex.tcl file is read, i.e. this
fake critcl package gets defined just by loading any other package forcing a
search. At this point a 'package require critcl' either throws an error
(version conflict, want X, have 0), or does nothing (no specific version was
requested, the fake satisfies the request). If you now want to run some critcl
code dynamically you will get an error about missing commands, because the
critcl support commands like cproc/ccommand which we expected to define them
were of the fake and did nothing at all.

Looking at the fake critcl package I have no idea why it was made in the first
place. Well, partially to handle the package load, but for that we do not have
to declare the relevant commands to be a package. Just having them is good
enough. I was certainly able to fix my trouble by commenting out all the
'package provide critcl 0' commands in all the tcllibc versions I had around.
Tcllibc still loaded, and now the real critcl package was accessible too.



<<categories>> Critcl | Development