Informatica Grafica
C A P I T U L O 23 Tcl y C

Originalmente el Tcl no se pensó para programar en lo que ahora se llama Tcl puro, Tcl nada más. Sino que se ideó como una herramienta para unir programas sencillos en C dando forma a una aplicación completa. Se consigue así, por una lado, la velocidad de ejecución propia de un programa compilado; y por otro la supuesta sencillez de Tcl/Tk para crear el interface de usuario.

Exec

La forma más sencilla de realizar la combinación ya se comentó en el capítulo 9: no hay más que realizar un programa en C de la forma habitual e invocarlo con exec en el momento oportuno. Por ejemplo, suponiendo que tenemos un programa para calcular un número aleatorio llamado azar:

Ejemplo 23-1.
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
void main (void)
{
srandom(time(NULL));
printf("%ld",random());
}

Para invocarlo desde Tcl no hay más que hacer:

set aleatorio [exec azar]

En casos más complejos se le pueden pasar argumentos de la misma forma que se haría desde DOS:

exec ejemplo $par1 $par2 $par3

Y en caso de necesidad se puede usar catch por si se produce algún error durante la ejecución:

catch "exec ejemplo $par1 $par2" resultado

Incluso se pueden redireccionar los streams de entrada, salida y error:

exec ejemplo <Fichero_entrada >&Fichero_salida_error


Open

Exec solamente sirve para casos sencillos, en los que no es necesario una comunicación continuada entre el script en Tcl y el programa en C (o C++, Pascal, ...). En caso de que sea necesaria, se puede usar el comando Tcl open con la opción "|" para ejecutar un programa y comunicarse con él usando la entrada y la salida estándar. El programa debe ser escrito de forma que realice todo el intercambio de datos a través de la salida y la entrada estándar con datos de tipo ASCII. Una vez que se haya comprobado que funciona correctamente desde DOS hay que escribir en Tcl script que nos de acceso al programa:

Ejemplo 23-2.
set fp [open "| ejemplo" r+]
fconfigure $fp -buffering line
puts $fp "datos u órdenes mandados al programa"
gets $fp cadena_recibida_desde_el _programa_C

Los comandos puts y gets pueden repetirse tantas veces como sea necesario. Una vez que hayamos acabado con el programa, le mandamos una orden para que se suicide sin dejar rastro y cerramos la tubería.


Implementación de comandos Tcl en C

Si bien para casos sencillos se pueden emplear los métodos ya comentados, a medida que se complica la parte correspondiente a la programación en C es mejor crear nuevos comandos Tcl cuya implementación se hace en C, y que podrán se usados igual que si fuesen comandos originales o procedimientos de Tcl.

 

La librería C/Tcl

La distribución de Tcl/Tk incluye una extensa librería de funciones que permiten realizar la implementación de en C de comandos Tcl. El uso de esta librería no es realmente complicado pero, al requerir el seguimiento de una serie de pasos bastante rígido, por parte del programador, puede parecerlo en un principio.

Hay dos formas de utilizar la librería:

Afortunadamente, la librería de que dispone Tcl/Tk es demasiado extensa como para poder comentar aquí todas las funciones que la componen; por ello sólo se comentarán las más importantes para poder realizar aplicaciones simples (y no tan simples), el resto se pueden encontrar el la ayuda de Tcl.


Compilación de un nuevo interprete

Para crear el nuevo intérprete es necesario seguir tres pasos:

De la inicialización se encargan las funciones:

Tcl_Main(int argc,char *argv[],
Tcl_AppInitProc *funcion)

o también:

Tk_Main(int argc,char *argv[],Tcl_AppInitProc *funcion)

según que el programa que vayamos a escribir necesite únicamente Tcl o también Tk. Los dos primeros parámetros son los mismos que los de la función main, mientras que el tercero es un puntero a una función definida por el usuario en la que se terminará la inicialización, a esta función se la suele llamar Tcl_AppInit, pero puede usarse cualquier nombre.

El prototipo de esta función es:

Int Tcl_AppInit(Tcl_interp *interprete)

