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''.
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*".
This is also a type that keeps information about contents of Lang variable.
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(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 *)
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 TCL. So you should use "LangFreeCallback" instead of "ckfree" or "free" if appropriate.
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".
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 C array of "Tcl_Obj *" of length "n", use
Tcl_Obj * *args = LangAllocVec(n); ... LangFreeVec(n,args);
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);
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".
#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, Tcl_Obj *, 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.
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)
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 *) 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 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(Tcl_Obj *) int LangInt(Tcl_Obj *) long LangLong(Tcl_Obj *) int LangIsList(Tcl_Obj * 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".
dArgBuffer;
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 "nop"s 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.
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:
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.