Custom types

DataStorm supports custom user types for the following:

  • Keys
  • Values
  • Update tags
  • Key or sample filter criteria

These data values must support binary encoding to be transmitted over the wire. Keys should also support the std::ostream& operator<<. If you intend to use regular expression filters on values, values should also support this operator.

On this page:

Binary encoding

By default, DataStorm uses the Ice encoding to transmit data values over-the-wire.

The Ice encoding supports encoding a number of C++ types out-of-the box:

  • unsigned char, bool, short, int, long long int, float, double, std::string
  • std::vector<T> where T is a type supported by the Ice encoding
  • std::map<K, V> where K and V are types supported by the Ice encoding

A DataStorm application can also define data types using the Slice language. The Slice definitions can be compiled with slice2cpp to generate C++ source and header files. The generated code will contain the necessary code to allow encoding and decoding the type.

Info

The Slice generated code contains support for both the Ice for C++98 and C++11 language mappings. When used with DataStorm, you should make sure to compile the generated code with -DICE_CPP11_MAPPING to build the C++11 language mapping generated code.

You can also provide encoding and decoding function if you don't want to rely on Slice for defining your data types. DataStorm uses the Encoder and Decoder templates for encoding. You will need to provide template specializations for your data type. The decoder and encoder template declarations are shown below:

C++
template<typename T, typename Enabler=void> struct Encoder
{
    static vector<unsigned char> encode(const T& value) noexcept;
};

template<typename T, typename Enabler=void> struct Decoder
{
    static T decode(const vector<unsigned char>& data) noexcept;
};

For example, you can specialize these two templates and implement the encode and decode static methods for the chrono::system_clock::time_point type as follow:

C++
namespace DataStorm
{

template<> struct Encoder<chrono::system_clock::time_point>
{
    static vector<unsigned char> encode(const chrono::system_clock::time_point& time) noexcept
    {
        ...
    }
};

template<> struct Decoder<chrono::system_clock::time_point>
{
    static chrono::system_clock::time_point decode(const vector<unsigned char>& data) noexcept
    {
        ...
    }
};

}

DataStorm can also provide the Ice communicator to the encoding and decoding methods. If you need the Ice communicator to perform the encoding and decoding, you should instead implement the following static methods:

C++
namespace DataStorm
{

template<> struct Encoder<chrono::system_clock::time_point>
{
    static vector<unsigned char> encode(const std::shared_ptr<Ice::Communicator>& communicator, const chrono::system_clock::time_point& time) noexcept
    {
        ...
    }
};

template<> struct Decoder<chrono::system_clock::time_point>
{
    static chrono::system_clock::time_point decode(const std::shared_ptr<Ice::Communicator>& communicator, const vector<unsigned char>& data) noexcept
    {
        ...
    }
};

}

Streaming operator

DataStorm uses the std::ostream& operator<< for two purposes:

  • Tracing
    If  you enable session or data tracing, DataStorm can print out the key values. For custom key types, if no streaming operator is provided, DataStorm will print the name of the C++ typeid and the address of the value. If you want to print out a user friendly value, you should override the streaming operator.
     
  • Filtering
    DataStorm provides a pre-defined regular expression filter. The regular expression is matched against the value transformed to a string with the streaming operator.

The streaming operator can be defined as follow:

C++
namespace DataStorm
{

std::ostream&
operator<<(std::ostream& os, const chrono::system_clock::time_point& t)
{
    os << t.time_since_epoch().count();
    return os;
}

}

Value cloning

DataStorm needs to copy values to handle partial updates. By default, values are copied using the default copy constructor. This might not always be appropriate depending on the value type. For example the value type could be a shared_ptr<T> or a complex type that requires deep-cloning. DataStorm uses the Cloner template to perform the clone. The template declaration is shown below:

C++
template<typename T, typename Enabler=void> struct Cloner
{
    static T clone(const T& value) noexcept;
};

You can specialize this template to implement a custom cloning for a given data type:

C++
namespace DataStorm
{

template<> struct Cloner<std::shared_ptr<MyClass>>
{
    static T clone(const std::shared_ptr<MyClass>& value) noexcept
    {
        return std::make_shared<MyClass>(value);
    }
};

}