critcl_usingit man page on DragonFly

Man page or keyword search:  
man Server   44335 pages
apropos Keyword Search (all sections)
Output format
DragonFly logo
[printable version]

critcl_use(n)		   C Runtime In Tcl (CriTcl)		 critcl_use(n)

______________________________________________________________________________

NAME
       critcl_use - Using Critcl

DESCRIPTION
       Welcome	to the C Runtime In Tcl, CriTcl for short, a system to build C
       extension packages for Tcl on the fly, from C code embedded within  Tcl
       scripts, for all who wish to make their code go faster.

       This  document is a (hopefully) gentle introduction to Critcl by way of
       a series of small examples.

       Readers which came directly to this document through a search or	 simi‐
       lar,  and  are  thus  in	 need  of an overview of the whole system, are
       advised to read the Introduction To CriTcl first.

       The examples here cover both how to embed C into Tcl with it,  and  how
       to build the distributable packages.  As such the intended audience are
       both writers of packages with embedded  C,  and	people	building  such
       packages.   To  make  things  easier the two themes each have their own
       section in this document, enabling all readers to quickly skip the part
       they are not interested in.

       The  sources  of	 Critcl,  should you have gotten them, contain several
       larger examples show-casing various aspects of the system. These demon‐
       stration	 packages can all be found in the sub-directory "examples/" of
       the sources.

