[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
6.3 GiNaC's expression output system
GiNaC allows the output of expressions in a variety of different formats (see section Input and output of expressions). This section will explain how expression output is implemented internally, and how to define your own output formats or change the output format of built-in algebraic objects. You will also want to read this section if you plan to write your own algebraic classes or functions.
All the different output formats are represented by a hierarchy of classes
rooted in the print_context
class, defined in the ‘print.h’
header file:
-
print_dflt
the default output format
-
print_latex
output in LaTeX mathematical mode
-
print_tree
a dump of the internal expression structure (for debugging)
-
print_csrc
the base class for C source output
-
print_csrc_float
C source output using the
float
type-
print_csrc_double
C source output using the
double
type-
print_csrc_cl_N
C source output using CLN types
The print_context
base class provides two public data members:
class print_context { ... public: std::ostream & s; unsigned options; }; |
s
is a reference to the stream to output to, while options
holds flags and modifiers. Currently, there is only one flag defined:
print_options::print_index_dimensions
instructs the idx
class
to print the index dimension which is normally hidden.
When you write something like std::cout << e
, where e
is
an object of class ex
, GiNaC will construct an appropriate
print_context
object (of a class depending on the selected output
format), fill in the s
and options
members, and call
void ex::print(const print_context & c, unsigned level = 0) const; |
which in turn forwards the call to the print()
method of the
top-level algebraic object contained in the expression.
Unlike other methods, GiNaC classes don't usually override their
print()
method to implement expression output. Instead, the default
implementation basic::print(c, level)
performs a run-time double
dispatch to a function selected by the dynamic type of the object and the
passed print_context
. To this end, GiNaC maintains a separate method
table for each class, similar to the virtual function table used for ordinary
(single) virtual function dispatch.
The method table contains one slot for each possible print_context
type, indexed by the (internally assigned) serial number of the type. Slots
may be empty, in which case GiNaC will retry the method lookup with the
print_context
object's parent class, possibly repeating the process
until it reaches the print_context
base class. If there's still no
method defined, the method table of the algebraic object's parent class
is consulted, and so on, until a matching method is found (eventually it
will reach the combination basic/print_context
, which prints the
object's class name enclosed in square brackets).
You can think of the print methods of all the different classes and output
formats as being arranged in a two-dimensional matrix with one axis listing
the algebraic classes and the other axis listing the print_context
classes.
Subclasses of basic
can, of course, also overload basic::print()
to implement printing, but then they won't get any of the benefits of the
double dispatch mechanism (such as the ability for derived classes to
inherit only certain print methods from its parent, or the replacement of
methods at run-time).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |