![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
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 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
Translation of some TCL functions.
????
. If this symbol is defined, your source is compiled with it.
TK_CONFIG_CALLBACK TK_CONFIG_LANGARG TK_CONFIG_SCALARVAR TK_CONFIG_HASHVAR TK_CONFIG_ARRAYVAR TK_CONFIG_IMAGE
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*.
TK_CONFIG_LANGARG
.
This is also a type that keeps information about contents of Lang variable.
TK_CONFIG_CALLBACK
.
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
.
LangCallback * LangMakeCallback(Arg) Arg LangCallbackArg(LangCallback *) char * LangString(Arg)
After you use the result of LangCallbackArg(),
you should free
it with
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)
???? It is very anti-intuitive, I hope the name is changed.
int LangCmpCallback(LangCallback *a,Arg b)
(currently only a stub), and, at last,
LangCallback * LangCopyCallback(LangCallback *)
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 *,Arg,char *method, int result,int argc,...)
????
Conceptually, LangCallback* is a substitute for ubiquitous char *
in TCL. So you should use LangFreeCallback
instead of ckfree
or free
if appropriate.
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)
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(Arg *,int) void LangSetDouble(Arg *,double)
The Lang functions separate uninitialized and initialized data comparing data with NULL
. So the declaration for an Arg should look like
Arg 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
.
eval
;
arg
into Lang variable *varPtr
;
Arg 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 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);
You can set the values of the Args using LangSet...
functions, and get string value using LangString
.
If you want to merge an array of Args into one Arg (that will be an array variable), use
result = Tcl_Merge(listLength, list);
munge
by Autoconverted.
(char*)NULL
, but NULL
as delimiter. Autoconverted.
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.
Tcl_GetResult(interp)
. Autoconverted.
Tk_WidgetResult(interp,textPtr->tkwin)
. Autoconverted.
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
.
Lang_SplitList
(see the description above).
#include "ptcl.h"
before inclusion of 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.
mTk/tkText*.[ch]
with the virgin Tk.
TK_EVENTTYPE_NONE TK_EVENTTYPE_STRING TK_EVENTTYPE_NUMBER TK_EVENTTYPE_WINDOW TK_EVENTTYPE_ATOM TK_EVENTTYPE_DISPLAY TK_EVENTTYPE_DATA
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)
sprintf
and sscanf
in your code (at least in the part that is working with interpreter).
ListFactory
The type for construction of dynamic lists is ListFactory
. The API below is a counterpart of the API for construction of dynamic
lists in TCL:
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 *)
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 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 TCL, 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.
double LangDouble(Arg) int LangInt(Arg) long LangLong(Arg) int LangIsList(Arg arg)
The function LangIsList()
is supported only partially under TCL, since there is no data types. It checks whether there is a space inside
the string arg
.
LangSetDouble()
and LangSetInt()
are
supported ways to assign numbers to assign an integer value to a variable,
for the sake of efficiency under TCL 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;
and assign this buffer to the Arg by
void LangSetDefaultBuffer(Arg *)
You can also create the buffer(s)
manually and assign them
using
void LangSetBuffer(Arg *, char *)
This is the only choice if you need to assign numeric values to several Args 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 API.
void LangNewArg(Arg *, LangFreeProc *)
The API for creating a new 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)
.
int LangArgEval(Tcl_Interp *, Arg arg)
Here 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.
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).
$CommentsMailTo = "perl5@dcs.ed.ac.uk"; include("../syssies_footer.inc");?>