EMBEDDING C
       This is the section for developers writing,  or	wishing	 to  write,  a
       package embedding C into Tcl via critcl.

       I  guess	 that  we  are allowed to asssume that you, gentle reader, are
       here because you have written some Tcl code which is  not  fast	enough
       (any  more)  and you wish to make it "go faster" by replacing parts (or
       all) of it with speedy C.

       Another, and I believe reasonable assumption to make would be that  you
       have  already investigated and ruled out or done things like changes to
       data structures and algorithms which reduce O(n*n) work to O (n*log n),
       O(n), or even O(1).  Of course, nothing prevents you from forging ahead
       here even if you have not done such. Still, even in that case  I	 would
       recommend that you consider investigating this line of making your code
       go faster as well.

       Now, with these introductory words out of the way, lets jump  into  the
       meat of things.

   A SIMPLE PROCEDURE
       Starting	 simple,  let us assume that the Tcl code in question is some‐
       thing like

		  proc math {x y z} {
		      return [expr {(sin($x)*rand())/$y**log($z)}]
		  }

       with the expression pretending to be something very complex  and	 slow.
       Converting this to C we get:

		  package require critcl

		  critcl::cproc math {double x double y double z} double {
		      double up	  = rand () * sin (x);
		double down = pow(y, log (z);
		      return up/down;
		  }

       Notable about this translation:

       [1]    All the arguments got type information added to them, here "dou‐
	      ble".  Like in C the type precedes the argument name. Other than
	      that  it	is  pretty much a Tcl dictionary, with keys and values
	      swapped.

       [2]    We now also have to declare the type of the result,  here	 "dou‐
	      ble", again.

       [3]    The  reference  manpage lists all the legal C types supported as
	      arguments and results.

   HANDLING A VARIABLE NUMBER OF ARGUMENTS
       In A Simple Procedure we demonstrated how easy a translation to	C  can
       be.  Is it still as easy when we introduce something moderately complex
       like handling a variable number of  arguments  ?	 A  feature  which  is
       needed  to handle commands with options and optional arguments ? Unfor‐
       tunately not.  We can use critcl::cproc only if the number of arguments
       is  known  beforehand,  i.e. at the time of declaration. This of course
       also means that they do not support default arguments either.

       Thus, to handle something like the example below

		  proc math {args} {
		      set sum 0
		      foreach y { set sum [expr {$sum + $y}] }
		return $sum
		  }

       we have to use critcl::ccommand instead.

       Its advantage: Access to the low-level C arguments representing the Tcl
       arguments  of  the command.  That allows things like variable number of
       arguments, optional arguments, options, etc.

       Its disadvantage: Access to the low-level C arguments representing  the
       Tcl  arguments of the command.  Where critcl::cproc handles the task of
       converting from Tcl to C values	(for  arguments)  and  back  (for  the
       result), with critcl::commands we have to do this on our own.

       Here is the translation of the example:

		  package require critcl

		  critcl::ccommand math {cd ip oc ov} {
		      double sum = 0;
		      double y;

		      while (oc) {
			  if (Tcl_GetDoubleFromObj (ip, ov[oc], &y) != TCL_OK) {
			return TCL_ERROR;
			  }
			  sum += y;
		    oc --;
		      }

		      Tcl_SetObjResult (ip, Tcl_NewDoubleObj (sum));
		      return TCL_OK:
		  }

       Notable about this translation:

       [1]    As  promised/threatened, all the conversions between the Tcl and
	      C domains are exposed, and the developer	should	know  her  way
	      around Tcl's C API.

       [2]    The four arguments "cd ip oc ov" are our names for the low-level
	      arguments holding

	      [1]    ClientData (reference)

	      [2]    Tcl_Interp (reference)

	      [3]    Number of arguments, and

	      [4]    Array of argument values, each a Tcl_Obj*.

	      This list of arguments, while not optional in itself, is allowed
	      to  be empty, and/or to contain empty strings as argument names.
	      If we do that critcl will supply standard names for the  missing
	      pieces, namely:

	      [1]    clientdata

	      [2]    interp

	      [3]    objc

	      [4]    objv

   DATA AS A TCL COMMAND
       Here  we	 assume	 that  we  have	 a Tcl procedure which returns a fixed
       string. In the final product we are going to C to hide this string from
       the casual user.

		  proc somedata {} {
		return {... A large blob of characters ...}
		  }

       The translation of this is simple and easy:

		  package require critcl

		  critcl::cdata somedata {... A large blob of characters ...}

       There is nothing really notable here.

   BLOCKS OF ARBITRARY C
       Often  just defining Tcl commands in C, as demonstrated in the sections
       A Simple Procedure, Handling A Variable Number Of Arguments,  and  Data
       As A Tcl Command is not really enough.  For example we may have several
       of our new C commands using the same code over and over,	 and  we  wish
       avoid  this duplication. Or we wish to pull in declarations and defini‐
       tions from some external library.

       In both cases we require the ability to embed an unstructured block  of
       C  code	which  can  contain  whatever  we  want,  defines,  functions,
       includes, etc. without being directly tied to Tcl commands.   The  com‐
       mand critcl::code provides us with exactly that.	 As our example now an
       excerpt taken from real code, the top of the "sha1c.tcl" critcl file in
       the sha1 module of Tcllib:

		  critcl::ccode {
		      #include "sha1.h"
		      #include <stdlib.h>
		      #include <assert.h>

		      static
		      Tcl_ObjType sha1_type; /* fast internal access representation */

		      static void
		      sha1_free_rep(Tcl_Obj* obj)
		      {
			  SHA1_CTX* mp = (SHA1_CTX*) obj->internalRep.otherValuePtr;
			  Tcl_Free(mp);
		      }

		      ...
		  }

       We  see	here the beginning of the C code defining a custom Tcl_ObjType
       holding the data of a SHA1 context used during the incremental calcula‐
       tion of sha1 hashes.

   LIFTING CONSTANTS
       When  writing  a	 critcl-based  package	to  make a third-party library
       available to scripts we do not only have to make the relevant functions
       available  as commands, often we also have to know all the various con‐
       stants, flags, etc. these functions take.

       Rather than writing such magic numbers directly we would greatly prefer
       to  use	symbolic names instead.	 Instead of providing one or more com‐
       mands to list and map the magic numbers to strings critcl only provides
       a  single  command which allows the export of C defines and enumeration
       values, mapping them to Tcl variables of the given names, whose	values
       are the associated magic numbers.

       This  is	 good enough because the developers of the third-party library
       were very likely like us and wanted to use symbolic  names  instead  of
       magic  numbers.	Which in C are declared as via defines and enumeration
       types. We just have to lift them up.

       Our example comes from cryptkit, a Tcl binding to cryptlib, a  cryptog‐
       raphy library.  The command

		  critcl::cdefines CRYPT_* ::crypt

       maps  all  Cryptlib  specific  #defines	and  enums  into the namespace
       ::crypt, telling critcl to create aliases to the symbols.

       Similarly

		  critcl::cdefines {
		      NULL
		      TRUE
		      FALSE
		      TCL_OK
		      TCL_ERROR
		  } ::crypt

       maps the listed defines into the namespace ::crypt.

       An important thing to note: These commands do not create the defines in
       the C level. They only lift pre-existing material.  Which can come from
       the headers of the third-party library, the usual case, but  also  from
       Blocks of arbitrary C.

       A  corrollary  to  the  above: What is not where, cannot be lifted. All
       listed names and patterns which have no actual C	 code  declaring  them
       are ignored, i.e. not mapped.

   FINDING HEADER FILES
       A  notable  thing  in  the example shown in the section about Blocks of
       arbitrary C is the

		  #include "sha1.h"

       statement. Where does this header come from ?  Looking  at  the	Tcllib
       module  we  will	 find  that  the  header  is actually a sibling to the
       "sha1c.tcl" file containing the embedded C code.	 However, critcl  does
       not know that. It has to be told.  While without that knowledge it will
       invoke the compiler just fine, the compilation will  fail  because  the
       header  is not on the include paths used by the compiler, and therefore
       will not be found.

       For this we have the critcl::cheaders command. It enables us to	either
       tell  the compiler the path(s) where the required headers can be found,
       using

		  critcl::cheaders -I/path/to/headers/

       or to tell it directly which headers we are using and where they live:

		  critcl::cheaders sha1.h

       And now critcl knows that "sha1.h" is  important,  and  that  it	 lives
       besides the ".critcl" file which referenced it (because of the relative
       path used).   Note  that	 this  doesn't	absolve	 us  of	 the  need  to
       "#include"  the	header	through a critcl::ccode block. This only tells
       critcl where it lives so that it can configure the  compiler  with  the
       proper include paths to actually find it on use.

       Further	note that a C development environment is usually configured to
       find all the system headers, obviating the need for a  critcl::cheaders
       declaration  when  such	are  used.  For	 these a plain "#include" in a
       critcl::ccode block is good enough.  In other words, the second form of
       invoking	 critcl::cheaders is pretty much only for headers which accom‐
       pany the ".critcl" file.

   SEPARATE C SOURCES
       In all of the examples shown so far the C code was fully embedded in  a
       ".critcl"  file.	 However,  if the C part is large it can make sense to
       break it out of the ".critcl" file into one  or	more  separate	proper
       ".c" file(s).

       The  critcl::csources  command can then be used to make this code known
       to the original ".critcl" file again.  This command accepts  the	 paths
       to the ".c" files as arguments, and glob patterns as well.  Our example
       comes from the struct::graph package in Tcllib.	Its core  C  functions
       are  in	separate  files,  and the ".critcl" code then makes them known
       via:

	      namespace eval ::struct {
		  # Supporting code for the main command.
		  critcl::cheaders graph/*.h
		  critcl::csources graph/*.c

		  ...
	      }

       which tells critcl that these files are	in  the	 subdirectory  "graph"
       relative	 to  the  location  of	"graph_c.tcl",	which  is the relevant
       ".critcl" file.

       This example also demonstrates again the use of critcl::cheaders, which
       we also saw in section Finding header files.

   FINDING EXTERNAL LIBRARIES
       When  creating a package exposing some third-party library to Tcl Find‐
       ing header files is only the first part, to enable failure-free	compi‐
       lation.	We  also  have to find the library/ies themselves so that they
       can be linked to our package. This is described here. The  last	issue,
       Lifting	constants from C to Tcl for the use by scripts is handled in a
       separate section and example.

       The relevant command is critcl::clibraries.  Its	 basic	semantics  are
       like  that  of  critcl::cheaders, i.e. It enables us to tell the linker
       the path(s) where the required libraries can be found, using

		  critcl::clibraries -L/path/to/libraries/

       name them

		  critcl::clibraries -lfoo

       or tell it directly which libraries we are using and where they live:

		  critcl::clibraries /path/to/library/foo.so

       This last way of using should be avoided however,  as  it  intermingles
       searching and naming, plus the name is platform dependent.

       For  OS	X  we  additionally  have  the critcl::framework command which
       enables us to name the frameworks used by our package.  Note that  this
       command can be used unconditionally. If the build target is not OS X it
       is ignored.

   CUSTOMIZING THE COMPILE AND LINK STEPS
       The commands critcl::cflags and critcl::ldflags enable you  to  provide
       custom options to the compile and link phases for a ".critcl" file.

       This  usually becomes necessary if the C code in question comes from an
       external library we are writing a Tcl binding for, with	multiple  con‐
       figurations to select, non-standard header locations, and other things.
       Among the latter, especially platform-specific  settings,  for  example
       byteorder.

       This  makes  critcl::check an important adjunct command, as this is the
       API for Checking The Environment, and then selecting the compile & link
       flags to use.

       I currently have no specific example to demonstrate these commands.

   HAVING BOTH C AND TCL FUNCTIONALITY
       Often  enough  only  pieces of a package require recoding in C to boost
       the whole system. Or, alternatively, the package in  question  consists
       of  a  low-level	 layer	C with a Tcl layer above encoding policies and
       routing to the proper low-level calls, creating	a  nicer  (high-level)
       API to the low-level functionality, etc.

       For  all	 of  this we have to be able to write a package which contains
       both C and Tcl, nevermind the fact the C parts are embedded in Tcl.

       The easiest way to structure such a package is to have  several	files,
       each  with  a  different	 duty.	First, a ".critcl" file containing the
       embedded C, and second one or  more  ".tcl"  files  providing  the  Tcl
       parts.	Then use the critcl::tsources command in the ".critcl" file to
       link the two parts together, declaring the ".tcl"  files	 as  necessary
       companions of the C part.

		  package require critcl

		  critcl::tsources your-companion.tcl ; # Companion file to use

		  ... embedded C via critcl commands ...

       With  a declaration as shown above the companion file will be automati‐
       cally sourced when the C parts are made available, thus making the  Tcl
       parts available as well.

   USING C WITH TCL FUNCTIONALITY AS FALLBACK
       There  is one special case of Having both C and Tcl functionality which
       deserves its own section.

       The possibility of not having the fast C code  on  some	platform,  and
       using a slower Tcl implementation of the functionality. In other words,
       a fallback which keeps the package working in the face  of  failure  to
       build  the  C  parts. A more concrete example of this would be a module
       implementing the SHA hash, in both C and Tcl, and using the  latter  if
       and only if the C implementation is not available.

       There two major possibilities in handling such a situation.

       [1]    Keep  all	 the  pieces  separated. In that scenario our concrete
	      example would be spread over three packages. Two low-level pack‐
	      ages  sha::c  and sha::tcl containing the two implementations of
	      the algorithm, and, thirdly, a  coordinator  package  sha	 which
	      loads either of them, based on availability.

	      The  Tcllib  bundle  of  packages	 contains a number of packages
	      structured in this manner, mostly in the struct module.

	      Writing the C and Tcl parts should be simple by  now,  with  all
	      the  examples  we	 had  so far. The only non-trivial part is the
	      coordinator, and even that if and only if we  wish  to  make  it
	      easy  to write a testsuite which can check both branches, C, and
	      Tcl without gymnastics. So, the most basic coordinator would be

		  set sha::version 1
		  if {[catch {
		      package require sha::c $sha::version
		  }]} {
		      package require sha::tcl $sha::version
		  }
		  package provide sha $sha::version

	      It tries to load the C implementation first, and falls  back  to
	      the  Tcl	implementation	if  that fails. The code as is assumes
	      that both implementations create exactly the same command names,
	      leaving the caller unaware of the choice of implementations.

	      A	 concrete  example of this scheme can be found in Tcllib's md5
	      package. While it actually uses ythe Trf as its accelerator, and
	      not  a  critcl-based  package the principle is the same. It also
	      demonstrates the need for additional glue code when the C imple‐
	      mentation	 doesn't  exactly match the signature and semantics of
	      the Tcl implementation.

	      This basic coordinator can be easily extended to try  more  than
	      two  packages to get the needed implementation. for example, the
	      C implementation may not just exist in  a	 sha::c	 package,  but
	      also  bundled somewhere else. Tcllib, for example, has a tcllibc
	      package which bundles all the C parts of its packages which have
	      them in a single binary.

	      Another  direction  to  take it in is to write code which allows
	      the loading of multiple implementations at the  same  time,  and
	      then  switching  between	them  at  runtime. Doing this requires
	      effort to keep the implementations out of each others way,  i.e.
	      they  cannot  provide the same command names anymore, and a more
	      complex coordinator as well, which is able to map from the  pub‐
	      lic command names to whatever is provided by the implementation.

	      The  main benefit of this extension is that it makes testing the
	      two different implementations easier,  simply  run  through  the
	      same  set	 of  tests  multiple  times,  each time with different
	      implementation active. The disadvantage is the  additional  com‐
	      plexity  of  the coordinator's internals. As a larger example of
	      this    technique	   here	   is	 the	 coordinator	 "mod‐
	      ules/struct/queue.tcl" handling the C and Tcl implementations of
	      Tcllib's struct::queue package:

		  # queue.tcl --
		  #	  Implementation of a queue data structure for Tcl.

		  package require Tcl 8.4
		  namespace eval ::struct::queue {}

		  ## Management of queue implementations.

		  # ::struct::queue::LoadAccelerator --
		  #	  Loads a named implementation, if possible.

		  proc ::struct::queue::LoadAccelerator {key} {
		      variable accel
		      set r 0
		      switch -exact -- $key {
			  critcl {
			      # Critcl implementation of queue requires Tcl 8.4.
			      if {![package vsatisfies [package provide Tcl] 8.4]} {return 0}
			      if {[catch {package require tcllibc}]} {return 0}
			      set r [llength [info commands ::struct::queue_critcl]]
			  }
			  tcl {
			      variable selfdir
			      if {
				  [package vsatisfies [package provide Tcl] 8.5] &&
				  ![catch {package require TclOO}]
			      } {
				  source [file join $selfdir queue_oo.tcl]
			      } else {
				  source [file join $selfdir queue_tcl.tcl]
			      }
			      set r 1
			  }
			  default {
			      return -code error "invalid accelerator/impl. package $key: must be one of [join [KnownImplementations] {, }]"
			  }
		      }
		      set accel($key) $r
		      return $r
		  }

		  # ::struct::queue::SwitchTo --
		  #	  Activates a loaded named implementation.

		  proc ::struct::queue::SwitchTo {key} {
		      variable accel
		      variable loaded

		      if {[string equal $key $loaded]} {
			  # No change, nothing to do.
			  return
		      } elseif {![string equal $key ""]} {
			  # Validate the target implementation of the switch.

			  if {![info exists accel($key)]} {
			      return -code error "Unable to activate unknown implementation \"$key\""
			  } elseif {![info exists accel($key)] || !$accel($key)} {
			      return -code error "Unable to activate missing implementation \"$key\""
			  }
		      }

		      # Deactivate the previous implementation, if there was any.

		      if {![string equal $loaded ""]} {
			  rename ::struct::queue ::struct::queue_$loaded
		      }

		      # Activate the new implementation, if there is any.

		      if {![string equal $key ""]} {
			  rename ::struct::queue_$key ::struct::queue
		      }

		      # Remember the active implementation, for deactivation by future
		      # switches.

		      set loaded $key
		      return
		  }

		  # ::struct::queue::Implementations --
		  #	  Determines which implementations are
		  #	  present, i.e. loaded.

		  proc ::struct::queue::Implementations {} {
		      variable accel
		      set res {}
		      foreach n [array names accel] {
			  if {!$accel($n)} continue
			  lappend res $n
		      }
		      return $res
		  }

		  # ::struct::queue::KnownImplementations --
		  #	  Determines which implementations are known
		  #	  as possible implementations.

		  proc ::struct::queue::KnownImplementations {} {
		      return {critcl tcl}
		  }

		  proc ::struct::queue::Names {} {
		      return {
			  critcl {tcllibc based}
			  tcl	 {pure Tcl}
		      }
		  }

		  ## Initialization: Data structures.

		  namespace eval ::struct::queue {
		      variable	selfdir [file dirname [info script]]
		      variable	accel
		      array set accel	{tcl 0 critcl 0}
		      variable	loaded	{}
		  }

		  ## Initialization: Choose an implementation,
		  ## most preferred first. Loads only one of the
		  ## possible implementations. And activates it.

		  namespace eval ::struct::queue {
		      variable e
		      foreach e [KnownImplementations] {
			  if {[LoadAccelerator $e]} {
			      SwitchTo $e
			      break
			  }
		      }
		      unset e
		  }

		  ## Ready

		  namespace eval ::struct {
		      # Export the constructor command.
		      namespace export queue
		  }

		  package provide struct::queue 1.4.2

	      In this implementation the coordinator renames the  commands  of
	      the low-level packages to the public commands, making the future
	      dispatch as fast as if the commands had these names anyway,  but
	      also  forcing  a spike of bytecode recompilation if switching is
	      ever done at the runtime of an application, and  not  just  used
	      for  testing,  and possibly disrupting introspection by the com‐
	      mands, especially if they move between different namespaces.

	      A different implementation would be to provide the  public  com‐
	      mands  as procedures which consult a variable to determine which
	      of the loaded implementations is active, and then	 call  on  its
	      commands.	 This doesn't disrupt introspection, nor does it trig‐
	      ger bytecode recompilation on switching. But it takes more  time
	      to  dispatch  to the actual implementation, in every call of the
	      public API for the package in question.

	      A concrete example of this scheme can be found in Tcllib's crc32
	      package.

       [2]    Mix  the pieces together. Please note that while I am describing
	      how to make this work I strongly prefer and recommend to use the
	      previously  shown	 approach using separate files/packages. It is
	      much easier to understand and maintain. With this warning	 done,
	      lets go into the nuts and bolts.

	      If we care only about mode "compile & run" things are easy:

		  package require critcl

		  if {![critcl::compiling]} {
		      proc mycommand {...} {
			  ...
		      }

		  } else {
		      critcl::cproc mycommand {...} {
			  ...
		      }
		  }

	      The  command  critcl::compiling  tells us whether we have a com‐
	      piler available or not, and in the latter case we implement  our
	      command in Tcl.

	      Now  what	 happens when we invoke mode "generate package" ?  ...
	      compiler failure ...  ... ok   - C code -	 everything  fine  ...
	      fail - no package ? or just no C code ? declare self as tsource,
	      to be used ?  ... platform-specific C/Tcl -- uuid.

   UNLAZY PACKAGES
       By default critcl is a bit inconsistent between modes "compile  &  run"
       and  "generate  package".  The  result  of the latter is a standard Tcl
       package which loads and sources all of its files immediately when it is
       required.  Whereas  "compile & run" defers actual compilation, linking,
       and loading until the first time one of the declared commands is	 actu‐
       ally used, making this very lazy.

       This  behaviour	can be quite unwanted if Tcl companion files, or other
       users of the C commands use introspection  to  determine	 the  features
       they  have  available.  Just  using [info commands] doesn't cut it, the
       auto_index array has to be checked as well, making things quite	incon‐
       venient for the users.

       To  fix this issue at the source, instead of in each user, be it inside
       of  the	package	 itself,  or  other  packages,	we  have  the  command
       critcl::load.   Used  as the last command in a ".critcl" file it forces
       the compile, link, and load trinity, ensuring that all C	 commands  are
       available immediately.

		  package require critcl

		  ... Declare C procedures, commands, etc.

		  critcl::load ; # Force build and loading.

       Note that is not allowed, nor possible to use critcl commands declaring
       anything after critcl::load has been called. I.e., code like

		  package require critcl

		  ... Declare C procedures, commands, etc.

		  critcl::load ; # Force build and loading.

		  ... More declarations of C code, ...
		  critcl::code { ... }

       will result in  an  error.  The	only  package-related  commands	 still
       allowed are

       [1]    critcl::done

       [2]    critcl::failed

       [3]    critcl::load

       as  these only query information, namely the build status, and are pro‐
       tected against multiple calls.

CHECKING YOUR C
       As said several times, by default critcl defers the  compile  and  link
       steps  for  a  file  until  it is needed, i.e. the first command of the
       ".critcl" file in question is actually invoked.

       This not only has the effect of lazily loading the package's  function‐
       ality,  but also, when developing using mode "compile & run", of us not
       seeing any errors in our code until we are actually trying to run  some
       demonstration.

       If  we  do not wish to have such a delay we have to be able to force at
       least the execution of the compile step.

       The command critcl::failed is exactly that.  When  called  it  forcibly
       builds  the  C code for the ".critcl" file it is part of, and returns a
       boolean vlaue signaling failure (true), or success (false).

		  package require critcl

		  ... Declare C procedures, commands, etc.

		  if {[critcl::failed]} {
		      ... signal error
		  }

       It is related and similar to critcl::load, the command to overcome  the
       lazy loading, as shown in section Unlazy Packages.

       Like  it	 is not allowed, nor possible to use critcl commands declaring
       anything after critcl::failed has been called, making  it  pretty  much
       the last critcl command in a ".critcl" file.  Code like

		  package require critcl

		  ... Declare C procedures, commands, etc.

		  if {[critcl::failed]} { ... }

		  ... More declarations of C code, ...
		  critcl::code { ... }

       will  result  in	 an  error.  The  only	package-related commands still
       allowed are

       [1]    critcl::done

       [2]    critcl::failed

       [3]    critcl::load

       as these only query information, namely the build status, and are  pro‐
       tected against multiple calls.

   WHICH TCL ?
       When building the shared library from the embedded C sources one of the
       things critcl does for us is to provide the Tcl headers, especially the
       stubs declarations.

       By default these are the Tcl 8.4 headers and stubs, which covers 90% of
       the cases. What when the package in question is meant for use with  Tcl
       8.5 or higher, using C-level features of this version of Tcl.

       Use the critcl::tcl command to declare to critcl the minimum version of
       Tcl required to operate the package. This can be either	8.4,  8.5,  or
       8.6, and critcl then supplies the proper headers and stubs.

		  package require critcl
		  critcl::tcl 8.5

		  ... Declare your code ...

   MAKING A WIDGET
       ...  requires  compiling	 against the Tk headers, and linking with Tk's
       stubs. For our convenience we have a simple, single command to activate
       all the necessary machinery, with critcl supplying the header files and
       stubs C code, instead of	 having	 to  make  it  work  on	 our  own  via
       critcl::cflags, critcl::ldflags, critcl::cheaders, critcl::csources.

       This command is critcl::tk.

		  package require critcl
		  critcl::tk ; # And now critcl knows to put in the Tk headers and other support.

		  ... Declare your code ...

       Please  note that this doesn't release you from the necessity of learn‐
       ing Tk's C API and how to use it to make a widget work. Sorry.

   CHECKING THE ENVIRONMENT
       ... may be necessary  when  creating  a	binding	 to  some  third-party
       library.	 The  headers  for  this  library may be found in non-standard
       locations, ditto for the library/ies itself. We may not have the	 head‐
       ers  and/or  library  on	 the build host. Types with platform-dependent
       sizes and definitions. Endianness issues. Any number of things.

       TEA-based packages can use autoconf and various	predefined  macros  to
       deal with all this.  We have the Power Of Tcl (tm) and critcl::check.

       This  command  takes a piece of C code as argument, like critcl::ccode.
       Instead of saving it for later it however tries to compile  it  immedi‐
       ately,  using  the  current  settings, and then returns a boolean value
       reporting on the success (true) or failure (false). From there  we  can
       then branch to different declarations.

       As example let us check for the existence of some header "FOO.h":

		  package require critcl

		  if {[critcl::check {
		      #include <FOO.h>
		  }]} {
		      ... Code for when FOO.h is present.
		  } else {
		      ... Code for when FOO.h is not present.
		  }

       Should  we, on the other hand, wish to search for the header ourselves,
       in non-standard locations we have the full power of Tcl available, i.e.
       loops,  the file and glob commands, etc., which can then be followed by
       a critcl::cheader command to declare the location we  found  (See  also
       Finding header files).

       A  nice extension to critcl would be a package collecting pocedures for
       common tasks like that, sort of like an autoconf for Tcl.  critcl::con‐
       fig seems to be nice name for such a package.

       Obvious	 adjunct   commands  which  can	 be  driven  by	 results  from
       critcl::check are

       critcl::cflags

       critcl::cheaders

       critcl::clibraries

       critcl::framework

       critcl::ldflags

       Less obvious, yet still valid are also

       critcl::ccode

       critcl::ccommand

       critcl::cdata

       critcl::cproc

       critcl::csources

       critcl::ctsources

       and pretty much everything else you can imagine.

   LICENSE INVOKED
       When writing packages it is always good manners to provide  prospective
       users  with  the	 license the package is under, so that they can decide
       whether they truly want to use the package, or not.

       As critcl-based packages often consist of only a single file a nice way
       of  doing  that is to embed the license in that file. By using a critcl
       command, namely critcl::license this information is then also available
       to  the	critcl application, which can put it into a standard location,
       i.e. "license.terms", of the generated packages.

       I currently have no specific example to demonstrate the command.

BUILDING CRITCL PACKAGES
       This is the section for developers having  to  generate	packages  from
       ".critcl" files, i.e binaries for deployment,

   GETTING HELP ...
       ... Is easy. Running

		  critcl -help

       prints the basics of using the application to stdout.

   PRE-FILLING THE RESULT CACHE
       The  default  mode  of  the  critcl  application is to take a series of
       ".critcl" files, build their binaries, and leave	 them  behind  in  the
       result  cache.  When  the files are later actually used the compile and
       link steps can be skipped, leading to shorter load times.

       The command line for this is

		  critcl foo.tcl

       or, to process multiple files

		  critcl foo.tcl bar.tcl ...

       One thing to be aware of, should critcl find  that  the	cache  already
       contains	 the results for the input files, no building will be done. If
       you are sure that these results are outdated use the option  -force  to
       force(sic!) critcl to rebuild the binaries.

		  critcl -force foo.tcl

       For  debugging purposes it may be handy to see the generated intermedi‐
       ate ".c" files as well. Their removal from the cache can	 be  prevented
       by specifying the option -keep.

		  critcl -keep foo.tcl

       These can be combined, of course.

   BUILDING A PACKAGE
       To  build the binary package for a ".critcl" file, instead of Pre-Fill‐
       ing The Result Cache, simply specify the option -pkg.

		  critcl -pkg foo.tcl

       This will geneate a package named foo.  A simpler  alternative  to  the
       above is

		  critcl -pkg foo

       The  application	 will automatically assume that the input file to look
       for is "foo.tcl".

       But what when the name of the input file is not the name of the package
       to build ? This we can handle as well:

		  critcl -pkg foo bar.tcl

       The  argument  foo  specifies  the  name,  and "bar.tcl" is the file to
       process.

       Going back to the very first example, it is of course possible  to  use
       an absolute path to specify the file to process:

		  critcl -pkg /path/to/foo.tcl

       The package name derived from that is still foo.

   BUILDING AND INSTALLING A PACKAGE
       Here  we assume that you know the basics of how to build a package.  If
       not, please read section Building A Package first.

       By default critcl will place all newly-made packages in	the  subdirec‐
       tory "lib" of the current working directory.  I.e. running

		  critcl -pkg foo

       will create the directory "lib/foo" which contains all the files of the
       package.

       When this behaviour is unwanted the option -libdir is available, allow‐
       ing the explicit specification of the destination location to use.

		  critcl -pkg -libdir /path/to/packages foo

       A common use might be to not only build the package in question, but to
       also immediately install it directly in the path where the user's tclsh
       will  be	 able  to  find	 it.  Assuming, for example, that the tclsh in
       question	 is  installed	at  "/path/to/bin/tclsh",  with	 the  packages
       searched for under "/path/to/lib" ([info library]), the command

		  critcl -pkg -libdir /path/to/lib foo

       will    build   the   package   and   place   it	  in   the   directory
       "/path/to/lib/foo".

   BUILDING FOR DEBUGGING
       Here we assume that you know the basics of how to build a package.   If
       not, please read section Building A Package first.

       An  important  issue, when there is trouble with the package, debugging
       becomes necessary a evil.  Critcl  supports  this  through  the	-debug
       option.	Using it enables various build modes which help with that.

       For  example, to activate the Tcl core's built-in memory debugging sub‐
       system build your package with

		  critcl -pkg -debug memory foo

       The resulting binary for	 package  foo  will  use  Tcl's	 debug-enabled
       (de)allocation  functions, making them visible to Tcl's memory command.
       This of course assumes that the Tcl core used was also built for memory
       debugging.

       Further, built your package with

		  critcl -pkg -debug symbols foo

       to  see	the  foo's  symbols  (types, functions, variables, etc.)  when
       inspecting a "core" file it is involved in with a symbolic debugger,

       To activate both memory debugging and symbols use either

		  critcl -pkg -debug all foo

       or

		  critcl -pkg -debug symbols -debug memory foo

   RETARGETING THE BINARIES
       The configuration settings critcl uses to drive the  compiler,  linker,
       etc.  are  by  default  selected based on the platform it is run on, to
       generate binaries which properly work on this platform.

       There is one main use-case for overriding this selection, which is done
       with the option -target:

       [1]    Cross-compilation.  The  building	 of  binaries for a platform T
	      while critcl actually runs on platform B.	 The standard configu‐
	      ration  of  critcl currently has settings for two cross-compila‐
	      tion targets. So, to build 32bit Windows	binaries  on  a	 Linux
	      host which has the Xmingw cross-compilation development environ‐
	      ment installed use

		  critcl -pkg -target mingw32 foo

	      Similarly, building a package for use on	ARM  processors	 while
	      critcl is running in an Intel environment use

		  critcl -pkg -target linux-arm foo

	      Note  that  both	configurations assume that the cross-compiling
	      compiler, linke, etc. are found first in the PATH.

   CUSTOM CONFIGURATIONS
       The compiler configurations coming  with	 critcl	 currently  cover  all
       hosts  having  gcc installed (the foremost among these are Linux and OS
       X), plus the native compilers of the more  common  unix-like  operating
       systems,	 i.e. Solaris, HP-UX, and AIX, and, on the non-unix side, Win‐
       dows.

       Developers using operating systems and compilers outside of this	 range
       will  either  have to install a gcc-based development environment, i.e.
       get into the covered range, or write their own custom configuration and
       then tell critcl about it.

       The  latter  is	the easier part, given that critcl supports the option
       -config whose argument is the path to the file  containing  the	custom
       configuration(s). I.e.

		  critcl -config /path/to/config ...

       will  run  critcl  with	the custom configuration in "/path/to/config",
       with the other options and arguments as explained in previous sections.
       Depending  on  the choice of name for the new configuration(s) this may
       or may not require a -target option to select the configuration needed.

       For the former, the writing of the custom configuration, the reader  is
       refered	to the section "Configuration Internals" of the CriTcl Package
       Reference for the necessary details.  This is an advanced topic	pretty
       much out of scope for this tutorial beyond what was already said.

   CUSTOM HEADER PATH
       Sometimes  the use of critcl::headers might not be enough for a package
       to find its headers. Maybe they are outside of the paths checked by the
       setup  code.   To  help	the application recognizes the option -I which
       allows the user to supply a single additional include path to use  dur‐
       ing the build phase of the package.

       Simply use

		  critcl -I /path/to/header ...

       and the specified header will be handed to the package to be built.

   INTROSPECTION OF TARGETS AND CONFIGURATIONS
       To see a list containing the names of all the available configurations,
       run

		  critcl -targets

       The configuration settings for either the default or user-chosen target
       can be inspected on stdout with

		  critcl -show

       and

		  critcl -show -target TARGET

       The raw contents of the configuration file used by critcl are dumped to
       stdout with

		  critcl -showall

       All of the above can of course be combined  with	 custom	 configuration
       files.

AUTHORS
       Jean Claude Wippler, Steve Landers, Andreas Kupries

BUGS, IDEAS, FEEDBACK
       This  document,	and the package it describes, will undoubtedly contain
       bugs    and    other    problems.     Please	report	   them	    at
       https://github.com/andreas-kupries/critcl/issues.   Ideas  for enhance‐
       ments you may have for either package, application, and/or the documen‐
       tation	are   also   very   welcome   and   should   be	  reported  at
       https://github.com/andreas-kupries/critcl/issues as well.

KEYWORDS
       C code, Embedded C Code,	 code  generator,  compile  &  run,  compiler,
       dynamic code generation, dynamic compilation, generate package, linker,
       on demand compilation, on-the-fly compilation

CATEGORY
       Glueing/Embedded C code

COPYRIGHT
       Copyright (c) Jean-Claude Wippler
       Copyright (c) Steve Landers
       Copyright (c) 2011-2015 Andreas Kupries

doc				    3.1.15			 critcl_use(n)
[top]

List of man pages available for DragonFly

Copyright (c) for man pages and the logo by the respective OS vendor.

For those who want to learn more, the polarhome community provides shell access and support.

[legal] [privacy] [GNU] [policy] [cookies] [netiquette] [sponsors] [FAQ]
Tweet
Polarhome, production since 1999.
Member of Polarhome portal.
Based on Fawad Halim's script.
....................................................................
Vote for polarhome
Free Shell Accounts :: the biggest list on the net