Quick Navigator

Search Site

Unix VPS
A - Starter
B - Basic
C - Preferred
D - Commercial
MPS - Dedicated
Previous VPSs
* Sign Up! *

Contact Us
Online Help
Domain Status
Man Pages

Virtual Servers

Topology Map

Server Agreement
Year 2038

USA Flag



Man Pages

Manual Reference Pages  -  TK::PTK (3)

.ds Aq ’


Tk2portableTk - how to make your Tk source portable to other interpreted languages.



Ilya Zakharevich <> has contributed most of this document. Many thanks.


<B>PortableTkB> is an attempt to make <B>TkB> useful from other languages. Currently tk4.0 runs under Perl using this approach. Below, Lang is the notation for an external language to which <B>PortableTkB> glues <B>TkB> code.

The main problem with using the code developed for <B>TCLB> with different languages is the absence of data types: almost anything is char*. It makes automatic translation hopeless. However, if you typedef several new symbols to be char*, you can still use your code in <B>TCLB>, and it will make the automatic translation possible.

Another problem with the approach that everything is a string is impossibility to have a result that says NotApplicable without setting an error. Thus different <B>TkB> command return different string values that mean error happened, like "", " " or "??". Other languages can be more flexible, so in <B>portableTkB> you should inform the compiler that what you want to return means error (see Setting variables).

Currently <B>PortableTkB> uses several different approachs to simplify translation: several <B>TCLB> functions that are especially dangerous to use are undefined, so you can easily find places that need to be updated to use Language-independent functions based on compiler warnings. Eventually a way to use these Language-independent functions under proper <B>TCLB> will be also provided. The end of this document provides a starting point for such a project.

Structure of <B>pTkB>, porting your code

<B>pTkB>, that is a port of <B>TkB>, is very special with respect to porting of other code to <B>portableTkB>. The problem is that currently there is very little hope to merge the modifications back into <B>TkB>, so a special strategy is needed to maintain this port. Do not use this strategy to port your own code.

<B>pTkB> is produced from <B>TkB> via a two-step process: first, some manual editing (the result is in the subdirectory mTk), and second, automatic conversion by the munge script (written in Perl). Thus the subdirectory pTk/mTk contains code with minimal possible difference from the virgin <B>TkB> code, so it is easier to merge(1) the differences between <B>TkB> versions into modified code.

It looks like the strategy for a portable code should be exactly opposite: starting from <B>TCLB>-based code, apply munge, and then hand-edit the resulting code. Probably it is also possible to target your code to <B>portableTkB> from scratch, since this will make it possible to run it under a lot of Languages.

The only reason anyone would like to look into contents of pTk/mTk directory is to find out which constructs are not supported by munge. On the other hand, pTk directory contains code that is conformant to <B>portableTkB>, so you can look there to find example code.

munge is the script that converts most common <B>TkB> constructs to their portableTk equivalent. For your code to qualify, you should follow <B>TkB> conventions on indentation and names of variables, in particular, the array of arguments for the ...CmdProc should be called argv.

For details on what munge can do, see Translation of some TCL functions.

<B>PortableTkB> API

    Checking what you are running under

<B>PortableTkB> provides a symbol ????. If this symbol is defined, your source is compiled with it.

    New types of configuration options

<B>PortableTkB> defines several new types of configuration options:


You should use them instead of TK_CONFIG_STRING whenever appropriate. This allows your application to receive a direct representation of the corresponding resource instead of the string representation, if this is possible under given language.

???? It looks like TK_CONFIG_IMAGE and TK_CONFIG_SCALARVAR set variables of type char*.

    Language data

The following data types are defined:
Tcl_Obj * is the main datatype of the language. This is a type that your C function gets pointers to for arguments when the corresponding Lang function is called. The corresponding config type is TK_CONFIG_LANGARG.

This is also a type that keeps information about contents of Lang variable.

Var Is a substitute for a char * that contains name of variable. In Lang it is an object that contains reference to another Lang variable.
LangResultSave ????
LangCallback LangCallback* a substitute for a char * that contains command to call. The corresponding config type is TK_CONFIG_CALLBACK.
LangFreeProc It is the type that the Lang_SplitList sets. Before you call it, declare

    Args *args;
    LangFreeProc *freeProc = NULL;
    code = Lang_SplitList(interp, value,
        &argc, &args, &freeProc);

After you use the split values, call

    if (args != NULL && freeProc) (*freeProc)(argc,args);

It is not guaranteed that the args can survive deletion of value.


The following macros and functions are used for conversion between strings and the additional types:

 LangCallback * LangMakeCallback(Tcl_Obj *)
 Tcl_Obj * LangCallbackArg(LangCallback *)
 char * LangString(Tcl_Obj *)

After you use the result of LangCallbackArg(), you should free it with freeProc LANG_DYNAMIC (it is not guaranteed that any change of Tcl_Obj * will not be reflected in <LangCallback>, so you cannot do LangSet...() in between, and you should reset it to NULL if you want to do any further assignments to this Tcl_Obj *).

The following function returns the Tcl_Obj * that is a reference to Var:

 Tcl_Obj * LangVarArg(Var)

???? It is very anti-intuitive, I hope the name is changed.

 int LangCmpCallback(LangCallback *a,Tcl_Obj * b)

(currently only a stub), and, at last,

 LangCallback * LangCopyCallback(LangCallback *)


Above we have seen the new datatype LangCallback and the corresponding Config option TK_CONFIG_CALLBACK. The following functions are provided for manipulation of LangCallbacks:

 void LangFreeCallback(LangCallback *)
 int LangDoCallback(Tcl_Interp *,LangCallback *,
        int result,int argc, char *format,...)

The argument format of LangDoCallback should contain a string that is suitable for sprintf with optional arguments of LangDoCallback. result should be false if result of callback is not needed.

 int LangMethodCall(Tcl_Interp *,Tcl_Obj *,char *method,
        int result,int argc,...)


Conceptually, LangCallback* is a substitute for ubiquitous char * in <B>TCLB>. So you should use LangFreeCallback instead of ckfree or free if appropriate.

    Setting variables

 void LangFreeArg (Tcl_Obj *, Tcl_FreeProc *freeProc)
 Tcl_Obj *  LangCopyArg (Tcl_Obj *);
 void Tcl_AppendArg (Tcl_Interp *interp, Tcl_Obj *)
 void LangSetString(Tcl_Obj * *, char *s)
 void LangSetDefault(Tcl_Obj * *, char *s)

These two are equivalent unless s is an empty string. In this case LangSetDefault behaves like LangSetString with s==NULL, i.e., it sets the current value of the Lang variable to be false.

 void LangSetInt(Tcl_Obj * *,int)
 void LangSetDouble(Tcl_Obj * *,double)

The Lang functions separate uninitialized and initialized data comparing data with NULL. So the declaration for an Tcl_Obj * should look like

 Tcl_Obj * arg = NULL;

if you want to use this arg with the above functions. After you are done, you should use LangFreeArg with TCL_DYNAMIC as freeProc.

    Language functions

int LangNull(Tcl_Obj *) to check that an object is false;
int LangStringMatch(char *string, Tcl_Obj * match) ????
void LangExit(int) to make a proper shutdown;
int LangEval(Tcl_Interp *interp, char *cmd, int global) to call Lang eval;
void Lang_SetErrorCode(Tcl_Interp *interp,char *code)
char *Lang_GetErrorCode(Tcl_Interp *interp)
char *Lang_GetErrorInfo(Tcl_Interp *interp)
void LangCloseHandler(Tcl_Interp *interp,Tcl_Obj * arg,FILE *f,Lang_FileCloseProc *proc) currently stubs only;
int LangSaveVar(Tcl_Interp *,Tcl_Obj * arg,Var *varPtr,int type) to save the structure arg into Lang variable *varPtr;
void LangFreeVar(Var var) to free the result;
int LangEventCallback(Tcl_Interp *,LangCallback *,XEvent *,KeySym) ????
int LangEventHook(int flags)
void LangBadFile(int fd)
int LangCmpConfig(char *spec, char *arg, size_t length) unsupported????;
void Tcl_AppendArg (Tcl_Interp *interp, Tcl_Obj *)
Another useful construction is

 Tcl_Obj * variable = LangFindVar(interp, Tk_Window tkwin, char *name);

After using the above function, you should call

 LangFreeVar(Var variable);

???? Note discrepancy in types!

If you want to find the value of a variable (of type Tcl_Obj *) given the variable name, use Tcl_GetVar(interp, varName, flags). If you are interested in the string value of this variable, use LangString(Tcl_GetVar(...)).

To get a <B>CB> array of Tcl_Obj * of length n, use

    Tcl_Obj * *args = LangAllocVec(n);

You can set the values of the Tcl_Obj *s using LangSet... functions, and get string value using LangString.

If you want to merge an array of Tcl_Obj *s into one Tcl_Obj * (that will be an array variable), use

    result = Tcl_Merge(listLength, list);

    Translation of some TCL functions

We mark items that can be dealt with by munge by Autoconverted.
Tcl_AppendResult does not take (char*)NULL, but NULL as delimiter. Autoconverted.
Tcl_CreateCommand, Tcl_DeleteCommand Tk_CreateWidget, Tk_DeleteWidget, the second argument is the window itself, not the pathname. Autoconverted.
sprintf(interp->result, ‘‘%d %d %d %d’’,...) Tcl_IntResults(interp,4,0,...). Autoconverted.
interp->result = ‘‘1’’; Tcl_SetResult(interp,"1", TCL_STATIC). Autoconverted.
Reading interp->result Tcl_GetResult(interp). Autoconverted.
interp->result = Tk_PathName(textPtr->tkwin); Tk_WidgetResult(interp,textPtr->tkwin). Autoconverted.
Sequence Tcl_PrintDouble, Tcl_PrintDouble, ..., Tcl_AppendResult Use a single command

 void Tcl_DoubleResults(Tcl_Interp *interp, int append,
        int argc,...);

append governs whether it is required to clear the result first.

A similar command for int arguments is Tcl_IntResults.

Tcl_SplitList Use Lang_SplitList (see the description above).

Translation back to TCL

To use your <B>portableTkB> program with <B>TCLB>, put

 #include "ptcl.h"

before inclusion of tk.h, and link the resulting code with ptclGlue.c.

These files currently implement the following:
Additional config types:



 Var, Tcl_Obj *, LangCallback, LangFreeProc.

Functions and macros:

 Lang_SplitList, LangString, LangSetString, LangSetDefault,
 LangSetInt, LangSetDouble Tcl_ArgResult, LangCallbackArg,
 LangSaveVar, LangFreeVar,
 LangFreeSplitProc, LangFreeArg, Tcl_DoubleResults, Tcl_IntResults,
 LangDoCallback, Tk_WidgetResult, Tcl_CreateCommand,
 Tcl_DeleteCommand, Tcl_GetResult.

Current implementation contains enough to make it possible to compile mTk/tkText*.[ch] with the virgin <B>TkB>.

    New types of events ????

PortableTk defines following new types of events:


and a function

 char * Tk_EventInfo(int letter,
            Tk_Window tkwin, XEvent *eventPtr,
            KeySym keySym, int *numPtr, int *isNum, int *type,
            int num_size, char *numStorage)

Checking for trouble

If you start with working TCL code, you can start convertion using the above hints. Good indication that you are doing is OK is absence of sprintf and sscanf in your code (at least in the part that is working with interpreter).

Additional API

What is described here is not included into base <B>portableTkB> distribution. Currently it is coded in <B>TCLB> and as Perl macros (core is coded as functions, so theoretically you can use the same object files with different interpreted languages).


Dynamic arrays in <B>TCLB> are used for two different purposes: to construct strings, and to construct lists. These two usages will have separate interfaces in other languages (since list is a different type from a string), so you should use a different interface in your code.

