In the polymake terminology, a client is a subroutine written in C++. There are many situations where the implementation in a client form is better a perl subroutine, especially when:

For technical reasons, the current polymake version still requires that a client runs as a separate program. In the next releases they become real subroutines, and the elements of the C++ API concerning the inter-process communication will disappear. Then you will have to change the clients developed so far. To minimize the conversion work, we suggest a special structure for client programs, where the computational part is clearly separated from the communication part. All clients included in the distribution are already built according to this scheme. You are welcome to use them as learning examples or just as copy-and-paste mines.

Let's look at one of the standard clients, pvolume, which takes the triangulation of a polytope and calculates its volume. The algorithm is simple enough to let us concentrate on the coding principles.

/* Copyright (c) 1997-2004 ... This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License ... */
To warm up, we start with legal issues. The polymake system as whole and the C++ API library in particular are distributed under the conditions of GPL. Since your client is using the library, it automatically inherits the license, until you have our explicit permittance to change it. We don't object in general to the commercial use of polymake or its parts, but, for obvious reasons, we want to be informed about it.
#include <Poly.h> #include <Rational.h> #include <Array.h> #include <linalg.h>
Poly.h declares the communication interface and the C++ pendant of the perl Poly::Object class.
Rational.h supplies the GMP rational numbers.
Array.h and linalg.h come from the Polymake Template Library and define the data structures and linear algebra functions used in this client. Some classes (like Matrix and Set) are already included in linalg.h, thus only Array needs a separate #include statement.
namespace polymake { namespace polytope {
All clients have to reside in an application-specific namespace. The namespace has always the same name as the application itself. The outer namespace polymake contains the definitions of the most heavily used PTL classes which are common to all applications. This naming convention almost completely eliminates the needs for explicit qualification of names and using statemens.
void volume(Poly& p, const char* points_property, const char* triangulation_property) {
This is the main computational function in this client. p is the object (presumably of type RationalPolytope) whose volume is to be computed. In C++ all polymake object type are represented by the same class Poly.
This client is designed flexible enough to accept different sets of input properties, for example POINTS and TRIANGULATION_INT or VERTICES and TRIANGULATION, hence the additional parameters points_property and triangulation_property.
const bool bounded=p.give("BOUNDED"); if (!bounded) { p.take("VOLUME") << Poly::undefined(); return; }
Here we see the two most important polymake API methods in action. The method give() returns the value of a property, in this case it is a boolean BOUNDED.
The method take() creates a new property VOLUME. In this code fragment we detect the case of an unbounded polyhedron. Its volume is not defined, thus we assign the special value Poly::undefined to the result property.
const Matrix<Rational> Points=p.give(points_property); typedef Array< Set<int> > triangulation; const triangulation Triangulation=p.give(triangulation_property);
Now we proceed with the case of a bounded polytope, where we have to do real computations. Thus we read the necessary properties using the same method give as above, but now with property names passed in variables.
A remark on the choice of the data structures. Although the most clients keep the point coordinates kept in a Matrix<Rational> (or in seldom cases Matrix<double>) and the triangulation in Array< Set<int> >, it is not the only possible choice. You may perfectly go with std::vector< std::set<int> > and some matrix class of your own. The main decision criteria must be the run-time characteristics of the class required for the algorithm, like random access, dynamic reallocation, or efficient set-theoretic operations.
The only requirements imposed by the polymake API on the data types used for keeping the object properties are:
Rational volume(0); for (Entire<triangulation>::const_iterator s=entire(Triangulation); !s.at_end(); ++s) volume += std::abs(det( Points.minor(*s,All) )); volume /= fac(Points.cols()-1); p.take("VOLUME") << volume; } } }
Now that we have gathered all necessary data, we can make the calculations. For each simplex in Triangulation, we take the coordinates of its vertices, compute the determinant, and sum it up ignoring the orientation. Finally, we divide by the factorial of the dimension, which is one less than the coordinate vector dimension due to the homogeneous coordinates used in the application polytope.
The last action ist then to store the computed value in the object's property VOLUME. Again, this is done by take().
Note the special end-sensitive iterator s created by the function entire at the beginning of the loop. It is a PTL's replacement for the standard pair of methods begin() and end().

Here is the end of the substantial part of the client. The rest deals with communication start-up and error handling; as already said in the introduction, it is the part that will substantially change, or even disappear, in the next releases.

using namespace polymake; int main(int argc, const char *argv[]) { if (argc!=4) { cerr << "usage: " << argv[0] << " <file> <points_property> <triangulation_property>\n"; return 1; }
By convention all polymake clients print a short usage message if called with wrong arguments (especially without arguments.) Even if this client is almost always called by the polymake server from the production rules and not from the command line, we don't want to break this good tradition.
The non-zero exit code is important for polymake script to detect a client failure.
try { Poly p(argv[1], ios::in | ios::out); polytope::volume(p, argv[2], argv[3]); }
First, the object p is attached to the polymake object of interest, which is either a data file (when called directly from the command line) or a "secret code" generated by the polymake script for a Poly::Object reference (when called from perl via the Modules::client function.) The set of flags ios::in | ios::out signals that the client is going to both get properties from the object and create new properties.
Then we call the function polymake::polytope::volume defined above.
catch (const std::exception& e) { cerr << e.what() << endl; return 1; } return 0; }
All errors occuring in clients are signaled by exceptions. Since there is no way to propagate an exception between processes (recall, the client is an independent program!) we must catch all possible errors here, print a descriptive message, and signal the failure by the non-zero exit code. To say it again, the next releases will deploy a more elegant error handling.

That's it. We recommend to study the source code of few other clients, for example cube as a typical client creating an object from scratch, or pyramid as a client constructing an object from others. Useful reference material are the descriptions of the main Template Library classes and the polymake communication API.