The main problem with using the code developed for TCL 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 TCL, 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 Tk command return different string
values that mean "error happened", like ""
, " "
or
"??"
. Other languages can be more flexible, so in portableTk you
should inform the compiler that what you want to return means "error"
(see the section on Setting variables).
Currently PortableTk uses several different approachs to simplify translation: several TCL 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 TCL will be also provided. The end of this document provides a starting point for such a project.
pTk is produced from Tk 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 Tk code, so it is easier to merge\|(1) the
differences between Tk versions into modified code.
It looks like the strategy for a portable code should be exactly
opposite: starting from TCL-based code, apply munge
, and then
hand-edit the resulting code. Probably it is also possible to target
your code to portableTk 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 portableTk, so you can look there to find example code.
munge
is the script that converts most common Tk constructs to
their portableTk
equivalent. For your code to qualify, you should
follow Tk 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
the section on Translation of some TCL functions.
Checking what you are running under
PortableTk provides a symbol ????
. If this symbol is defined,
your source is compiled with it.
New types of configuration options
PortableTk defines several new types of configuration options:
TK_CONFIG_CALLBACK TK_CONFIG_LANGARG TK_CONFIG_SCALARVAR TK_CONFIG_HASHVAR TK_CONFIG_ARRAYVAR TK_CONFIG_IMAGE
???? It looks like TK_CONFIG_IMAGE
and TK_CONFIG_SCALARVAR
set
variables of type char*
.
Language data
The following data types are defined:
Arg
TK_CONFIG_LANGARG
.
This is also a type that keeps information about contents of Lang
variable.
Var
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
Lang_SplitList
sets. Before you call it,
declare
Args *args; LangFreeProc *freeProc = NULL; ... code = Lang_SplitList(interp, value, &argc, &args, &freeProc);
if (args != NULL && freeProc) (*freeProc)(argc,args);
args
can survive deletion of value
.
Conversion
The following macros and functions are used for conversion between strings and the additional types:
LangCallback * LangMakeCallback(Arg) Arg LangCallbackArg(LangCallback *) char * LangString(Arg)
freeProc
LANG_DYNAMIC
(it is not guaranteed that any change of
Arg
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 Arg
).
The following function returns the Arg
that is a reference to Var
:
Arg LangVarArg(Var)
int LangCmpCallback(LangCallback *a,Arg b)
LangCallback * LangCopyCallback(LangCallback *)
Callbacks
Above we have seen the new datatype LangCallback
and the
corresponding Config option TK_CONFIG_CALLBACK
. The following
functions are provided for manipulation of LangCallback
s:
void LangFreeCallback(LangCallback *) int LangDoCallback(Tcl_Interp *,LangCallback *, int result,int argc, char *format,...)
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 *,Arg,char *method, int result,int argc,...)
Conceptually, LangCallback*
is a substitute for ubiquitous char *
in \s-1TCL\s0. So you should use LangFreeCallback
instead of ckfree
or free
if appropriate.
Setting variables
void LangFreeArg (Arg, Tcl_FreeProc *freeProc) Arg LangCopyArg (Arg); void Tcl_AppendArg (Tcl_Interp *interp, Arg) void LangSetString(Arg *, char *s) void LangSetDefault(Arg *, char *s)
LangSetDefault
behaves like LangSetString
with s==NULL
, i.e.,
it sets the current value of the Lang variable to be false.
void LangSetInt(Arg *,int) void LangSetDouble(Arg *,double)
NULL
. So the declaration for an Arg
should
look like
Arg arg = NULL;
arg
with the above functions. After you are
done, you should use LangFreeArg
with TCL_DYNAMIC
as freeProc
.
Language functions
Use
int LangNull(Arg)
int LangStringMatch(char *string, Arg match)
void LangExit(int)
int LangEval(Tcl_Interp *interp, char *cmd, int global)
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,Arg arg,FILE *f,Lang_FileCloseProc *proc)
int LangSaveVar(Tcl_Interp *,Arg arg,Var *varPtr,int type)
arg
into Lang variable *varPtr
;
void LangFreeVar(Var var)
int LangEventCallback(Tcl_Interp *,LangCallback *,XEvent *,KeySym)
int LangEventHook(int flags)
void LangBadFile(int fd)
int LangCmpConfig(char *spec, char *arg, size_t length)
void Tcl_AppendArg (Tcl_Interp *interp, Arg)
Another useful construction is
Arg variable = LangFindVar(interp, Tk_Window tkwin, char *name);
LangFreeVar(Var variable);
If you want to find the value of a variable (of type Arg
) 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 C array of Arg
of length n
, use
Arg *args = LangAllocVec(n); ... LangFreeVec(n,args);
Arg
s using LangSet...
functions,
and get string value using LangString
.
If you want to merge an array of Arg
s into one Arg
(that will
be an array variable), use
result = Tcl_Merge(listLength, list);
Translation of some \s-1TCL\s0 functions
We mark items that can be dealt with by munge
by Autoconverted.
Tcl_AppendResult
(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.
Tcl_IntResults(interp,4,0,...)
. Autoconverted.
Tcl_SetResult(interp,"1", TCL_STATIC)
. Autoconverted.
interp->result
Tcl_GetResult(interp)
. Autoconverted.
interp->result = Tk_PathName(textPtr->tkwin);
Tk_WidgetResult\|(interp,textPtr->tkwin). Autoconverted.
Tcl_PrintDouble, Tcl_PrintDouble, ..., Tcl_AppendResult
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
Lang_SplitList
(see the description above).
#include "ptcl.h"
tk.h
, and link the resulting code with
ptclGlue.c
.
These files currently implement the following:
TK_CONFIG_CALLBACK TK_CONFIG_LANGARG TK_CONFIG_SCALARVAR TK_CONFIG_HASHVAR TK_CONFIG_ARRAYVAR TK_CONFIG_IMAGE
Var, Arg, LangCallback, LangFreeProc.
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 Tk.
New types of events ????
PortableTk defines following new types of events:
TK_EVENTTYPE_NONE TK_EVENTTYPE_STRING TK_EVENTTYPE_NUMBER TK_EVENTTYPE_WINDOW TK_EVENTTYPE_ATOM TK_EVENTTYPE_DISPLAY TK_EVENTTYPE_DATA
char * Tk_EventInfo(int letter, Tk_Window tkwin, XEvent *eventPtr, KeySym keySym, int *numPtr, int *isNum, int *type, int num_size, char *numStorage)
sprintf
and sscanf
in your code (at least in the part that is
working with interpreter).
ListFactory
Dynamic arrays in \s-1TCL\s0 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 \s-1API\s0
below is a counterpart of the \s-1API\s0 for construction of dynamic lists
in \s-1TCL\s0:
void ListFactoryInit(ListFactory *) void ListFactoryFinish(ListFactory *) void ListFactoryFree(ListFactory *) Arg * ListFactoryArg(ListFactory *) void ListFactoryAppend(ListFactory *, Arg *arg) void ListFactoryAppendCopy(ListFactory *, Arg *arg) ListFactory * ListFactoryNewLevel(ListFactory *) ListFactory * ListFactoryEndLevel(ListFactory *) void ListFactoryResult(Tcl_Interp *, ListFactory *)
ListFactoryFinish
should precede the
actual usage of the value of ListFactory
, and there are two
different ways to append an Arg
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 \s-1TCL\s0, 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.
DStrings
Production of strings are still supported in portableTk.
Accessing Arg
s
The following functions for getting a value of an Arg
may be
provided:
double LangDouble(Arg) int LangInt(Arg) long LangLong(Arg) int LangIsList(Arg arg)
arg
.
Assigning numbers to Arg
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 \s-1TCL\s0 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 Arg
, the best way to do this is to
declare a buffer in the beginning of a block by
dArgBuffer;
Arg
by
void LangSetDefaultBuffer(Arg *)
void LangSetBuffer(Arg *, char *)
Arg
s simultaneously. The advantage of the first approach is
that the above declarations can be made nop
s in different languages.
Note that if you apply LangSetDefaultBuffer
to an Arg
that
contains some value, you can create a leak if you do not free that
Arg
first. This is a non-problem in real languages, but can be a
trouble in TCL
, unless you use only the above \s-1API\s0.
Creating new Arg
s
The \s-1API\s0 for creating a new Arg
is
void LangNewArg(Arg *, LangFreeProc *)
Arg
is absent. Just initialize Arg
to
be NULL
, and apply one of LangSet...
methods.
After you use this Arg
, it should be freed thusly:
LangFreeArg(arg, freeProc)
.
Evaluating a list
Use
int LangArgEval(Tcl_Interp *, Arg arg)
arg
should be a list to evaluate, in particular, the first
element should be a LangCallback
massaged to be an Arg
. The
arguments can be send to the subroutine by reference or by value in
different languages.
Getting result as Arg
Use Tcl_ArgResult
. It is not guaranteed that result survives this
operation, so the Arg
you get should be the only mean to access the
data from this moment on. After you use this Arg
, you should free
it with freeProc
LANG_DYNAMIC
(you can do LangSet...() in between).