IBM ILOG Solver User's Manual > Extending the Library > Writing a Constraint: Allocating Frequencies > Writing your own constraint > Implementation classes and handles |
Implementation classes and handles |
INDEX
![]() |
Throughout this manual, it has been taken for granted that the major entities of Solver--variables and constraints themselves, for example--are simply objects in the full sense of C++. In fact, Solver entities depend on two classes: a handle class and an implementation class, where an object of the handle class points to an object of the corresponding implementation class. As documented in the IBM ILOG Solver Reference Manual, handles are passed by value, and they are created as automatic objects, where "automatic" has the usual C++ meaning.
For the most part, you will work only with handles, that is, the objects that are really pointers to corresponding implementation objects on the Solver heap. Solver takes care of all the allocation, memory-management, and de-allocation of these internal implementation objects, keeping the details of these implementation classes "out of sight." However, if for some reason, you need to define new classes of Solver objects, you need to do so in a two-step process: you'll have to define the underlying implementation class plus the corresponding class of handles. This chapter explains how to do that for constraints.
For a predefined Solver class, IlcConstraintI
, implementing an object with its own data members and virtual functions, Solver defines the handle class IlcConstraint
. Essentially, such a handle class contains objects that are really only pointers to instances of the corresponding implementation class IlcConstraintI
.
This has the practical effect of elevating the idea of a pointer to the level of an object itself. A number of advantages derive from this; for one thing, it provides greater certainty about how pointers are used. In particular, there is no longer any risk that the compiler will mistakenly interpret the expression x+1
as a pointer and apply pointer arithmetic to it. Instead, pointers are consistently and systematically treated as objects.
However, the fundamental precepts of Solver that there are "No more pointers" and that "Everything is an object" mean that when you extend Solver by defining a new implementation class, you must also define a function that returns a handle.
This rule applies particularly to the definition of new classes of constraints. Up to now, you've seen only the definition of a new implementation class of a new constraint. The definition of an implementation class generally looks something like this:
Handles for constraints are instances of the class IlcConstraint
. That class provides a constructor of an implementation class, or more precisely, IlcConstraintI*
. You must define a function that returns a handle, and the minimal service that the handle provides is to manage the memory allocation of instances in your implementation class. The form that it generally takes looks like this:
IlcConstraint MyConstraint( /* parameters for the constraint */){ // get the solver from a variable return new (solver.getHeap()) MyConstraintI(solver, /* ...args... */); } |
The body of that function builds an instance of MyConstraint
by calling the constructor and allocates a place on the Solver heap by calling the overloaded operator, new (solver.getHeap() ... )
. When you use this Solver new
, you'll get an efficient allocator that automatically manages how space is recovered in case of backtracking.
In our example, x != y
, you call IlcDiff
the function for creating the constraint. Here's the code for it:
That definition, of course, has two parts: the implementation and the handle. To use this new constraint, you simply write:
solver.add(IlcDiff(x, y)); |
That statement invokes all the mechanisms of Solver that insure what's taken into account, such mechanisms as propagation, backtracking, and so forth. In general, it can be said that a call to IlcDiff
as you've written it builds an instance of IlcDiffConstraint
and returns a handle for that object (in other words, an encapsulation of the object) to post by means of the post
member function. The instance of the implementation class is connected to the value event of x
and of y
. Every time these variables are bound, the propagate
member function executes.
The definition just outlined is the minimal definition for a new constraint. It is not adequate if you want to use the new definition as a metaconstraint.
© Copyright IBM Corp. 1987, 2009. Legal terms. | PREVIOUS NEXT |