Tcl_interp es una estructura definida en <tcl.h> que nos va a servir para pasar información a las funciones que implementan nuestros comandos y para recibir los resultados de ellas.

Lo primero que hay que hacer en esta función es llamar a las funciones Tcl_Init y Tk_Init que se encargan de inicializar el intérprete con los comandos originales de Tcl y Tk; en el caso de Tk_Init se encarga ,además, de crear la ventana principal de la aplicación.

Los prototipos de estas funciones:

int Tcl_Init(Tcl_interp *interprete)
int Tk_Init(Tcl_interp *interprete)

El parámetro que hay que pasarles es el mismo ‘interprete’ que recibió la función Tcl_AppInit. En cuanto al entero que devuelven estas funciones, puede tomar los valores:

Una vez que ya tenemos el interprete inicializado se pasa a la definición de los comandos que queramos implementar; para ello se usa la función Tcl_CreateCommand, cuyo prototipo es:

Tcl_CreateCommand(Tcl_interp *intérprete,
char NombreCmd,Tcl_Cmdproc * proc,
clientData clientData,
Tcl_CmdDeleteProc deleteProc)

Normalmente a estos dos últimos parámetros se les pasa NULL.

En cuanto a la implementación de las funciones, el prototipo de las funciones que lo hagan debe ser:

int funcion (ClientData clientdata,Tcl_interp *inteprete,
int argc,char *argv[])

los parámetros que se le pasan al comando Tcl definido vienen en argc y argv de la misma forma que en la función main de C, es decir, en argc el número de parámetros incluyendo el nombre del comando y cada elemento de la matriz argv contiene un puntero la cadena en la que está el parámetro correspondiente. La función devuelve un entero: TCL_ERROR o TCL_OK según haya habido o no un error.

En caso de que los parámetros pasados al comando no sean cadenas sino enteros o reales, se usan las siguientes funciones para hacer la transformación:

Int Tcl_GetInt(interp, string, parametro_entero)
int Tcl_GetDouble(interp, string, parametro_flotante)
int Tcl_GetBoolean(interp, string, parametro_booleano)

todas ellas devuelven TCL_OK si se pudo hacer la transformación, y TCL_ERROR en caso contrario.

Si alguno de los parámetros es una lista, se puede separar en su componentes por medio de la función:

int Tcl_SplitList(Tcl_interp *interprete,char *lista,
int *numero_elemento,
char ***puntero_a_elementos)

Para devolver valores al intérprete se usa Tcl_SetResult:

Tcl_SetResult(Tcl_Interp *interprete,
char *string,Tcl_freeProc freeProc)

En caso de que el valor a devolver sea una lista, se usan las funciones Tcl_AppendElement y Tcl_AppendResult.

Una vez que se ha completado la inicialización del programa, se puede invocar un script Tcl con lo que comenzará la ejecución del programa propiamente dicho; para ello se usa una sentencia del tipo:

Tcl_SetVar(interprete,"tcl_rcFileName","~/script",
TCL_GLOBAL_ONLY);

donde "~/" representa el directorio "hogar" del usuario y script es el fichero Tcl que va a ser invocado. En caso de que no se indique ninguno, el programa se ejecutará igual que lo hace wish. En Windows el directorio donde se encuentra el fichero se indicará: "c:/path_tcl/script".

Llegados a este punto, vamos a pasar al ejemplo. Vamos a crear un intérprete con los siguientes nuevos comandos:

En primer lugar, por ser más sencillo, se va a hacer para el caso de un sistema operativo de tipo UNIX y a continuación para Windows

