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.
Any nonoverloaded member function can be accessed from TCL, provided the arguments and return types can be converted from/to TCL objects. In particular, it is not possible at present to call methods that take nonconstant references.
Overloaded method types in general cannot be called, but it is
possible to create variable length argument lists by declaring a
method with an (int,char**)
, or a (TCL_args)
signature. Such methods are not easily called from C++, and generally,
one needs to define a set of overloaded functions of a different name
(eg capitalised) suitable for calling
from C++, as well as the variable length argument list for use from
TCL. However, as a special case of an accessor (emulating the
setting/getting of an member attribute), one may make use of the
Accessor class, which is equally callable from C++ as
TCL.
Accessor is not easily usable from within the C++98 language standard
(see acessor.h in the test directory), but makes much more sense in
the C++11 standard. For example, assume that Name()
and
Name(const string&)
have been defined as a getter and setter
method of the attribute name
, then one may define a member
Accessor<string> name { [this](){return this->Name();}, [this](const std::string& x){return this->Name(x);} };where the use of lambdas and brace initialisers makes it easy to assign code for the getter and setter components of the accessor. This member will be accessible as an attribute from TCL (just as if name had been defined as a string member), and also callable from C++ as
name()
or name("someName")
as appropriate.
One downside of the Accessor class is that it is not copy constructible, as copying the accessor will copy a reference to the wrong accessed object. Consequently, if copy construction is required for the object being accessed (eg for DCAS), then a custom copy constructor needs to be provided.
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]
.
You can create TCL_args objects by using the <<
operator. This
enables the calling of methods taking a TCL_args object from C++,
viz:
struct Foo { void bar(TCL_args args) {...} }; Foo().bar(TCL_args()<<1<<3.5);
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 to extract the data, use
memberPtrCasted
if (T* m=TCL_obj_properties[d]->memberPtrCasted<T>()) ... *mwill allow you to access the TCL object
d
, if it is castable to
an object of type T
(is a T
, or is derived from a T
).
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);
(TCL_args)
. This enables constructor arguments to be
passed in from TCL.
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.