Skip to content

Latest commit

 

History

History
256 lines (170 loc) · 8.55 KB

examples-writing-converters.rst

File metadata and controls

256 lines (170 loc) · 8.55 KB

Writing Converters

Basic Concept

Warning

This example has been written with simplicity in mind and should not be used as a blueprint for production code. In particular:

  • It does not handle machine Endianess properly
  • It does not handle machine word sizes properly
  • It leaks memory
  • It does not validate any data

Note

In many practical cases, some of these problems (and much of the manual work) can be avoided by using an IDL-specification in combination with code generation and generic converters as described in the `google protocol buffers`_ documentation.

In |project|, :term:`converters <converter>` are used to serialize and deserialize programming-language objects for transportation (e.g. over a network connection). |project| comes with :term:`converters <converter>` for the fundamental types listed :ref:`here <types>`. However, in some use-cases it is necessary to use additional :term:`converters <converter>` for domain-specific :term:`data types <data type>` and/or serialization mechanisms.

This example demonstrates how to add such :term:`converters <converter>` to |project| using the running example of a :term:`converter` for a fictional SimpleImage :term:`data type`.

In order to implement a new converter, the following information is required:

TODO

:cpp:class:`converter_tutorial::SimpleImage` Domain Class

The domain :term:`data type`:

.. literalinclude:: upstream/rsb-cpp/examples/custom_converter/SimpleImage.h
   :language:    cpp
   :start-after: mark-start::body
   :end-before:  mark-end::body
   :linenos:

:cpp:class:`converter_tutorial::SimpleImageConverter` Class

For the actual :term:`converter` implementation, four things are needed:

  1. The C++ representation of the :term:`wire type` has to be passed to the :cpp:class:`rsb::converter::Converter` interface as a template parameter.
  2. The :term:`wire schema` and :term:`data type` name have to be passed to the :cpp:class:`rsb::converter::Converter` constructor.
  3. The :cpp:member:`rsb::converter::Converter::serialize` method has to be implemented.
  4. The :cpp:member:`rsb::converter::Converter::deserialize` method has to be implemented.

A naive and incomplete implementation can be found in the following listings:

.. literalinclude:: upstream/rsb-cpp/examples/custom_converter/SimpleImageConverter.h
   :language:    cpp
   :start-after: mark-start::body
   :end-before:  mark-end::body
   :linenos:
.. literalinclude:: upstream/rsb-cpp/examples/custom_converter/SimpleImageConverter.cpp
   :language:    cpp
   :start-after: mark-start::body
   :end-before:  mark-end::body
   :linenos:

Using the Converter

A simple program that demonstrates the use of our :cpp:class:`SimpleImageConverter` can be found in

.. literalinclude:: upstream/rsb-cpp/examples/custom_converter/sender.cpp
   :language:    cpp
   :start-after: mark-start::body
   :end-before:  mark-end::body
   :linenos:

A similar program in which the registration of the :term:`converter` is missing can be found in

.. literalinclude:: upstream/rsb-cpp/examples/custom_converter/senderNoConverter.cpp
   :language:    cpp
   :start-after: mark-start::body
   :end-before:  mark-end::body
   :linenos:

This second program serves the purpose of familiarizing you with the "missing-converter" error message, that you will encounter sooner or later ;)

TODO
TODO

Using Protocol Buffer Types in Converters for Custom Domain Types

Imagine you have a custom domain type (in this example called FooBar) in your software, which you want to use with |project|. For being able to directly send and receive this type, you need a new :term:`converter`, which handles this type. Moreover, the generated data to be sent over the wire should be compatible with a data type defined using `google protocol buffers`_ (e.g. from the :ref:`RST library <rst:rst>`). For this example, we assume the protocol buffers type is called rst.foo.Bar. To implement a converter matching these assumptions, adapt the following explanations according to your actual data types:

TODO

Create a header file called :file:`FooBarConverter.h` and fill it with the following contents:

#include <rsb/converter/Converter.h>
#include <rsb/converter/ProtocolBufferConverter.h>

#include <rst/foo/Bar.pb.h>

class FooBarConverter: public rsb::converter::Converter<std::string> {
public:

    FooBarConverter();
    virtual ~FooBarConverter();

    std::string getWireSchema() const;

    std::string serialize(const rsb::AnnotatedData &data, std::string &wire);
    rsb::AnnotatedData deserialize(const std::string &wireType,
            const std::string &wire);

private:

    rsb::converter::ProtocolBufferConverter<rst::foo::Bar> converter;

};

Afterwards, create an implementation file called :file:`FooBarConverter.cpp` along the following lines:

#include "FooBarConverter.h"

#include <rsc/runtime/TypeStringTools.h>

using namespace std;

FooBarConverter::FooBarConverter() :
        rsb::converter::Converter<string>("unused", RSB_TYPE_TAG(FooBar)) {
}

FooBarConverter::~FooBarConverter() {
}

string FooBarConverter::getWireSchema() const {
    return converter.getWireSchema();
}

string FooBarConverter::serialize(const rsb::AnnotatedData &data,
        string &wire) {

    assert(data.first == this->getDataType());

    boost::shared_ptr<FooBar> source = boost::static_pointer_cast<FooBar>(data.second);
    boost::shared_ptr<rst::foo::Bar> dest(new rst::foo::Bar());

    // TODO 1: extract data from domain type and fill it into the protobuf message

    return converter.serialize(
            rsb::AnnotatedData(rsc::runtime::typeName<rst::foo::Bar>(), dest),
            wire);

}

rsb::AnnotatedData FooBarConverter::deserialize(
        const std::string &wireType, const std::string &wire) {

    boost::shared_ptr<rst::foo::Bar> source =
            boost::static_pointer_cast<rst::foo::Bar>(
                    converter.deserialize(wireType, wire).second);

    boost::shared_ptr<FooBar> dest(new FooBar);

    // TODO 2: Extract data from the protobuf message and fill it into your domain type

    return rsb::AnnotatedData(getDataType(), dest);

}

Your custom conversion logic of how to convert between the Protocol Buffer messages and your custom data types needs to be implemented at the two places indicated with TODO comments. The `google protocol buffers`_ documentation explains how to access the values from in the protocol buffer message.

Note

Please use a reasonable namespace for you actual converter.

TODO
TODO