C++ types
C++ types in SPL are typically implemented based on the C++ standard library or other well established libraries.
In most cases, the C++ types in SPL are implemented based on commonly
used C++ classes. If a C++ standard library type is available, then
the SPL C++ type implementation is either a typedef of or extends
from that type. Examples include SPL::rstring
, which
extends from std::string
and SPL::complex32
,
which is a typedef to std::complex<SPL::float32>
.
For types that are not provided by the standard library, SPL C++
types employ well established libraries. For instance, list<T>
extends
from std::vector<T>
– part of the C++ standard
library. Another example is SPL::decimal32
, which
is a typedef to std::decimal::decimal32
– a
decimal floating point class that is provided by decNumber++
library.
In other cases, SPL provides its own type implementations. Examples
include SPL::blob
and SPL::timestamp
.
SPL::list
extends
from std::vector
and not std::list
.
Furthermore, SPL::map
and SPL::set
extend
from std::tr1::unordered_map
and std::tr1::unordered_set
;
not from std::map
and std::set
.
SPL lists are not linked-lists. They are dynamic arrays laid out on
contiguous memory. SPL maps and sets are hash-tables, and not trees.Tuples (tuple<...>
in SPL) and enumerations
(enum{...}
in SPL) are generated types.
Typedefs can be used to refer to these generated types in operator
implementations. For instance, assume that an operator that receives
tuples of type tuple<float32 speed, enum{UP,DOWN} dir>
on
its first input port. In this case, the operator has a typedef IPort0Type
for
the C++ class that is generated for this tuple. In general, for tuples
from input port n
, there is a typedef IPort<n>Type
and
similarly for tuples from output port n
, there
is a typedef OPort<n>Type
. Tuples provide member
constructors, getters, and setters for initialization and manipulation
of tuple attributes.
Getters take the form <type> const & get_<name>()
const
and <type> & get_<name>()
.
The setters take the form void set_<name>(<type>
const &)
.
void process(Tuple & tuple, uint32_t port) {
IPort0Type & tp = static_cast<IPort0Type &>(tuple);
float32 & speed = tp.get_speed();
IPort0Type::dir_type & dir = tp.get_dir();
if(dir==IPort0Type::dir_type::UP) ...
}
The
generated tuple classes also have typedefs for each attribute they
contain. In this example, the typedef dir_type
from
the generated tuple class refers to the enumeration. In general, the
typedefs inside the generated tuple classes take the form <name>_type
where <name>
is
the attribute name. The generated enumeration classes provide
the same literals that appear in an SPL application's source
code. In the example, dir_type::UP
is compared against
the value of the dir
attribute, which is of type enum{UP,DOWN}
.tuple<float64 value, tuple<float32 x, float32
y> pos>
on its first input port. In this case, it can access
the position tuple as follows:void process(Tuple & tuple, uint32_t port) {
IPort0Type & tp = static_cast<IPort0Type &>(tuple);
float64 & value = tp.get_value();
IPort0Type::pos_type & pos = tp.get_pos();
float32 & x = pos.get_x();
float32 & y = pos.get_y();
}
process
member function of the operators
is a reflective type. Table 1 gives
the reflective base classes for SPL C++ types. These APIs enable operator
developers to write generic code for handling different types, without
the need for code generation (otherwise done through Perl code generation
APIs at compile time). This generic code can cost additional processing.
For performance sensitive applications, use code that is type-specific.
All SPL types have <<
and >>
operator
overloads for serializing and de-serializing themselves to/from character
streams. They also provide operator overloads for binary serialization
and deserialization by theNetworkByteBuffer
and NativeByteBuffer
classes.
<<
and >>
operators
are overloaded for the rstring
class such that the
character serialization and deserialization of rstrings
use
the SPL string literal format. This way any string that is serialized
to a character stream can be deserialized unambiguously. An rstring
can
be cast to std::string&
to get the non-literal
based serialization and deserialization behavior. For example,ostream & ostr = ...
SPL::rstring abc = "ab c";
ostr << abc; //writes: "ab c"
ostr << abc.string(); // writes: ab c
ostr << abc.c_str(); // writes: ab c
ostr << static_cast<std::string&>(abc); //writes: ab c
assert(abc[0]=='a'); // "is about serialization, not part of the content