The type for construction of dynamic lists is ListFactory. The API below is a counterpart of the API for construction of dynamic lists in <B>TCLB>:

 void ListFactoryInit(ListFactory *)
 void ListFactoryFinish(ListFactory *)
 void ListFactoryFree(ListFactory *)
 Tcl_Obj * * ListFactoryArg(ListFactory *)
 void ListFactoryAppend(ListFactory *, Tcl_Obj * *arg)
 void ListFactoryAppendCopy(ListFactory *, Tcl_Obj * *arg)
 ListFactory * ListFactoryNewLevel(ListFactory *)
 ListFactory * ListFactoryEndLevel(ListFactory *)
 void ListFactoryResult(Tcl_Interp *, ListFactory *)

The difference is that a call to ListFactoryFinish should precede the actual usage of the value of ListFactory, and there are two different ways to append an Tcl_Obj * to a ListFactory: ListFactoryAppendCopy() guarantees that the value of arg is copied to the list, but ListFactoryAppend() may append to the list a reference to the current value of arg. If you are not going to change the value of arg after appending, the call to ListFactoryAppend may be quicker.

As in <B>TCLB>, the call to ListFactoryFree() does not free the ListFactory, only the objects it references.

The functions ListFactoryNewLevel() and ListFactoryEndLevel() return a pointer to a ListFactory to fill. The argument of ListFactoryEndLevel() cannot be used after a call to this function.


Production of strings are still supported in <B>portableTkB>.

Accessing CWTcl_Obj *s

The following functions for getting a value of an Tcl_Obj * may be provided:

 double LangDouble(Tcl_Obj *)
 int LangInt(Tcl_Obj *)
 long LangLong(Tcl_Obj *)
 int LangIsList(Tcl_Obj * arg)

The function LangIsList() is supported only partially under <B>TCLB>, since there is no data types. It checks whether there is a space inside the string arg.

Assigning numbers to CWTcl_Obj *s

While LangSetDouble() and LangSetInt() are supported ways to assign numbers to assign an integer value to a variable, for the sake of efficiency under <B>TCLB> it is supposed that the destination of these commands was massaged before the call so it contains a long enough string to sprintf() the numbers inside it. If you are going to immediately use the resulting Tcl_Obj *, the best way to do this is to declare a buffer in the beginning of a block by


and assign this buffer to the Tcl_Obj * by

   void LangSetDefaultBuffer(Tcl_Obj * *)

You can also create the buffer(s) manually and assign them using

   void LangSetBuffer(Tcl_Obj * *, char *)

This is the only choice if you need to assign numeric values to several Tcl_Obj *s simultaneously. The advantage of the first approach is that the above declarations can be made nops in different languages.

Note that if you apply LangSetDefaultBuffer to an Tcl_Obj * that contains some value, you can create a leak if you do not free that Tcl_Obj * first. This is a non-problem in real languages, but can be a trouble in TCL, unless you use only the above API.

Creating new CWTcl_Obj *s

The API for creating a new Tcl_Obj * is

 void LangNewArg(Tcl_Obj * *, LangFreeProc *)

The API for creating a new Tcl_Obj * is absent. Just initialize Tcl_Obj * to be NULL, and apply one of LangSet... methods.

After you use this Tcl_Obj *, it should be freed thusly:

LangFreeArg(arg, freeProc).

    Evaluating a list


 int LangArgEval(Tcl_Interp *, Tcl_Obj * arg)

Here arg should be a list to evaluate, in particular, the first element should be a LangCallback massaged to be an Tcl_Obj *. The arguments can be send to the subroutine by reference or by value in different languages.

Getting result as CWTcl_Obj *

Use Tcl_ArgResult. It is not guaranteed that result survives this operation, so the Tcl_Obj * you get should be the only mean to access the data from this moment on. After you use this Tcl_Obj *, you should free it with freeProc LANG_DYNAMIC (you can do LangSet...() in between).
Search for    or go to Top of page |  Section 3 |  Main Index

perl v5.20.3 PTK (3) 2013-11-15

Powered by GSP Visit the GSP FreeBSD Man Page Interface.
Output converted with manServer 1.07.