home *** CD-ROM | disk | FTP | other *** search
Wrap
<HTML> <HEAD> <TITLE>Tk2portableTk - how to make your B<Tk> source portable to other interpreted languages.</TITLE> <LINK REL="stylesheet" HREF="../../../Active.css" TYPE="text/css"> <LINK REV="made" HREF="mailto:"> </HEAD> <BODY> <TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=100%> <TR><TD CLASS=block VALIGN=MIDDLE WIDTH=100% BGCOLOR="#cccccc"> <STRONG><P CLASS=block> Tk2portableTk - how to make your B<Tk> source portable to other interpreted languages.</P></STRONG> </TD></TR> </TABLE> <A NAME="__index__"></A> <!-- INDEX BEGIN --> <UL> <LI><A HREF="#name">NAME</A></LI><LI><A HREF="#supportedplatforms">SUPPORTED PLATFORMS</A></LI> <LI><A HREF="#author">Author</A></LI> <LI><A HREF="#description">DESCRIPTION</A></LI> <LI><A HREF="#structure of ptk, porting your code">Structure of <STRONG>pTk</STRONG>, porting your code</A></LI> <LI><A HREF="#portabletk api"><STRONG>PortableTk</STRONG> API</A></LI> <UL> <LI><A HREF="#checking what you are running under">Checking what you are running under</A></LI> <LI><A HREF="#new types of configuration options">New types of configuration options</A></LI> <LI><A HREF="#language data">Language data</A></LI> <LI><A HREF="#conversion">Conversion</A></LI> <LI><A HREF="#callbacks">Callbacks</A></LI> <LI><A HREF="#setting variables">Setting variables</A></LI> <LI><A HREF="#language functions">Language functions</A></LI> <LI><A HREF="#translation of some tcl functions">Translation of some TCL functions</A></LI> </UL> <LI><A HREF="#translation back to tcl">Translation back to TCL</A></LI> <UL> <LI><A HREF="#new types of events ">New types of events ????</A></LI> </UL> <LI><A HREF="#checking for trouble">Checking for trouble</A></LI> <LI><A HREF="#additional api">Additional API</A></LI> <UL> <LI><A HREF="#listfactory"><CODE>ListFactory</CODE></A></LI> <LI><A HREF="#dstrings">DStrings</A></LI> <LI><A HREF="#accessing args">Accessing <CODE>Arg</CODE>s</A></LI> <LI><A HREF="#assigning numbers to args">Assigning numbers to <CODE>Arg</CODE>s</A></LI> <LI><A HREF="#creating new args">Creating new <CODE>Arg</CODE>s</A></LI> <LI><A HREF="#evaluating a list">Evaluating a list</A></LI> <LI><A HREF="#getting result as arg">Getting result as <CODE>Arg</CODE></A></LI> </UL> </UL> <!-- INDEX END --> <HR> <P> <H1><A NAME="name">NAME</A></H1> <P>Tk2portableTk - how to make your <STRONG>Tk</STRONG> source portable to other interpreted languages.</P> <P> <HR> <H1><A NAME="supportedplatforms">SUPPORTED PLATFORMS</A></H1> <UL> <LI>Linux</LI> <LI>Solaris</LI> <LI>Windows</LI> </UL> <HR> <H1><A NAME="author">Author</A></H1> <P>Ilya Zakharevich <<A HREF="mailto:ilya@math.ohio-state.edu">ilya@math.ohio-state.edu</A>> has contributed most of this document. Many thanks.</P> <P> <HR> <H1><A NAME="description">DESCRIPTION</A></H1> <P><STRONG>PortableTk</STRONG> is an attempt to make <STRONG>Tk</STRONG> useful from other languages. Currently tk4.0 runs under Perl using this approach. Below, <EM>Lang</EM> is the notation for an external language to which <STRONG>PortableTk</STRONG> glues <STRONG>Tk</STRONG> code.</P> <P>The main problem with using the code developed for <STRONG>TCL</STRONG> with different languages is the absence of data types: almost anything is <CODE>char*</CODE>. It makes automatic translation hopeless. However, if you <CODE>typedef</CODE> several new symbols to be <CODE>char*</CODE>, you can still use your code in <STRONG>TCL</STRONG>, <EM>and</EM> it will make the automatic translation possible.</P> <P>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 <STRONG>Tk</STRONG> command return different string values that mean ``error happened'', like <CODE>""</CODE>, <CODE>" "</CODE> or <CODE>"??"</CODE>. Other languages can be more flexible, so in <STRONG>portableTk</STRONG> you should inform the compiler that what you want to return means ``error'' (see <A HREF="#setting variables">Setting variables</A>).</P> <P>Currently <STRONG>PortableTk</STRONG> uses several different approachs to simplify translation: several <STRONG>TCL</STRONG> 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 <STRONG>TCL</STRONG> will be also provided. The end of this document provides a starting point for such a project.</P> <P> <HR> <H1><A NAME="structure of ptk, porting your code">Structure of <STRONG>pTk</STRONG>, porting your code</A></H1> <P><STRONG>pTk</STRONG>, that is a port of <STRONG>Tk</STRONG>, is very special with respect to porting of other code to <STRONG>portableTk</STRONG>. The problem is that currently there is very little hope to merge the modifications back into <STRONG>Tk</STRONG>, so a special strategy is needed to maintain this port. Do not use this strategy to port your own code.</P> <P><STRONG>pTk</STRONG> is produced from <STRONG>Tk</STRONG> via a two-step process: first, some manual editing (the result is in the subdirectory <CODE>mTk</CODE>), and second, automatic conversion by the <CODE>munge</CODE> script (written in Perl). Thus the subdirectory <CODE>pTk/mTk</CODE> contains code with minimal possible difference from the virgin <STRONG>Tk</STRONG> code, so it is easier to <CODE>merge(1)</CODE> the differences between <STRONG>Tk</STRONG> versions into modified code.</P> <P>It looks like the strategy for a portable code should be exactly opposite: starting from <STRONG>TCL</STRONG>-based code, apply <CODE>munge</CODE>, and then hand-edit the resulting code. Probably it is also possible to target your code to <STRONG>portableTk</STRONG> from scratch, since this will make it possible to run it under a lot of <EM>Lang</EM>uages.</P> <P>The only reason anyone would like to look into contents of <CODE>pTk/mTk</CODE> directory is to find out which constructs are not supported by <CODE>munge</CODE>. On the other hand, <CODE>pTk</CODE> directory contains code that is conformant to <STRONG>portableTk</STRONG>, so you can look there to find example code.</P> <P><CODE>munge</CODE> is the script that converts most common <STRONG>Tk</STRONG> constructs to their <CODE>portableTk</CODE> equivalent. For your code to qualify, you should follow <STRONG>Tk</STRONG> conventions on indentation and names of variables, in particular, the array of arguments for the <CODE>...CmdProc</CODE> should be called <CODE>argv</CODE>.</P> <P>For details on what <CODE>munge</CODE> can do, see <A HREF="#translation of some tcl functions">Translation of some TCL functions</A>.</P> <P> <HR> <H1><A NAME="portabletk api"><STRONG>PortableTk</STRONG> API</A></H1> <P> <H2><A NAME="checking what you are running under">Checking what you are running under</A></H2> <P><STRONG>PortableTk</STRONG> provides a symbol <CODE>????</CODE>. If this symbol is defined, your source is compiled with it.</P> <P> <H2><A NAME="new types of configuration options">New types of configuration options</A></H2> <P><STRONG>PortableTk</STRONG> defines several new types of configuration options:</P> <PRE> TK_CONFIG_CALLBACK TK_CONFIG_LANGARG TK_CONFIG_SCALARVAR TK_CONFIG_HASHVAR TK_CONFIG_ARRAYVAR TK_CONFIG_IMAGE</PRE> <P>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.</P> <P>???? It looks like <CODE>TK_CONFIG_IMAGE</CODE> and <CODE>TK_CONFIG_SCALARVAR</CODE> set variables of type <CODE>char*</CODE>.</P> <P> <H2><A NAME="language data">Language data</A></H2> <P>The following data types are defined:</P> <DL> <DT><STRONG><A NAME="item_Arg"><CODE>Arg</CODE></A></STRONG><BR> <DD> is the main datatype of the language. This is a type that your C function gets pointers to for arguments when the corresponding <EM>Lang</EM> function is called. The corresponding config type is <CODE>TK_CONFIG_LANGARG</CODE>. <P>This is also a type that keeps information about contents of <EM>Lang</EM> variable.</P> <P></P> <DT><STRONG><A NAME="item_Var"><CODE>Var</CODE></A></STRONG><BR> <DD> Is a substitute for a <CODE>char *</CODE> that contains name of variable. In <EM>Lang</EM> it is an object that contains reference to another <EM>Lang</EM> variable. <P></P> <DT><STRONG><A NAME="item_LangResultSave"><CODE>LangResultSave</CODE></A></STRONG><BR> <DD> ???? <P></P> <DT><STRONG><A NAME="item_LangCallback"><CODE>LangCallback</CODE></A></STRONG><BR> <DD> <CODE>LangCallback*</CODE> a substitute for a <CODE>char *</CODE> that contains command to call. The corresponding config type is <CODE>TK_CONFIG_CALLBACK</CODE>. <P></P> <DT><STRONG><A NAME="item_LangFreeProc"><CODE>LangFreeProc</CODE></A></STRONG><BR> <DD> It is the type that the <CODE>Lang_SplitList</CODE> sets. Before you call it, declare <PRE> Args *args; LangFreeProc *freeProc = NULL; ... code = Lang_SplitList(interp, value, &argc, &args, &freeProc);</PRE> <P>After you use the split values, call</P> <PRE> if (args != NULL && freeProc) (*freeProc)(argc,args);</PRE> <P>It is not guaranteed that the <CODE>args</CODE> can survive deletion of <CODE>value</CODE>.</P> <P></P></DL> <P> <H2><A NAME="conversion">Conversion</A></H2> <P>The following macros and functions are used for conversion between strings and the additional types:</P> <PRE> LangCallback * LangMakeCallback(Arg) Arg LangCallbackArg(LangCallback *) char * LangString(Arg)</PRE> <P>After you use the result of LangCallbackArg(), you should free it with <CODE>freeProc</CODE> <CODE>LANG_DYNAMIC</CODE> (it is not guaranteed that any change of <A HREF="#item_Arg"><CODE>Arg</CODE></A> will not be reflected in <LangCallback>, so you cannot do LangSet...() in between, and you should reset it to <CODE>NULL</CODE> if you want to do any further assignments to this <A HREF="#item_Arg"><CODE>Arg</CODE></A>).</P> <P>The following function returns the <A HREF="#item_Arg"><CODE>Arg</CODE></A> that is a reference to <A HREF="#item_Var"><CODE>Var</CODE></A>:</P> <PRE> Arg LangVarArg(Var)</PRE> <P>???? It is very anti-intuitive, I hope the name is changed.</P> <PRE> int LangCmpCallback(LangCallback *a,Arg b)</PRE> <P>(currently only a stub), and, at last,</P> <PRE> LangCallback * LangCopyCallback(LangCallback *)</PRE> <P> <H2><A NAME="callbacks">Callbacks</A></H2> <P>Above we have seen the new datatype <A HREF="#item_LangCallback"><CODE>LangCallback</CODE></A> and the corresponding <EM>Config option</EM> <CODE>TK_CONFIG_CALLBACK</CODE>. The following functions are provided for manipulation of <A HREF="#item_LangCallback"><CODE>LangCallback</CODE></A>s:</P> <PRE> void LangFreeCallback(LangCallback *) int LangDoCallback(Tcl_Interp *,LangCallback *, int result,int argc, char *format,...)</PRE> <P>The argument <A HREF="../../../lib/Pod/perlfunc.html#item_format"><CODE>format</CODE></A> of <CODE>LangDoCallback</CODE> should contain a string that is suitable for <A HREF="#item_sprintf"><CODE>sprintf</CODE></A> with optional arguments of <CODE>LangDoCallback</CODE>. <A HREF="#item_result"><CODE>result</CODE></A> should be false if result of callback is not needed.</P> <PRE> int LangMethodCall(Tcl_Interp *,Arg,char *method, int result,int argc,...)</PRE> <P>????</P> <P>Conceptually, <CODE>LangCallback*</CODE> is a substitute for ubiquitous <CODE>char *</CODE> in <STRONG>TCL</STRONG>. So you should use <CODE>LangFreeCallback</CODE> instead of <CODE>ckfree</CODE> or <CODE>free</CODE> if appropriate.</P> <P> <H2><A NAME="setting variables">Setting variables</A></H2> <PRE> 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)</PRE> <P>These two are equivalent unless s is an empty string. In this case <CODE>LangSetDefault</CODE> behaves like <CODE>LangSetString</CODE> with <CODE>s==NULL</CODE>, i.e., it sets the current value of the <EM>Lang</EM> variable to be false.</P> <PRE> void LangSetInt(Arg *,int) void LangSetDouble(Arg *,double)</PRE> <P>The <EM>Lang</EM> functions separate uninitialized and initialized data comparing data with <CODE>NULL</CODE>. So the declaration for an <A HREF="#item_Arg"><CODE>Arg</CODE></A> should look like</P> <PRE> Arg arg = NULL;</PRE> <P>if you want to use this <CODE>arg</CODE> with the above functions. After you are done, you should use <CODE>LangFreeArg</CODE> with <CODE>TCL_DYNAMIC</CODE> as <CODE>freeProc</CODE>.</P> <P> <H2><A NAME="language functions">Language functions</A></H2> <P>Use</P> <DL> <DT><STRONG><A NAME="item_LangNull"><CODE>int LangNull(Arg)</CODE></A></STRONG><BR> <DD> to check that an object is false; <P></P> <DT><STRONG><A NAME="item_LangStringMatch"><CODE>int LangStringMatch(char *string, Arg match)</CODE></A></STRONG><BR> <DD> ???? <P></P> <DT><STRONG><A NAME="item_LangExit"><CODE>void LangExit(int)</CODE></A></STRONG><BR> <DD> to make a proper shutdown; <P></P> <DT><STRONG><A NAME="item_LangEval"><CODE>int LangEval(Tcl_Interp *interp, char *cmd, int global)</CODE></A></STRONG><BR> <DD> to call <EM>Lang</EM> <A HREF="../../../lib/Pod/perlfunc.html#item_eval"><CODE>eval</CODE></A>; <P></P> <DT><STRONG><A NAME="item_Lang_SetErrorCode"><CODE>void Lang_SetErrorCode(Tcl_Interp *interp,char *code)</CODE></A></STRONG><BR> <DD> <DT><STRONG><A NAME="item_Lang_GetErrorCode"><CODE>char *Lang_GetErrorCode(Tcl_Interp *interp)</CODE></A></STRONG><BR> <DD> <DT><STRONG><A NAME="item_Lang_GetErrorInfo"><CODE>char *Lang_GetErrorInfo(Tcl_Interp *interp)</CODE></A></STRONG><BR> <DD> <DT><STRONG><A NAME="item_LangCloseHandler"><CODE>void LangCloseHandler(Tcl_Interp *interp,Arg arg,FILE *f,Lang_FileCloseProc *proc)</CODE></A></STRONG><BR> <DD> currently stubs only; <P></P> <DT><STRONG><A NAME="item_LangSaveVar"><CODE>int LangSaveVar(Tcl_Interp *,Arg arg,Var *varPtr,int type)</CODE></A></STRONG><BR> <DD> to save the structure <CODE>arg</CODE> into <EM>Lang</EM> variable <CODE>*varPtr</CODE>; <P></P> <DT><STRONG><A NAME="item_LangFreeVar"><CODE>void LangFreeVar(Var var)</CODE></A></STRONG><BR> <DD> to free the result; <P></P> <DT><STRONG><A NAME="item_LangEventCallback"><CODE>int LangEventCallback(Tcl_Interp *,LangCallback *,XEvent *,KeySym)</CODE></A></STRONG><BR> <DD> ???? <P></P> <DT><STRONG><A NAME="item_LangEventHook"><CODE>int LangEventHook(int flags)</CODE></A></STRONG><BR> <DD> <DT><STRONG><A NAME="item_LangBadFile"><CODE>void LangBadFile(int fd)</CODE></A></STRONG><BR> <DD> <DT><STRONG><A NAME="item_LangCmpConfig"><CODE>int LangCmpConfig(char *spec, char *arg, size_t length)</CODE></A></STRONG><BR> <DD> unsupported????; <P></P> <DT><STRONG><A NAME="item_Tcl_AppendArg"><CODE>void Tcl_AppendArg (Tcl_Interp *interp, Arg)</CODE></A></STRONG><BR> <DD> </DL> <P>Another useful construction is</P> <PRE> Arg variable = LangFindVar(interp, Tk_Window tkwin, char *name);</PRE> <P>After using the above function, you should call</P> <PRE> LangFreeVar(Var variable);</PRE> <P>???? Note discrepancy in types!</P> <P>If you want to find the value of a variable (of type <A HREF="#item_Arg"><CODE>Arg</CODE></A>) given the variable name, use <CODE>Tcl_GetVar(interp, varName, flags)</CODE>. If you are interested in the string value of this variable, use <CODE>LangString(Tcl_GetVar(...))</CODE>.</P> <P>To get a <STRONG>C</STRONG> array of <A HREF="#item_Arg"><CODE>Arg</CODE></A> of length <CODE>n</CODE>, use</P> <PRE> Arg *args = LangAllocVec(n); ... LangFreeVec(n,args);</PRE> <P>You can set the values of the <A HREF="#item_Arg"><CODE>Arg</CODE></A>s using <CODE>LangSet...</CODE> functions, and get string value using <CODE>LangString</CODE>.</P> <P>If you want to merge an array of <A HREF="#item_Arg"><CODE>Arg</CODE></A>s into one <A HREF="#item_Arg"><CODE>Arg</CODE></A> (that will be an array variable), use</P> <PRE> result = Tcl_Merge(listLength, list);</PRE> <P> <H2><A NAME="translation of some tcl functions">Translation of some TCL functions</A></H2> <P>We mark items that can be dealt with by <CODE>munge</CODE> by <EM>Autoconverted</EM>.</P> <DL> <DT><STRONG><A NAME="item_Tcl_AppendResult"><CODE>Tcl_AppendResult</CODE></A></STRONG><BR> <DD> does not take <CODE>(char*)NULL</CODE>, but <CODE>NULL</CODE> as delimiter. <EM>Autoconverted</EM>. <P></P> <DT><STRONG><A NAME="item_Tcl_CreateCommand%2C_Tcl_DeleteCommand"><CODE>Tcl_CreateCommand</CODE>, <CODE>Tcl_DeleteCommand</CODE></A></STRONG><BR> <DD> <CODE>Tk_CreateWidget</CODE>, <CODE>Tk_DeleteWidget</CODE>, the second argument is the window itself, not the pathname. <EM>Autoconverted</EM>. <P></P> <DT><STRONG><A NAME="item_sprintf"><CODE>sprintf(interp->result, "%d %d %d %d",...)</CODE></A></STRONG><BR> <DD> <CODE>Tcl_IntResults(interp,4,0,...)</CODE>. <EM>Autoconverted</EM>. <P></P> <DT><STRONG><A NAME="item_result"><CODE>interp->result = "1";</CODE></A></STRONG><BR> <DD> <CODE>Tcl_SetResult(interp,"1", TCL_STATIC)</CODE>. <EM>Autoconverted</EM>. <P></P> <DT><STRONG>Reading <CODE>interp->result</CODE></STRONG><BR> <DD> <CODE>Tcl_GetResult(interp)</CODE>. <EM>Autoconverted</EM>. <P></P> <DT><STRONG><A NAME="item_Tk_PathName"><CODE>interp->result = Tk_PathName(textPtr->tkwin);</CODE></A></STRONG><BR> <DD> <CODE>Tk_WidgetResult(interp,textPtr->tkwin)</CODE>. <EM>Autoconverted</EM>. <P></P> <DT><STRONG><A NAME="item_Sequence_Tcl_PrintDouble%2C_Tcl_PrintDouble%2C_%2E">Sequence <CODE>Tcl_PrintDouble, Tcl_PrintDouble, ..., Tcl_AppendResult</CODE></A></STRONG><BR> <DD> Use a single command <PRE> void Tcl_DoubleResults(Tcl_Interp *interp, int append, int argc,...);</PRE> <P><CODE>append</CODE> governs whether it is required to clear the result first.</P> <P>A similar command for <A HREF="../../../lib/Pod/perlfunc.html#item_int"><CODE>int</CODE></A> arguments is <CODE>Tcl_IntResults</CODE>.</P> <P></P> <DT><STRONG><A NAME="item_Tcl_SplitList"><CODE>Tcl_SplitList</CODE></A></STRONG><BR> <DD> Use <CODE>Lang_SplitList</CODE> (see the description above). <P></P></DL> <P> <HR> <H1><A NAME="translation back to tcl">Translation back to TCL</A></H1> <P>To use your <STRONG>portableTk</STRONG> program with <STRONG>TCL</STRONG>, put</P> <PRE> #include "ptcl.h"</PRE> <P><EM>before</EM> inclusion of <CODE>tk.h</CODE>, and link the resulting code with <CODE>ptclGlue.c</CODE>.</P> <P>These files currently implement the following:</P> <DL> <DT><STRONG><A NAME="item_Additional_config_types%3A">Additional config types:</A></STRONG><BR> <DD> <PRE> TK_CONFIG_CALLBACK TK_CONFIG_LANGARG TK_CONFIG_SCALARVAR TK_CONFIG_HASHVAR TK_CONFIG_ARRAYVAR TK_CONFIG_IMAGE</PRE> <DT><STRONG><A NAME="item_Types%3A">Types:</A></STRONG><BR> <DD> <PRE> Var, Arg, LangCallback, LangFreeProc.</PRE> <DT><STRONG><A NAME="item_Functions_and_macros%3A">Functions and macros:</A></STRONG><BR> <DD> <PRE> 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.</PRE> </DL> <P>Current implementation contains enough to make it possible to compile <CODE>mTk/tkText*.[ch]</CODE> with the virgin <STRONG>Tk</STRONG>.</P> <P> <H2><A NAME="new types of events ">New types of events ????</A></H2> <P>PortableTk defines following new types of events:</P> <PRE> TK_EVENTTYPE_NONE TK_EVENTTYPE_STRING TK_EVENTTYPE_NUMBER TK_EVENTTYPE_WINDOW TK_EVENTTYPE_ATOM TK_EVENTTYPE_DISPLAY TK_EVENTTYPE_DATA</PRE> <P>and a function</P> <PRE> char * Tk_EventInfo(int letter, Tk_Window tkwin, XEvent *eventPtr, KeySym keySym, int *numPtr, int *isNum, int *type, int num_size, char *numStorage)</PRE> <P> <HR> <H1><A NAME="checking for trouble">Checking for trouble</A></H1> <P>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 <A HREF="#item_sprintf"><CODE>sprintf</CODE></A> and <CODE>sscanf</CODE> in your code (at least in the part that is working with interpreter).</P> <P> <HR> <H1><A NAME="additional api">Additional API</A></H1> <P>What is described here is not included into base <STRONG>portableTk</STRONG> distribution. Currently it is coded in <STRONG>TCL</STRONG> and as Perl macros (core is coded as functions, so theoretically you can use the same object files with different interpreted languages).</P> <P> <H2><A NAME="listfactory"><CODE>ListFactory</CODE></A></H2> <P>Dynamic arrays in <STRONG>TCL</STRONG> 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.</P> <P>The type for construction of dynamic lists is <CODE>ListFactory</CODE>. The API below is a counterpart of the API for construction of dynamic lists in <STRONG>TCL</STRONG>:</P> <PRE> 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 *)</PRE> <P>The difference is that a call to <CODE>ListFactoryFinish</CODE> should precede the actual usage of the value of <CODE>ListFactory</CODE>, and there are two different ways to append an <A HREF="#item_Arg"><CODE>Arg</CODE></A> to a <CODE>ListFactory</CODE>: <CODE>ListFactoryAppendCopy()</CODE> guarantees that the value of <CODE>arg</CODE> is copied to the list, but <CODE>ListFactoryAppend()</CODE> may append to the list a reference to the current value of <CODE>arg</CODE>. If you are not going to change the value of <CODE>arg</CODE> after appending, the call to ListFactoryAppend may be quicker.</P> <P>As in <STRONG>TCL</STRONG>, the call to <CODE>ListFactoryFree()</CODE> does not free the <CODE>ListFactory</CODE>, only the objects it references.</P> <P>The functions <CODE>ListFactoryNewLevel()</CODE> and <CODE>ListFactoryEndLevel()</CODE> return a pointer to a <CODE>ListFactory</CODE> to fill. The argument of <CODE>ListFactoryEndLevel()</CODE> cannot be used after a call to this function.</P> <P> <H2><A NAME="dstrings">DStrings</A></H2> <P>Production of strings are still supported in <STRONG>portableTk</STRONG>.</P> <P> <H2><A NAME="accessing args">Accessing <A HREF="#item_Arg"><CODE>Arg</CODE></A>s</A></H2> <P>The following functions for getting a value of an <A HREF="#item_Arg"><CODE>Arg</CODE></A> <EM>may</EM> be provided:</P> <PRE> double LangDouble(Arg) int LangInt(Arg) long LangLong(Arg) int LangIsList(Arg arg)</PRE> <P>The function <CODE>LangIsList()</CODE> is supported only partially under <STRONG>TCL</STRONG>, since there is no data types. It checks whether there is a space inside the string <CODE>arg</CODE>.</P> <P> <H2><A NAME="assigning numbers to args">Assigning numbers to <A HREF="#item_Arg"><CODE>Arg</CODE></A>s</A></H2> <P>While <CODE>LangSetDouble()</CODE> and <CODE>LangSetInt()</CODE> are supported ways to assign numbers to assign an integer value to a variable, for the sake of efficiency under <STRONG>TCL</STRONG> it is supposed that the destination of these commands was massaged before the call so it contains a long enough string to <A HREF="#item_sprintf"><CODE>sprintf()</CODE></A> the numbers inside it. If you are going to immediately use the resulting <A HREF="#item_Arg"><CODE>Arg</CODE></A>, the best way to do this is to declare a buffer in the beginning of a block by</P> <PRE> dArgBuffer;</PRE> <P>and assign this buffer to the <A HREF="#item_Arg"><CODE>Arg</CODE></A> by</P> <PRE> void LangSetDefaultBuffer(Arg *)</PRE> <P>You can also create the <CODE>buffer(s)</CODE> manually and assign them using</P> <PRE> void LangSetBuffer(Arg *, char *)</PRE> <P>This is the only choice if you need to assign numeric values to several <A HREF="#item_Arg"><CODE>Arg</CODE></A>s simultaneously. The advantage of the first approach is that the above declarations can be made <CODE>nop</CODE>s in different languages.</P> <P>Note that if you apply <CODE>LangSetDefaultBuffer</CODE> to an <A HREF="#item_Arg"><CODE>Arg</CODE></A> that contains some value, you can create a leak if you do not free that <A HREF="#item_Arg"><CODE>Arg</CODE></A> first. This is a non-problem in real languages, but can be a trouble in <CODE>TCL</CODE>, unless you use only the above API.</P> <P> <H2><A NAME="creating new args">Creating new <A HREF="#item_Arg"><CODE>Arg</CODE></A>s</A></H2> <P>The API for creating a new <A HREF="#item_Arg"><CODE>Arg</CODE></A> is</P> <PRE> void LangNewArg(Arg *, LangFreeProc *)</PRE> <P>The API for creating a new <A HREF="#item_Arg"><CODE>Arg</CODE></A> is absent. Just initialize <A HREF="#item_Arg"><CODE>Arg</CODE></A> to be <CODE>NULL</CODE>, and apply one of <CODE>LangSet...</CODE> methods.</P> <P>After you use this <A HREF="#item_Arg"><CODE>Arg</CODE></A>, it should be freed thusly:</P> <P><CODE>LangFreeArg(arg, freeProc)</CODE>.</P> <P> <H2><A NAME="evaluating a list">Evaluating a list</A></H2> <P>Use</P> <PRE> int LangArgEval(Tcl_Interp *, Arg arg)</PRE> <P>Here <CODE>arg</CODE> should be a list to evaluate, in particular, the first element should be a <A HREF="#item_LangCallback"><CODE>LangCallback</CODE></A> massaged to be an <A HREF="#item_Arg"><CODE>Arg</CODE></A>. The arguments can be send to the subroutine by reference or by value in different languages.</P> <P> <H2><A NAME="getting result as arg">Getting result as <A HREF="#item_Arg"><CODE>Arg</CODE></A></A></H2> <P>Use <CODE>Tcl_ArgResult</CODE>. It is not guaranteed that result survives this operation, so the <A HREF="#item_Arg"><CODE>Arg</CODE></A> you get should be the only mean to access the data from this moment on. After you use this <A HREF="#item_Arg"><CODE>Arg</CODE></A>, you should free it with <CODE>freeProc</CODE> <CODE>LANG_DYNAMIC</CODE> (you can do LangSet...() in between).</P> <TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=100%> <TR><TD CLASS=block VALIGN=MIDDLE WIDTH=100% BGCOLOR="#cccccc"> <STRONG><P CLASS=block> Tk2portableTk - how to make your B<Tk> source portable to other interpreted languages.</P></STRONG> </TD></TR> </TABLE> </BODY> </HTML>