The TCL_obj function creates a set of TCL commands that implement get/set functionality for the class members. For example, with a class definition:
class foo: public TCL_obj_t {int a, b; void foobar(int,char**)} bar;
TCL_obj(&bar,"bar",bar); creates the TCL commands bar.a
and bar.b. To set the value of bar.a, use the command bar.a val from TCL. To get the value, use [bar.a].
Also created is the TCL command bar.foobar, which will run respective member function of foo when called from TCL.
As an alternative to (int,char**) arguments for implementing
TCL commands, one can also declare the arguments
(TCL_args). TCL_args variables can be
assigned to numerical variables or strings variables, and the
appropriate argument is popped off the argument stack:
int x=args; double y=args;assigns the first argument to x and the second to y. This method use the
Tcl_Obj structure, so the values needn't be converted to
strings at all.
The arguments may also be assigned using the >> operator:
int x; double y; args >> x >> y;
A third style uses the [] operator:
int x=args[0]; double y=args[1];The number of remaining arguments is available as
TCL_args::count.
If operator>>(istream,T) is defined, then you may also use the
>> operator to set a variable of type T, eg:
void foo::bar(TCL_args args)
{
iarray x;
args>>x;
}
the assignment operator cannot be used for this purpose, unlike simple
types, because nonmember assignment operators are disallowed in the
standard. Type conversion operators do not appear to work.
For technical reasons, the name of the TCL command is available as
args[-1].
The TCL_obj_t data type also implements checkpoint and restart functions, so that any class inheriting from TCL_obj_t also gains this functionality, as well as client-server functionality.
A helper macro that performs the above is make_model, which is used in a declarative sense, which also initialises the checkpoint functor.
Associated with each of these TCL commands, is an object of type
member_entry<T>. Pointers to these objects
are stored in a hash table called
TCL_obj_properties. The STL hash
table behaved rather stangely when used for this purpose, so a class
wrapper around TCL hash tables was employed instead:
template<class T>
struct TCL_obj_hash
{
struct entry
{
entry& operator=(const T& x);
operator T();
};
entry operator[](const char*x);
};
So objects of member_entry<T>* can be inserted into the hash
table as follows:
member_entry<T>* m; eco_string d; TCL_obj_properties[d]=m;but explicit casts are required to extract the data:
member_entry<T>* m; eco_string d; m=(member_entry<T>*)(member_entry_base)TCL_obj_properties[d];Note the double cast is essential! is there anyway to do this more cleanly?.
A utility macro allows these objects to be accessed simply:
declare(name,typename, tcl_name)where name is the name of a variable (in reality a reference), of type typename that will refer to the variable having the TCL handle tcl_name. The macro performs error checking to ensure such a variable actually exists, and that it is of the same type as typename.
Objects can be placed into TCL_obj_properties by a several different means:
TCL_obj_t) into TCL_obj_properties, and also
completes the construction of the TCL_obj_t object;
TCL_obj_register(object,object name);
distrand PDF PDF.min -10 PDF.max 10 PDF.nsamp 100 PDF.width 3 PDF.Init dist ..... PDF.deleteThis macro also defines an x.delete procedure for deleting that object, once no longer desired.
A TCL registered object, particularly dynamically created
TCLTYPE objects can be assigned to a member of type
TCL_obj_ref. This is particularly useful
for random number generators:
class Foo: public TCL_obj_t
{
public:
TCL_obj_ref<random_gen> rng;
...
rng->rand();
};
Then the member Foo::rng can be assigned an arbitrary random number
generator within the TCL script, such as the PDF example above.
distrand PDF PDF.min -10 ... foo.rng PDF ...
Using TCL_obj_ref also allows that object to be serialised, and
to be reconnected after a restart, provided the object has been
created prior to the restart.