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:
GT_Tcl, which implements utilities
for C++ - Tcl conversions, and several C++ friendly wrappers for Tcl
procedures.GT_Tcl_Info, which implements
a wrapper for the GT_Tcl_Algorithm<A>,
which implements a Tcl interface for a algorithm class A.
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);static string GT_Tcl::tcl (const GT_Graph& g, const node n)
static void tcl (const GT_Graph& g, const node n, string& result);
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);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) 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)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)static string GT_Tcl::tcl (const list<int>& integers);
static void GT_Tcl::tcl (const list<int>& integers, string&
result);static string GT_Tcl::tcl (const double d);
static void GT_Tcl::tcl (const double d, string& result);static string GT_Tcl::tcl (const list<double>& doubles);
static void GT_Tcl::tcl (const list<double>& doubles, string&
result);static string GT_Tcl::tcl (const list<string>& strings);
static void GT_Tcl::tcl (const list<string>& strings, string&
result);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);static string GT_Tcl::merge (const list<string>&
strings)static string GT_Tcl::merge (int& argc, char**& argv)
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);
}
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)Tcl_SplitList.
static int GT_Tcl::split_list (Tcl_Interp* interp, const
char* list, int& argc, char**& argv)char* using Tcl's Tcl_SplitList. argc
returns the number of entries in the array. This is a shortcurt for
Tcl_SplitList.
Tcl_Free.
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() constint GT_Tcl_Info::argc() constchar** GT_Tcl_Info::argv()const char** GT_Tcl_Info::argv() const
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) constchar*. 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) constGT_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; } }
bool GT_Tcl_Info::is_last_arg (int index) constis_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) constexists 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) constargs_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)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)
constargs_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) constargs_left_exactly tests wether there are at
least n arguments left after index. This is a
shortcut for args_left (index, n, true).
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:
const char* s. In the first case, the value is
parsed from the indexth command line parameter. In the second
case, the value is parsed directly from s.
TCL_OK. Otherwise,
TCL_ERROR is returned, and the result parameter is not
touched.
int parse (int& index, int& i)
int parse (const char* s, int& i)int parse (int& index, double& d)
int parse (const char* s, double& d)int parse (int& index, bool& b)
int parse (const char* s, bool& b)int parse (int& index, list<double>& l)
int parse (const char* s, list<double>& l)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)GT_Graph g.
int parse (int& index, GT_Graph*& g)
int parse (const char* s, GT_Graph*& g)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)int parse (int& index, const GT_Tcl_Graph* g, edge& e)
int parse (const char* s, const GT_Tcl_Graph* g, edge& e)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)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)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)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)
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)
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)
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)
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)
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)
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);
info.msg ("This is a lame excuse for an error message.");
void GT_Tcl_Info::msg (int error_number)void GT_Tcl_Info::msg (int error, int
i)void GT_Tcl_Info::msg (int error, const string&
s)
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.
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.
The following methods are required for a class that is derived from
GT_Tcl_Algorithm<>:
Tcl_Sample (const string& name)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()Tcl_Sample. It is virtual since
GT_Tcl_Algorithm<> has virtual functions.
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)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 ()check overrides the check method in
the algorithm class (see GT_Algorithm). It may be
overwritten here to reset additional member variables.
reset should always call
Algorithm::reset.
virtual int run (GT_Graph& g)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.
bool reset_before_run (bool)
virtual void reset_before_run (bool)
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)
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)
false. If your code
just examins the graph , set it to
true.
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)virtual void result (int i)virtual void result (const list<int>& l)virtual void result (double d)virtual void result (const list<double>&
doubles)virtual void result (const GT_Graph& g)GT_Tcl_Graph or derived from that.
virtual void result (const GT_Graph& g, const node n)
GT_Tcl_Graph or derived from that.
virtual void result (const GT_Graph& g, const edge e)
GT_Tcl_Graph or derived from that.
virtual void result (const GT_Graph& g,
const list<node>& l)GT_Tcl_Graph or derived from that.
virtual void result (const GT_Graph& g,
const list<edge>& edges)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)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)
To transform the result of an algorithm's check or
run methods into Tcl, proceed as follows:
check and/or run methods in the
class derived from GT_Tcl_Algorithm<Algorithm>.
Algorithm::check respectively.
Algorithm::check.
check or run into
a Tcl string and put that into result.
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.
Installation and initialization of a Graphscript command takes two steps:
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.