The Tcl Interface



To use the Tcl interface and the class GT_Tcl_Algorithm, insert the following code in your program:

#include <gt_base/Tcl_Graph.h>
#include <gt_base/Tcl_Algorithm.h>

Graphlets Tcl interface consists roughly consists of the following classes:

The class GT_Tcl

Tools for Conversion from C++ to Tcl

The Graphscript representation of a graph, node or edge is of the form GT:id, where id is the id attribute of the graph, node or edge. The following methods can be used to convert nodes and edges to their Graphlet identifiers:

static string GT_Tcl::tcl (const GT_Graph& g)
static void tcl (const GT_Graph& g, string& result);
Transforms graph g into its Tcl representation.
static string GT_Tcl::tcl (const GT_Graph& g, const node n)
static void tcl (const GT_Graph& g, const node n, string& result);
Transforms node n in graph g into its Tcl representation.
static string GT_Tcl::tcl (const GT_Graph& g, const edge e)
static void GT_Tcl::tcl (const GT_Graph& g, const edge e, string& result);
Transforms node n in graph g into its Tcl representation.
static string GT_Tcl::tcl (const GT_Graph& g, const list<node>& nodes)
static void GT_Tcl::tcl (const GT_Graph& g, const list<node>& nodes, string& result)
Transforms the list nodes into its Tcl representations. All nodes must belong to graph g.
static string GT_Tcl::tcl (const GT_Graph& g, const list<edge>& edges)
static void GT_Tcl::tcl (const GT_Graph& g, const list<edge>& edges, string& result)
Transforms the list edges into its Tcl representations. All edges must belong to graph g.

The following methods transform integers, floating point numbers, and lists thereof into their Tcl representation.

static string GT_Tcl::tcl (const int i);
static void GT_Tcl::tcl (const int i, string& result)
Transforms an integer into a Tcl number.
static string GT_Tcl::tcl (const list<int>& integers);
static void GT_Tcl::tcl (const list<int>& integers, string& result);
Transforms a list of integers into a Tcl list of integers.
static string GT_Tcl::tcl (const double d);
static void GT_Tcl::tcl (const double d, string& result);
Transforms a floating point number into a Tcl number.
static string GT_Tcl::tcl (const list<double>& doubles);
static void GT_Tcl::tcl (const list<double>& doubles, string& result);
Transforms a list of floating point numbers into a Tcl list of numbers.
static string GT_Tcl::tcl (const list<string>& strings);
static void GT_Tcl::tcl (const list<string>& strings, string& result);
Transforms a list of strings into a Tcl list.

Wrappers for Tcl_Merge

The methods GT_Tcl::merge are wrappers for the function Tcl_Merge:

static string GT_Tcl::tcl (const list<string>& strings);
static void GT_Tcl::tcl (const list<string>& strings, string& result);
Transforms a list of strings into a Tcl list.
static string GT_Tcl::merge (const list<string>& strings)
Merges strings into a proper Tcl list.
static string GT_Tcl::merge (int& argc, char**& argv)
Merges the array argv (with argc entries) into a proper Tcl list.

The GT_Tcl::merge methods merge a C++ list of strings or an argc/argv array into a single string which has proper Tcl list syntax. This can be used The following example illustrates how to implement a command that returns a node and a list of edges, which is not covered by the standard methods for result managements:

void My_Tcl_Class::make_result (node n, list<edge>& edges, string& result)
{
    list<string> results;

    // Insert n and e in results
    r.push_back (GT_Tcl::tcl(n));
    r.push_back (GT_Tcl::tcl(edges));

    // Construct a proper Tcl list
    result = GT_Tcl::merge (results);
}

Wrappers for Tcl_SplitList

The following wrappers provide an easy way to use Tcl's Tcl_SplitList from C++. They are the inverse to the above GT_Tcl::merge methods.

static int GT_Tcl::split_list (Tcl_Interp* interp, const char* list, list<string>& splitted)
Split list into a list of strings using Tcl's Tcl_SplitList.
static int GT_Tcl::split_list (Tcl_Interp* interp, const char* list, int& argc, char**& argv)
Split list into a an array argv of char* using Tcl's Tcl_SplitList. argc returns the number of entries in the array. This is a shortcurt for Tcl_SplitList.
Warning: argv must be deallocated with Tcl_Free.

