View on GitHub

Cpptcl

C++/Tcl, a library that allows to easily integrate C++ and Tcl.

Download this project as a .zip file Download this project as a tar.gz file
[prev][top][next]

Call Policies

Factories and sinks

C++ has extremely rich type system. You can see this when you expose some free or member function:

i.def("fun", fun);

The name of the function, fun, is enough for the C++/Tcl libray to discover the following information:
Sadly, these two pieces of information are sometimes not enough.
Consider the following C++ functions:

SomeClass * produce() {
  return new SomeClass();
}

void consume(SomeClass *p) {
  // ...
  delete p;
}

These two functions can be exposed this way:

i.def("produce", produce);
i.def("consume", consume);

After doing so, you can safely use them like here:

% set object [produce]
p0x807b790
% consume $object

The problem is that the produce command will not register any new Tcl command that would be responsible for managing member function calls or explicit object destruction, because, simply, it has no idea to which class definition (in the Tcl sense, not the C++ sense) the returned object belongs. In other words, the object name that is returned by the produce command has no meaning to the Tcl interpreter.
To solve this problem, some additional information has to be provided while exposing the produce function:

i.def("produce", produce, factory("SomeClass"));

As you see, there is additional parameter that provides hints to the C++/Tcl library. Such hints are called policies.
The factory policy above provides the following information:

What about the consume function?
There is also a problem - normally, objects are destroyed by explicit call to their -delete method. This call does two things:
We already know that the consume() function does the first thing. It is important also to do the second, otherwise the Tcl interpreter will be polluted with commands associated with non-existent objects (any call to such a command will result in undefined behaviour, which usually means a program crash).
In other words, we have to provide a hint to the C++/Tcl library that the consume() function is a sink for objects:
i.def("consume", consume, sink(1));

The sink policy above says:
The following is a complete example with the use of policies:

// example5.cc

#include "cpptcl.h"
#include <string>

using namespace std;
using namespace Tcl;

class Person {
public:
  Person(string const &n) : name(n) {}

  void setName(string const &n) { name = n; }
  string getName() { return name; }

private:
  string name;
};

// this is a factory function
Person * makePerson(string const &name) {
  return new Person(name);
}

// this is a sink function
void killPerson(Person *p) {
  delete p;
}

CPPTCL_MODULE(Mymodule, i) {
  // note that the Person class is exposed without any constructor
  i.class_<Person>("Person", no_init)
    .def("setName", &Person::setName)
    .def("getName", &Person::getName);
  
  i.def("makePerson", makePerson, factory("Person"));
  i.def("killPerson", killPerson, sink(1));
}

Now, let's try to exercise some interactive session with this:

% load ./mymodule.so 
% set p [Person "Maciej"]
invalid command name "Person"
% 

As you see, the Tcl class Person has no constructor (this is the effect of providing the no_init policy when the class was exposed).
Let's try another way:

% set p [makePerson "John"]
p0x807b810
% $p getName
John
% 

It works fine. Now:

% killPerson $p
% $p getName
invalid command name "p0x807b810"
% 


Interestingly, a single function may need to combine several policies at once:

ClassA * create(int i, ClassB *p1, string const &s, ClassC *p2) {
  // consume both p1 and p2 pointers
  // and create new object:
  return new ClassA();
}

This function needs to combine three policies:
  1. The factory policy, because it returns new objects.
  2. The sink policy on its second parameter.
  3. The sink policy on its fourth parameter.
The following definition does it:

i.def("create", create, factory("ClassA").sink(2).sink(4));

As you see, policies can be chained. Their order is not important, apart from the fact that the factory policy can be related to only one class, so it is the last factory policy in the chain that is effective.


Variadic functions

Another thing that can be controlled from policies is whether the function is variadic (whether it can accept variable number of arguments) or not.
This feature is supported for:
In order for the function (or constructor or method) to be variadic, both of the following conditions must be true:
  1. The type of last (or the only) parameter must be object const &
  2. The variadic() policy must be provided when defining that function.
The last formal parameter is of the generic object type and it allows to retrieve both single values and lists.
An example may help:

void fun(int a, int b, object const &c);

// later:
  i.def("fun", fun);

Above, the fun() function is defined in the interpreter as having exactly three parameters.
If more arguments are provided, they are discarded. If less, an error is reported.
Now, consider the following change:

void fun(int a, int b, object const &c);

// later:
i.def("fun", fun, variadic());

With the variadic() policy, the function will accept:
Similarly, a constructor with variable list of parameters may be defined as:

class MyClass {
public:
  MyClass(int a, object const &b);
  void fun();
};

// later:
i.class_<MyClass>("MyClass", init<int, object const &>(), variadic())
  .def("fun", &MyClass::fun);

Note that the last parameter in the constructor has type object const & and that the variadic() policy was provided.
This constructor will accept 1, 2 or more arguments.

Of course, the variadic class methods can be defined in the same way:

class MyClass {
public:
  void fun(object const &a);
};

// later:
i.class_<MyClass>("MyClass")
  .def("fun", &MyClass::fun, variadic());

As you see, the special, last parameter may be the only one, or preceded by any number of "normal" parameters.

The possibility to define a variadic function is currently the only way to write commands that accept more than 9 arguments.

The variadic() policy may be combined (in any order) with other policies.

The following is a full example of module that defines one variadic function:

#include "cpptcl.h"

using namespace Tcl;

int sumAll(object const &argv) {
  interpreter i(argv.get_interp(), false);

  int sum = 0;

  size_t argc = argv.length(i);
  for (size_t indx = 0; indx != argc; ++indx) {
    object o(argv.at(i, indx));
    sum += o.get<int>(i);
  }

  return sum;
}

CPPTCL_MODULE(Mymodule, i) {
  i.def("sum", sumAll, variadic());
}

This module can be used from the Tcl interpreter like here;

% load ./mymodule.so 
% sum
0
% sum 5
5
% sum 5 6 7
18
%