Ejemplo 23-3. Unix
#include <stdio.h>
#include <tcl.h>
#include <tk.h>
/* Prototipos de funciones: */
int Tcl_AppInit(Tcl_Interp *interprete);
int hola (ClientData clientdata,Tcl_Interp *interprete,int argc,char *argv[]);
int media (ClientData clientdata,Tcl_Interp *interprete,int argc,char *argv[]);
/* Lo único que necesita hacer main es invocar a Tk_Main */
void main (int argc,char *argv[])
{
Tk_Main(argc,argv,Tcl_AppInit);
}
/* Tk_Main crea el intérprete antes de llamar al Tcl_AppInit y se lo pasa como parámetro. */
int Tcl_AppInit(Tcl_Interp *interprete)
{
/*Incializamos Tcl:*/
if(Tcl_Init(interprete)==TCL_ERROR)
{
fprintf(stderr,"Error en la inicialización de Tcl.\n");
return TCL_ERROR;
}
/*Inicializamos Tk: */
if(Tk_Init(interprete)==TCL_ERROR)
{
fprintf(stderr,"Error en la inicialización de Tk.\n");
return TCL_ERROR;
}
/* Definimos los nuevos comandos: */
Tcl_CreateCommand(interprete,"hola",hola,(ClientData)NULL,
(Tcl_CmdDeleteProc *)NULL);
Tcl_CreateCommand(interprete,"media",media,(ClientData)NULL,
(Tcl_CmdDeleteProc *)NULL);
/*Si quisieramos invocar un script de Tcl se haría aquí:
Tcl_SetVar(interprete,"tcl_rcFileName","~/prueba",TCL_GLOBAL_ONLY);
*/
return TCL_OK;
}
/* Con esto ya está terminada la inicialización, no hay más que implementar los comandos. */
int hola (ClientData clientdata,Tcl_Interp *interprete,int argc,char *argv[])
{
printf("\n\nˇHola Mundo!!!\n\n");
return TCL_OK;
}
int media (ClientData clientdata,Tcl_Interp *interprete,int argc,char *argv[])
{
int elementos;
double numero;
double suma;
int i;
char **valores;
char resultado[20];
/* Lo primero que hay que hacer es coger la lista con los datos que
se encuentra en argv[1] y separarla en su componentes: */
Tcl_SplitList(interprete,argv[1],&elementos,&valores);
for(suma=0,i=0;i<elementos;i++)
{
if(Tcl_GetDouble(interprete,valores[i],&numero)==TCL_ERROR)
{
Tcl_SetResult(interprete,"Error en los parámetros",TCL_STATIC);
return TCL_ERROR;
}
suma+=numero;
}
sprintf(resultado,"%f",suma/elementos);
Tcl_SetResult(interprete,resultado,TCL_VOLATILE);
return TCL_OK;
}

Una vez compilado el programa, más tarde se verá cómo, al ejecutarlo, aparentemente es como si estuviéramos usando el wish; todo es igual hasta que usamos los comandos hola y media {1 2 3 4 5}, incluso podemos usar comandos de Tk como button etc., es decir, tenemos un intérprete completo de Tcl/Tk más los comandos definidos por nosotros, un intérprete que podremos usar de la misma forma que wish, tanto interactivamente como en un script de Tcl, sin más que poner en la primera línea:

#!/path/ejemplo.

Con la distribución de Tcl se incluye un fichero llamado tclAppInit.c en el que se incluye un modelo de función Tcl_AppInit más complejo del aquí indicado que se puede usar como ejemplo.

Para el caso de Windows el programa queda:

Ejemplo 23-4. Windows
#include <tk.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#include <malloc.h>
#include <locale.h>
// Unos cuantos prototipos:
EXTERN void TkConsoleCreate(void);
EXTERN int TkConsoleInit(Tcl_Interp *interp);
static void setargv _ANSI_ARGS_((int *argcPtr, char ***argvPtr));
int hola (ClientData clientdata,Tcl_Interp *interprete,int argc,char *argv[]);
int media (ClientData clientdata,Tcl_Interp *interprete,int argc,char *argv[]);
// WinMain : Entrada al programa desde Windows.
int APIENTRY WinMain
{
char **argv, *p;
int argc;
char buffer[MAX_PATH];
setlocale(LC_ALL, "C");
// Se crean los canales para la consola y los instala como los estandard *
TkConsoleCreate();
// Pasa los parámetros de la forma normal Windows a la de DOS
setargv(&argc, &argv);
// Reemplaza argv[0] con el path completo del ejecutable.
GetModuleFileName(NULL, buffer, sizeof(buffer));
argv[0] = buffer;
for (p = buffer; *p != '\0'; p++)
{
if (*p == '\\')
{
*p = '/';
}
}
// Y por fin se invoca a Tk_Main
Tk_Main(argc,argv, Tcl_AppInit);
return 1;
}
// Tcl_AppInit: Inicialización del interprete
int Tcl_AppInit(Tcl_Interp *interp)
{
if (Tcl_Init(interp) == TCL_ERROR)
{
printf("Error en la inicialización de Tcl.\n");
return TCL_ERROR;
}
if (Tk_Init(interp) == TCL_ERROR)
{
printf("Error en la incialización de Tk.\n");
return TCL_ERROR;
}
// Se inicializa la consola:
if (TkConsoleInit(interp) == TCL_ERROR)
{
printf("Error en la inicialización de la consola.\n");
return TCL_ERROR;
}
Tcl_CreateCommand(interp,"hola",hola,(ClientData)NULL,
(Tcl_CmdDeleteProc *)NULL);
Tcl_CreateCommand(interp,"media",media,(ClientData)NULL,
(Tcl_CmdDeleteProc *)NULL);
return TCL_OK;
}
int hola
(ClientData clientdata,Tcl_Interp *interprete,int argc,char *argv[])
{
Tcl_SetResult(interprete,"\n\nˇHola Mundo!!!\n\n",TCL_STATIC)
return TCL_OK;
}

Al igual que para el caso de Unix, se puede encontrar un modelo para la función WinMain en la distribución de Tk, en el fichero winMain.c, pero no con la que viene precompilada, sino con la distribución completa sin compilar; en este fichero se encuentra también la función setargv que no se ha incluido en el ejemplo para ahorrar espacio. Asimismo, las funciones relativas a la consola se pueden encontrar en el fichero tkConsole.c que también se incluye con la distribución sin compilar; por último, la función media es la misma del ejemplo anterior.

Las funciones de la consola sólo son necesarias si se quiere usar esta, en caso contrario se quitan y ya está.

La estructura Tcl_interp contiene un campo llamado result que se usa para contener el resultado de comando; la longitud de este campo es originalmente de unos 200 bytes; se puede, en teoría, escribir el resultado directamente en él sin necesidad de usar la funciones de la familia Tcl_SetResult. Por ejemplo, en la función media se podría haber puesto:

sprintf(interprete->result,"%f",suma/elementos);

esta forma de especificar el resultado ya estaba desaconsejada en anteriores versiones de Tcl, a partir de la versión 8.0 , por motivos que veremos más adelante, "se desaprueba fuertemente".

Tcl_Eval

En el ejemplo del apartado anterior se muestra un "Hola mundo" que es claramente decepcionante para cualquier entorno gráfico que se precie; lo menos sería ponerlo en una ventana con un botón de aceptar y demás, algo así como:

Ejemplo 23-5. Tcl_Eval
set ven [toplevel .hola]
set marco [frame $ven.text -bd 2 -relief sunken]
set texto [text $marco.texto -font {Helvetica 20} -height 1 -width12]
$texto insert end "ˇˇHola Mundo!!"
button $ven.aceptar -text Aceptar -command "destroy $ven"
pack $marco.texto -padx 10 -pady 30
pack $marco -side top -fill x -padx 5 -pady 5
pack $ven.aceptar -side top -pady 5

Para conseguir este resultado sin necesidad de recurrir a la librería en C de Tk se usan las funciones de la familia Tcl_Eval. El prototipo del función Tcl_Eval es:

int Tcl_Eval(interprete, comando),

donde interprete es el que estemos usando y comando es la instrucción TclTk que queremos evaluar. Esta instrucción se puede usar en la función que implementa un comando sin que produzca ningún tipo de interferencia entre ambos comandos.

Existen varias funciones relacionadas con Tcl_Eval, como son Tcl_RecordAndEval que permite guardar el comando que se va a ejecutar de forma que pueda ser recuperado con el comando de Tcl history, Tcl_EvalFile que permite a su vez evaluar el contenido de un fichero; por ejemplo como el que contiene la ventana para saludar al mundo que acabamos de hacer. Para usarlo, modificamos la función hola del ejemplo 23-3

Ejemplo 23-6. Tcl_EvalFile
int hola
(ClientData clientdata,Tcl_Interp *interprete,int argc,char *argv[])
{
if(Tcl_EvalFile(interprete,"hola.tcl")==TCL_ERROR)
{
Tcl_SetResult
(interprete,"Error en la evaluación de fichero.",TCL_STATIC);
return TCL_ERROR;
}
return TCL_OK; }

Variables compartidas entre C y Tcl

En ocasiones puede ser interesante comprobar directamente el valor de una variable en Tcl desde la parte en C del programa o viceversa, incluso unir una variable en C y una en Tcl de forma que contengan siempre el mismo valor, es decir, que al cambiar una, la otra lo haga automáticamente. Por ejemplo, esto nos puede valer para tener una variable booleana que nos diga si estamos conectados a una base de datos o no.

Para ello tenemos las funciones:

char * Tcl_SetVar(interprete, nombre_variable, nuevo_valor, flags)
char * Tcl_SetVar2
(interprete, nombre_matriz, nombre_inidice, nuevo_valor, flags)

que fijan el valor de una variable, Tcl_SetVar2 se usa en caso de matrices, aunque también sirve Tcl_SetVar si el nombre de la variable incluye el índice entre paréntesis. En el ejemplo 23-3 se usa Tcl_SetVar para que Tcl supiera qué script utilizar al inicializarse.

Análogamente, las siguientes funciones devuelven el valor de una variable Tcl:

char * Tcl_GetVar(interprete,nombre_variable, flags)
char * Tcl_GetVar2(interprete,nombre_matriz,nombre_indice, flags)

Mientras que estas eliminan variables:

int Tcl_UnsetVar(interprete, nombre_variable, flags)
int Tcl_UnsetVar2(interprete, nombre_matriz, nombre_indice, flags)

Por último, las siguientes funciones se usan para unir variable Tcl y C:

int Tcl_LinkVar(interprete,Variable_Tcl direccion_variable_C, tipo)
Tcl_UnlinkVar(interprete,Variable_Tcl)
Tcl_UpdateLinkedVar(interprete, Variable_Tcl)


Compilación de los programas

Lógicamente el método de compilación dependerá de la plataforma y el compilador elegido.

En Unix habrá que usar un fichero make del estilo del que se muestra en el ejemplo 23-6

Ejemplo 23-7.
TCL_LIB=/usr/lib/
TCL_INCLUDE=/usr/include/
programa_tcl:
gcc -L${TCL_LIB} -I{TCL_INCLUDE} programa_tcl.c -o programa_tcl -ltcl8.0 -ltk8.0
/usr/lib/libX11.so -lm

claro que sólo funcionará si los directorios indicados para las librerías coinciden con los de la plataforma en que se use. En caso contrario, puede preguntarse al administrador del sistema dónde se encuentran, aunque normalmente el administrador preferirá que se los busque uno mismo con find.

La forma de compilación varía entre los distintos sistemas, así por ejemplo, en Linux es necesario incluir la librería libdl.so.x.yy, donde la X y las YY indican el número de versión. Esto se debe a la forma en que están compiladas las librerías en la distribución slackware.

En Windows 95 y NT, lo más frecuente es usar los entornos integrados de desarrollo de Microsoft o de Borland:



Load

Como ya se indicó al principio del capítulo, se puede usar el comando load de Tcl para cargar un fichero que contenga un paquete de comandos, los inicializa y los incorpora al interprete. Su sintaxis es:

load NombreFichero NombrePaquete

El tipo de fichero varía de unos sistemas a otros, pero normalmente consiste en una librería compartida, .so en Unix .dll en Windows.

Una vez que se carga el fichero, se invoca a un procedimiento que inicialice el paquete. El nombre de esta función viene dado por el nombre del paquete; por ejemplo, si se llama estadistica, deberá incluir una función de inicialización llamada Estadistica_Init, cuyo prototipo será:

int Estadistica_Init(Tcl_Interp *interprete);

Load también puede usarse para el caso de paquetes que se hayan lincado estáticamente con la aplicación, declarándolos con la función Tcl_StaticPackage.

En cuanto a cómo realizar un DLL, lo mejor es consultar la documentación del compilador que vaya a usarse. También puede consultarse un ejemplo que se encuentra en Internet junto con las distribuciones de Tcl, en un fichero llamado example.zip.

Objetos

En su momento se comentó que en Tcl el único tipo de datos que existía eran las cadenas, sin embargo, a partir de la versión 8.0, Tcl usa, además, internamente los llamados objetos. Estos objetos se usan para almacenar variables, los argumentos que se pasan a los comandos así como los resultados. Los objetos se comportan como cadenas, pero internamente tienen una representación de los datos con la que se puede operar de forma más eficiente ya que varía según el tipo de dato de que se trate.

Cada una de las dos representaciones de los datos contenidos en un objeto se calculan únicamente cuando son necesarias. Por ejemplo, si tenemos un objeto que representa un entero, se pueden estar haciendo operaciones con él sin necesidad de volver a calcular cada vez la representación del entero como cadena, lo que no se hará hasta que explícitamente se necesite la cadena; esto permite una mayor rapidez en los cálculos, ya que no hay que hacer la transformación de cadena a entero y viceversa cada vez que se use en una operación.

Cuando se cambia cualquiera de las representaciones de un objeto, la otra se invalida, aunque no vuelve a calcularse hasta que sea necesario. Por ello no es conveniente acceder directamente a los campos de la estructura que componen un objeto; es por ello también, por lo que prácticamente se prohibe escribir los resultados de una función directamente en "interprete->result", tal y como se comentó anteriormente.

Para manipular un objeto como si fuera una cadena, se usan funciones como:

Tcl_Obj * Tcl_NewStringObj(puntero_a_cadena , longitud)

que crea un nuevo objeto que contendrá la cadena y devuelve un puntero al objeto creado,

Tcl_SetStringObj(Puntero_objeto, puntero_cadena, longitud)

modifica la cadena contenida en un objeto,

char * Tcl_GetStringFromObj(Puntero_objeto, longitud_cadena)

devuelve un puntero, que no debe ser modificado, a la cadena contenida y cuya longitud se indica en longitud_cadena.

Estas funciones se "repiten" para los distintos tipos posibles de objetos, así tenemos:

Tcl_Obj * Tcl_NewBooleanObj(Valor_booleano)
Tcl_Obj * Tcl_NewDoubleObj(Valor_doble)
Tcl_Obj * Tcl_NewIntObj(Valor_entero)
Tcl_Obj * Tcl_NewListObj(numero_objetos, matriz_de_puntero_objetos)
Tcl_Obj * Tcl_NewLongObj(Valor_largo)

Las peculiaridades de cada tipo se pueden consultar en las páginas de ayuda correspondientes.

Los objetos lo invaden todo en la nueva versión de Tcl ,así para definir un comando de Tcl en C, además de usar Tcl_CreateCommand se puede usar Tcl_CreateObjCommand, se diferencian a la hora de invocar la función correspondiente. La versión antigua, como ya se ha explicado, pasa el valor de los parámetros a la función en una matriz de punteros a cadenas de caracteres; pues bien, en la versión moderna lo hace en una matriz de punteros a objetos, así, si los parámetros son, por ejemplo reales, como los objetos tienen una representación interna adecuada, resulta que todo es más eficiente.

También se usan objetos para devolver resultados a Tcl desde un comando implementado en C, así tenemos:

Tcl_SetObjResult(interprete, Puntero_objeto)

equivalente a Tcl_SetResult para objetos.

En realidad las funciones mencionadas anteriormente, tales como Tcl_SetResult, Tcl_AppendElement y Tcl_AppendElement no es que no puedan usarse, pero es mejor, en la medida de lo posible, usar las nuevas basadas en objetos ya que son más eficientes, aunque a decir verdad, si tuviéramos prisa no usaríamos TclTk, así que se pueden seguir usando.

Por ejemplo, si tenemos un comando que debe devolver una lista de enteros, tendríamos que definir un nuevo objeto del tipo lista con Tcl_NewListObj, construir la lista con Tcl_ListObjAppendElement, y finalmente fijar el resultado con Tcl_SetObjResult.

