A C++ class named plstream
has been introduced. It's central
purpose is provide a specific, object based encapsulation of the
concept of a PLplot output stream. Any output produced using a
plstream
object, will go to the PLplot output stream associated with
that object, regardless of what stream may have been active before.
In order to write a multiple output stream PLplot application, a C++
program can declare plstream
objects, and invoke drawing methods on
those objects, without regard to ordering considerations or other
coherency considerations. Although this has obvious simplification
benefit even for simple programs, the full benefit is most easily
appreciated in the context of Tk extended wish applications in which a
plstream
can be associated with each plframe.
The PLplot C API is composed of a set of drawing functions, all
prefixed with "pl", in an effort to prevent namespace collision.
However, the prefix "pl" is gratuitous, and in particular is
unnecessary in a C++ context. The plstream
class mirrors most
of the PLplot C API, but does so by dropping the "pl" prefix. The
plstream
class thus serves to collect the PLplot drawing
functions into a scope in which collisions with other similarly named
functions is not a concern. So, where a C programmer might write:
plsstrm( 1 ); plenv( ... ); plline( ... );
The C++ programmer can write:
plstream p( ... ); p.env( ... ); p.line( ... );
Is that an important benefit? The utility varies with the number of output streams in use in the program.
plmkstrm() is replaced by object declaration. plsstrm() is replaced by method invocation on the desired output stream object. plgstrm() is rendered irrelevant.
The skeptic may say, "But you have to type the same number of
characters! You've replaced 'pl' with 'p.', except it could be worse
for a longer object name." True. BUT, in this new scheme, most plots
will not be generated by invoking methods on a specific stream object,
but rather by deriving from plstream
, and invoking methods of
"this" object. See the section on derivation below.
The plstream
class will provide an abstract interface to the
2-d drawing functions. Instead of forcing the C++ user to organize
data in one of a small set of generally brain dead data layouts with
poor memory management properties, potentially forcing the C++ user to
not use a superior method, or to copy data computed in one layout
format to another for plotting (with consequent bug production), the
plstream
2-d plotting functions will accept an abstract layout
specification. The only thing which is important to the 2-d drawing
functions is that the data be "indexable". They should not care about
data layout.
Consequently, an abstract class, "Contourable_Data" is provided. This class provides a pure virtual method which accepts indexes, and is to be made to produce a function value for the user's 2-d data field. It is of no concern to PLplot how the user does this. Any mapping between index and data which the user wishes to use, may be used.
This methodology allows the C++ user to compute data using whatever storage mechanism he wants. Then, by deriving a class from PLplot's Contourable_Data abstract class, he can provide a mapping to his own data layout.
Note that this does /not/ mean that the C++ user's internal data layout must be derived from PLplot's Contourable_Data class. Suppose for example that the user data is stored in a C++ "matrix" class. To make this data contourable, the user may define a class which specializes the indexing concept of the PLplot Contourable_Data class to his matrix class. For example:
class Matrix { ... }; class Contourable_Matrix : public Contourable_Data { Matrix& m; public: Contourable_Matrix( Matrix& _m ) : m(_m) {} PLFLT operator()( int i, int j ) const { return m(i,j); } }; plstream p( ... ); Matrix m; // Code to fill m with data Contourable_Matrix cm(m); p.shade( cm, ... );
In this way the C++ user is completely freed from the tyranny of moronic data layout constraints imposed by PLplot's C or Fortran API.
The plstream::plshades
method and the other similar
methods require callbacks for fill and pltr, mirroring the requirements
for plshades
. The user may specify their own callbacks or
may use the callbacks provided by Plplot. If using Plplot callbacks the user
has two options. They may use the appropriate C functions as described in the
C API, however this will require direct linkage of the user's executable to
the C library as well as the C++ library, which would otherwise not be
necessary when using shared libraries. To avoid linking of the C library
the user may instead utilise the functions within the plcallback
namespace. The plcallback
namespace provides
fill
, tr0
, tr1
,
tr2
, and tr2p
callbacks which mirror
the functionality of the appropriate C functions.
Use of abstraction as in C) above will allow a single method in
plstream
to perform the services of multiple functions in the C API.
In those cases where multiple functions were provided with different
data layout specifications, but similar functionality, these can all
be collapsed into one, through the use of the abstract interface
technique described above.
Moreover, function name overloading can be used to simplify the
namespace for those cases where multiple functions were used to get
variations on a basic capability. For example, a single name such as
contour or shade can be used for multiple methods taking different
argument sets, so that for example, one can make simple plots of
rectangular data sets, or more complex generalized coordinate
mappings.