The class GT_Tcl_Info

The standard parameter set for a tcl command are client data, Tcl interpreter, number of arguments and arguments:

int sample_cmd (ClientData client_data,
    Tcl_Interp* interp,
    int argc,
    char* argv[])

We use a different approach. First, client_data is no longer freely available because it is a pointer to information on the C++ object that is associated with the Tcl command.

Second, arc and argv are almost always used as a pair, so we pack them, together with the interpreter, into a utility class named GT_Tcl_Info. So, the first parameter in all methods that parse the arguments of the Tcl command is of class GT_Tcl_Info.

interp, argc and argv

The class GT_Tcl_Info implements the following methods to access the original arguments of the Tcl command:

Tcl_Interp* GT_Tcl_Info::interp()
const Tcl_Interp* GT_Tcl_Info::interp() const
Provides access to the Tcl interpreter of the command.
int GT_Tcl_Info::argc() const
The number of arguments of the Tcl command.
char** GT_Tcl_Info::argv()
const char** GT_Tcl_Info::argv() const
The array of arguments of the Tcl command.

Access to parameters via index

In addition to the above methods, there are two operators [] and () to provide wasier access to individual arguments:

char* GT_Tcl_Info::operator[] (const int i)
const char* GT_Tcl_Info::operator[] (const int i) const
Access command line argument i as a char*. The following example illustrates how to use this operator:
class Tcl_My_Algorithm : public GT_Tcl_Algorithm {
    int the_option1;
    double the_option2;
public:
    Tcl_My_Algorithm();
    virtual ~Tcl_My_Algorithm();

    int parse ();
}


void Tcl_My_Algorithm::Tcl_My_Algorithm () :
    the_option1 (0), the_option2 (0.0)
{
}


int Tcl_My_Algorithm::parse (GT_Tcl_info& info, int& index, GT_Tcl_Graph* g)
{
    if (GT::streq(info[index], "-option1")) {
        // Process option1
        return TCL_OK;
    } else if (GT::streq(info[index], "-option2")) {
        // Process option2
        return TCL_OK;
    } else {
        info.msg (GT::format ("Unknown option: %s", info[index]);
        return TCL_ERROR;
    }
}

The function GT::streq (const char* s1, const char*s2) is a simple wrapper for (strcmp (s1,s2) == 0) whose sole purpose is to beef up readability.

char* GT_Tcl_Info::operator() (const int i)
const char* GT_Tcl_Info::operator() (const int i) const
Same as above, but returns a GT_Key object instead of a char*:
class Tcl_My_Algorithm : public GT_Tcl_Algorithm {
    int the_option1;
    double the_option2;
    GT_Key the_option1_key;
    GT_Key the_option2_key;
public:
    Tcl_My_Algorithm();
    virtual ~Tcl_My_Algorithm();

    int parse ();
}


void Tcl_My_Algorithm::Tcl_My_Algorithm () :
    the_option1 (0), the_option2 (0.0)
{
    the_option1_key = graphlet->keymapper.add ("-option1");
    the_option2_key = graphlet->keymapper.add ("-option2");
}


int Tcl_My_Algorithm::parse (GT_Tcl_info& info, int& index, GT_Tcl_Graph* g)
{
    if (info(index) == the_option1_key)) {
        // Process option1
        return TCL_OK;
    } else if (info(index) == the_option2_key)) {
        // Process option2
        return TCL_OK;
    } else {
        info.msg (GT::format ("Unknown option: %s", info[index]);
        return TCL_ERROR;
    }
}

Utilities for Indices

bool GT_Tcl_Info::is_last_arg (int index) const
is_last_arg checks wether the argument index is the last argument in the list. The following example shows how to parse the parameter list of a command that accepts only one option, namely "-option1" or "-option2" :
int Tcl_My_Algorithm::parse (GT_Tcl_info& info, int& index, GT_Tcl_Graph* g)
{
    const char* usage = "Usage: -option1|-option2";

    if (info.is_last_arg (index)) {
        if (GT::streq (info[index], "-option1")) {
            index += 1;
            // Process -option1
        } else if (GT::streq (info[index], "-option2")) {
            index += 1;
            // Process -option2
        } else {
            info.msg (usage);
            return TCL_ERROR;
        }
    } else {
        info.msg (usage);
        return TCL_ERROR;
    }

    return TCL_OK;
} 
bool GT_Tcl_Info::exists (int index) const
The method exists checks wether there exists an argument at index. The following example illustrates how to parse the parameters of an algorithm that accepts only a single parameter which must be a node.
int Tcl_My_Algorithm::parse (GT_Tcl_info& info, int& index, GT_Tcl_Graph* g)
{
    // The parameters of this algorithm is a single, optional
    // node n.

    node n;

    if (info.exists (index)) {
        if (info.parse (index, n) == TCL_OK && !info.exists (index+1)) {
            index ++;
            // Process n
        } else {
            info.msg ("Usage: ?node?");
            return TCL_ERROR;
        }
    } else {
        // The node is optional, so no error here.
    }

    return TCL_OK;
} 
int GT_Tcl_Info::args_left (int index) const
The method args_left returns how many arguments are left after index. The following example illustrates how to parse a command that takes two nodes as parameters:
int Tcl_My_Algorithm::parse (GT_Tcl_info& info, int& index, GT_Tcl_Graph* g)
{
    // The parameters of this algorithm are two nodes,
    // n1 and n2.

    const char* usage = "Usage: node1 node2";
    node node1;
    node node2;

    if (info.args_left (index) == 1) {
        if (info.parse (index, node1) == TCL_OK &&
            info.parse (index+1, node2) == TCL_OK) {
            // Process node1, node2
            index += 2;
        } else {
            info.msg (usage);
            return TCL_ERROR;
        }
    } else {
        info.msg (usage);
        return TCL_ERROR;
    }

    return TCL_OK;
} 
bool GT_Tcl_Info::args_left (const int index, const int n, bool exact)
The method args_left tests wether there are at least n arguments left (exact == false), or wether there are exactly n arguments left (exact == true).
bool GT_Tcl_Info::args_left_at_least (const int index, const int n) const
The method args_left_at_least tests wether there are at least n arguments left after index. This is a shortcut for args_left (index, n, false).
bool GT_Tcl_Info::args_left_exactly (const int index, const int n) const
The method args_left_exactly tests wether there are at least n arguments left after index. This is a shortcut for args_left (index, n, true).

Parsing the Arguments of the Tcl Command

The class GT_Tcl_Info provides a number of utility methods to parse command line parameters. On the Tcl side, all parameters are represented as strings. The parse methods are useful to translate Tcl command line parameters to C++ objects. The following example illustrates how to use the parse method:

int My_Tcl_Class::parse (GT_Tcl_info& info, int& index, GT_Tcl_Graph* g)
{
    int code = TCL_OK;
    
    if (GT::streq (info[index], "-option1")) {

        // Version 1 - parse with index argument

        if (!info.is_last_arg(index)) {
        
            int i;
            code = info.parse (index+1, i);
            if (code == TCL_OK) {
                sample_option (i);
                index ++;
            } else {
                info.msg ("Illegal value for option -option1");
                return code;
            }

        } else {

            info.msg (GT::format ("Usage: -option1 value"));
            return TCL_ERROR;

        }

    } else if (GT::streq (info[index], "-option2")) {
        
        // Version 2 - parse with const char* argument

        if (!info.is_last_arg(index)) {
        
            int i;
            const char* value = info[index+1];
            code = info.parse (value, i);
            if (code == TCL_OK) {
                sample_option (i);
                index ++;
            } else {
                info.msg ("Illegal value for option -option2");
                return code;
            }

        } else {

            info.msg (GT::format ("Usage: -option2 value"));
            return TCL_ERROR;

        }
    } else {

        info.msg (GT::format ("%s is not a valid argument", info[index]);
        return TCL_ERROR;

    }


    return code;
}

The parse methods below are all organized as follows:

Parsing Simple Data Types

int parse (int& index, int& i)
int parse (const char* s, int& i)
Parse an integer i.
int parse (int& index, double& d)
int parse (const char* s, double& d)
Parse a floating point d.
int parse (int& index, bool& b)
int parse (const char* s, bool& b)
Parse a boolean value b.
int parse (int& index, list<double>& l)
int parse (const char* s, list<double>& l)
Parse a list of floating point numbers l.

Parsing Graphs, Nodes and Edges

The following methods parse graphs, nodes, edges and lists of nodes and edges:

int parse (int& index, GT_Tcl_Graph*& g)
int parse (const char* s, GT_Tcl_Graph*& g)
Parse a GT_Graph g.
int parse (int& index, GT_Graph*& g)
int parse (const char* s, GT_Graph*& g)
Parse a GT_Graph g.
int parse (int& index, const GT_Tcl_Graph* g, node& n)
int parse (const char* s, const GT_Tcl_Graph* g, node& n)
Parse a node n of graph g.
int parse (int& index, const GT_Tcl_Graph* g, edge& e)
int parse (const char* s, const GT_Tcl_Graph* g, edge& e)
Parse a edge e of graph g.
int parse (int& index, const GT_Tcl_Graph* g, list<node>& nodes)
int parse (const char* s, const GT_Tcl_Graph* g, list<node>& nodes)
Parse a list of nodes nodes of graph g.
int parse (int& index, const GT_Tcl_Graph* g, list<edge>& edges)
int parse (const char* s, const GT_Tcl_Graph* g, list<edge>& edges)
Parse a list of edges edges of graph g.

The following methods parse a list which may contain nodes and edges. This may be using when dealing with selected objects, which may either be nodes or edges, or ease the implementation of parameters which can either be nodes or edges.

For example, the Tcl code for a command could be

proc my_action {editor} {
    make_green $graph -objects [concat \
        $GT_selection($editor,selected,node) 
        $GT_selection($editor,selected,node)]
}

In C++, the parse method would be as follows:

int My_Tcl_Class::parse (GT_Tcl_info& info, int& index, GT_Tcl_Graph* g)
{
    int code = TCL_OK;
    
    if (GT::streq (info[index], "-objects") &&
        !info.is_last_arg(index)) {
        
        list<node> nodes;
        list<edge> edges;
        if ((code = info.parse (index+1, g, nodes, edges))) {
            process_nodes (nodes);
            process_edges (edges);
        } else {
            info.msg ("Illegal value for option -objects");
            return code;
        }
    }
}
int parse (int& index, const GT_Tcl_Graph* g, list<node>& nodes, list<edge>& edges)
int parse (const char* s, const GT_Tcl_Graph* g, list<node>& nodes, list<edge>& edges)
Parse a list of nodes or edges of graph g, and return it in nodes and edges.

Parsing Tcl Arrays

Many graph algorithms require a value for each node in the graph as input. For example, a planarity test algorithm usually needs a dfs number for each node. To maximize flexibility, the corresponding Tcl command have the following form:

planar_embedding $graph \
    -dfsnumbers dfsnum

where dfsnum is an array with nodes as indices and dfs numbers as entries. This apporach would allow easy studying of the effect of different DFS number sets on algoritms.

The following parse methods translate arrays of nodes and edges on the command line into GTL node_map and edge_map structures:

int parse (int& index, const GT_Tcl_Graph* g, node_map<int>*& array)
int parse (const char* s, const GT_Tcl_Graph* g, node_map<int>*& array)
Parse an array of nodes with integer values of graph g at position index and return it in array.
int parse (int& index, const GT_Tcl_Graph* g, node_map<double>*& array)
int parse (const char* s, const GT_Tcl_Graph* g, node_map<double>*& array)
Parse an array of nodes with floating point values of graph g at position index and return it in array.

int parse (int& index, const GT_Tcl_Graph* g, node_map<string>*& array)
int parse (const char* s, const GT_Tcl_Graph* g, node_map<string>*& array)
Parse an array of nodes with integer values of graph g at position index and return it in array.
int parse (int& index, const GT_Tcl_Graph* g, edge_map<int>*& array)
int parse (const char* s, const GT_Tcl_Graph* g, edge_map<int>*& array)
Parse an array of edges with integer values of graph g at position index and return it in array.

int parse (int& index, const GT_Tcl_Graph* g, edge_map<double>*& array)
int parse (const char* s, const GT_Tcl_Graph* g, edge_map<double>*& array)
Parse an array of edges with floating point values of graph g at position index and return it in array.

int parse (int& index, const GT_Tcl_Graph* g, edge_map<string>*& array)
int parse (const char* s, const GT_Tcl_Graph* g, edge_map<string>*& array)
Parse an array of edges with string values of graph g at position index and return it in array.

Error Messages

GT_Tcl_Info's msg procedures can be used to return an error message.

void GT_Tcl_Info::msg (const string& text)
void GT_Tcl_Info::msg (const char* text);
Return a plain text error message:
info.msg ("This is a lame excuse for an error message.");
void GT_Tcl_Info::msg (int error_number)
Output Graphlet error message error_number. See the section on error messages in the graphlet toolbox for more details.
void GT_Tcl_Info::msg (int error, int i)
Output Graphlet error message error with parameter i. See the section on error messages in the graphlet toolbox for more details.
void GT_Tcl_Info::msg (int error, const string& s)
Output Graphlet error message error with text parameter s. See the section on error messages in the graphlet toolbox for more details.

Note: The msg methods set the result of the associated Tcl interpreter directly (by way of Tcl_SetResult).

Occassionally, parseres need to return more complex error messages. In these cases, the error message should be constructed with the GT_Tcl::tcl methods as listed above, and then fed into GT_Tcl_Info::msg.

The class GT_Tcl_Algorithm

The Tcl Interface of a Graph Algorithm

A Tcl interface for a Graphlet algorithm is constructed with the template class GT_Tcl_Algorithm. GT_Tcl_Algorithm<a> is a template class which creates a generic Tcl interface for a class a which must be derived from GT_Algorithm.

class Tcl_Sample : public GT_Tcl_Algorithm<Sample>
{
public:    
    Tcl_Sample (const string& name);
    virtual ~Tcl_Sample ();

    virtual int run (GT_Graph& g);
    virtual int parse (GT_Tcl_info& info, int& index, GT_Tcl_Graph* g);
};

The class Tcl_Sample derives both from the class Sample and the class GT_Tcl_Algorithm_Command. Here, Sample implements the algorithm, GT_Tcl_Algorithm_Command implements the Tcl interface, and GT_Tcl_Algorithm<> combines the two.

Required Methods

The following methods are required for a class that is derived from GT_Tcl_Algorithm<>:

Tcl_Sample (const string& name)
This is the constructor of the class Tcl_Sample. Its parameter is the name of the Tcl command, which is usually also the name of the algorithm. GT_Tcl_Algorithm<>'s constructor takes a single argument which is the name of the Tcl Command, so the constructor should be declared as
Tcl_Sample::Tcl_Sample (const string& name) :
    GT_Tcl_Algorithm<Sample> (name)
{
    // Initialize member variables here
}
  
virtual ~Tcl_Sample()
This is the destructor of the class Tcl_Sample. It is virtual since GT_Tcl_Algorithm<> has virtual functions.

Optional Methods

virtual int parse (GT_Tcl_info& info, int& index, GT_Tcl_Graph* g)
parse is called to parse the argument at index.
virtual int check (GT_Graph& g, string& message)
The method check overrides the check method in the algorithm class (see GT_Algorithm). A typical reason to override check is to produce a Tcl specific error message, for example to return the offending nodes or edges.
virtual void reset ()
The method check overrides the check method in the algorithm class (see GT_Algorithm). It may be overwritten here to reset additional member variables.
Note: reset should always call Algorithm::reset.
virtual int run (GT_Graph& g)
The method run overrides the check method in the algorithm class (see GT_Algorithm). run must be overriden if the algorithm performs Tcl specific actions, such as hooks, animation or accessing Tcl variables.

Flags

bool reset_before_run (bool)
virtual void reset_before_run (bool)
If this flag is set to true, then the method reset each time when the command is executed. This is the default. If you want to keep all parameter settings from one call to another, for example because each call performs a n animation step, then set this flag to false.
bool might_change_structure ()
virtual void might_change_structure (bool)
This flag is a hint that the command might change the topological structure of the graph. The default is false. If your code just examins the graph (or only assigns new coordinates), set it to true.
bool might_change_coordinates()
virtual void might_change_coordinates (bool)
This flag is a hint that the command might change any coordinates in the graph. The default is false. If your code just examins the graph , set it to true.

Returning a result

The class GT_Tcl_Algorithm provides a member variable result of type string which holds the Tcl result of the algorithm. There are also several utility procedures which help to manage result and feed graphs, nodes and edges directly into result:

virtual void result (const string& s)
Return the string s.
virtual void result (int i)
Return the integer i.
virtual void result (const list<int>& l)
Return the list of floating point numbers l.
virtual void result (double d)
Return the floating point number d.
virtual void result (const list<double>& doubles)
Return the list of floating point numbers l.
virtual void result (const GT_Graph& g)
Return the graph g. Note that g must be an object of class GT_Tcl_Graph or derived from that.
virtual void result (const GT_Graph& g, const node n)
Return the node n of graph g. Note that g must be an object of class GT_Tcl_Graph or derived from that.
virtual void result (const GT_Graph& g, const edge e)
Return the edge e of graph g. Note that g must be an object of class GT_Tcl_Graph or derived from that.
virtual void result (const GT_Graph& g, const list<node>& l)
Return the list of nodes l. All nodes must belong to graph g. Note that g must be an object of class GT_Tcl_Graph or derived from that.
virtual void result (const GT_Graph& g, const list<edge>& edges)
Return the list of edges l. All edges must belong to graph g. Note that g must be an object of class GT_Tcl_Graph or derived from that.

The following procedures are used to return node and edge maps. They do not actually return a Tcl result, but copy a node_map or edge_map data structure into a Tcl array:

virtual void result (const GT_Graph& g, const char* name, const node_map<int>& array)
virtual void result (const GT_Graph& g, const char* name, const node_map<double>& array)
virtual void result (const GT_Graph& g, const char* name, const edge_map<string>& array)
Fill the Tcl array name with the contents of map.
virtual void result (const GT_Graph& g, const char* name, const edge_map<int>& array)
virtual void result (const GT_Graph& g, const char* name, const edge_map<double>& array)
virtual void result (const GT_Graph& g, const char* name, const node_map<string>& array)
Fill the Tcl array name with the contents of map.

Returning an error code

To transform the result of an algorithm's check or run methods into Tcl, proceed as follows:

  1. Implement check and/or run methods in the class derived from GT_Tcl_Algorithm<Algorithm>.
  2. These methods execute Algorithm::check respectively. Algorithm::check.
  3. Then convert the output of check or run into a Tcl string and put that into result.

Error Handling

The default behavior for the Tcl interface is to return a Tcl error, which will cause a runtime error unless catched if the check fails. The following piece of Tcl can be used to prevent a runtime message:

if [catch { my_algorithm GT(top,graph) } error_message] {
    tk_dialog .my_errormsg "Error Message" \
        error_message error 0 "Ok"
}

An even better way to solve the above problem is to implement a dedicated GraphScript command for the check phase. This will also allow for a better user interface for error handling, for example to select the offending nodes and edges.

Installing Graphscript Commands

Installation and initialization of a Graphscript command takes two steps:

  1. Create the C++ object.
  2. Install it into the Tcl interpreter, and check the return code for errors.
GT_Tcl_Command's install method is used to install a command into a Tcl interpreter:
// (1) Create the C++ object
GT_Tcl_Algorithm_Command* sample =
new GT_Tcl_Sample ("sample");
// (2) Install the object in the interpreter
code = sample->install (interp);
// (3) IMPORTANT: check return code.
if (code == TCL_ERROR) {
    return code;
    }

This installation should be placed in the startup file of your Graphscript interpreter or in the initialization procedure of your module. See the section on how to build modules for details.


<- Algorithms | Up | Modules ->