También hay una nueva versión de Tcl_Eval llamada, con la originalidad habitual, Tcl_ObjEval, de prototipo:

int Tcl_EvalObj(interprete, Puntero_objeto

donde el objeto contiene una cadena con uno o varios comandos. En realidad, Tcl_Eval está implementada en la nueva versión mediante Tcl_ObjEval, por lo que es más rápido usarla directamente.

Como ejemplo de programación con objetos vamos a hacer un interprete con dos nuevos comandos:

Ejemplo 23-8
#include <stdio.h>
#include <tcl.h>
#include <tk.h>
/* Prototipos de funciones: */
int Tcl_AppInit(Tcl_Interp *interprete);
int media (ClientData clientdata,Tcl_Interp *interprete,
int objc,Tcl_Obj *CONST objv[]);
int divisores (ClientData clientdata,Tcl_Interp *interprete,
int objc,Tcl_Obj *CONST objv[]);
/* Lo único que necesita hacer main es invocar a Tk_Main */
void main (int argc,char *argv[])
{
Tk_Main(argc,argv,Tcl_AppInit);
}
/* Tk_Main crea el intérprete antes de llamar al Tcl_AppInit y se lo pase como parámetro. */
int Tcl_AppInit(Tcl_Interp *interprete)
{
/*Incializamos Tcl:*/
if(Tcl_Init(interprete)==TCL_ERROR)
{
fprintf(stderr,"Error en la inicialización de Tcl.\n");
return TCL_ERROR;
}
/*Inicializamos Tk: */
if(Tk_Init(interprete)==TCL_ERROR)
{
fprintf(stderr,"Error en la inicialización de Tk.\n");
return TCL_ERROR;
}
/* Definimos los nuevos comandos: */
Tcl_CreateObjCommand(interprete,"media",media,(ClientData)NULL,
(Tcl_CmdDeleteProc *)NULL);
Tcl_CreateObjCommand(interprete,"divisores",divisores,(ClientData)NULL,
(Tcl_CmdDeleteProc *)NULL);
return TCL_OK;
}
/* Con esto ya está terminada la inicialización, no hay más que implementar los comandos. */
int media
(ClientData clientdata,Tcl_Interp *interprete,int objc,Tcl_Obj *CONST objv[])
{
int elementos;
double numero;
double suma;
int i;
Tcl_Obj **valores;
Tcl_Obj *resultado;
/* Lo primero que hay que hacer es coger la lista con los datos que

se encuentra en argv[1] y separarla en su componentes: */

Tcl_ListObjGetElements(interprete,objv[1],&elementos,&valores);

for(suma=0,i=0;i<elementos;i++)
{
if(Tcl_GetDoubleFromObj(interprete,valores[i],&numero)==TCL_ERROR)
{
resultado=Tcl_NewStringObj("Error en los parametros.",30);
Tcl_SetObjResult(interprete,resultado);
return TCL_ERROR;
}
suma+=numero;
}
resultado=Tcl_NewDoubleObj(suma/elementos);
Tcl_SetObjResult(interprete,resultado);
return TCL_OK;
}
int divisores
(ClientData clientdata,Tcl_Interp *interprete,int objc,Tcl_Obj *CONST objv[])
{
int numero;
int i;
Tcl_Obj *resultado;
if (Tcl_GetIntFromObj(interprete,objv[1],&numero)==TCL_ERROR)
{
resultado=Tcl_NewStringObj("Que numero es ese?",30);
Tcl_SetObjResult(interprete,resultado);
return TCL_ERROR;
}
resultado=Tcl_NewListObj(0,(Tcl_Obj **)NULL);
for(i=1;i<=numero;i++)
if((numero%i)==0)
Tcl_ListObjAppendElement(interprete,resultado,Tcl_NewIntObj(i));
Tcl_SetObjResult(interprete,resultado);
return TCL_OK;
}

Previous Page Next Page


© 1995-98, etsimo WWW team
Última modificación: 19 de Agosto de 1998 - 17:49:44