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.

Important: It is important to note that 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 &).

Continuing with the example:
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}.
As another example, assume that an operator that receives tuples of type 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();
}
The SPL language runtime also provides reflective APIs for manipulating values of SPL types. For instance, the Tuple type that is seen as part of the 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.

The << 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