C++ full codec¶
This page describes how to manipulate prophy messages using C++ codec.
Codec uses value-semantics types with integral, array
,
vector
and optional
fields to represent prophy structs and unions.
These types have means to size, encode, decode and print represented messages.
Codec is nicknamed “full” as opposed to “raw” one, which allows to form an encoded message way faster but requires moderate amount of user code and attention.
Compilation¶
Prophy Compiler can be used to generate C++ full codec source code from .prophy files. This generated code together with C++ prophy header-only library can be used to handle Prophy messages.
Example compiler invocation:
prophyc --cpp_full_out . test.prophy
will result in creating test.ppf.hpp
and test.ppf.cpp
.
Generated code¶
All types in generated C++ code are enclosed in prophy::generated
namespace.
Note
array
and optional
class templates shipped with C++ prophy library
are derived from std
and boost
implementations,
for the sake of making library independent of C++11 std lib or boost libraries.
Enums are represented as regular C++ enums:
enum Test1
{
Test1_1 = 1,
Test1_2 = 2,
Test1_3 = 3
};
enum Test1
{
Test1_1 = 1,
Test1_2 = 2,
Test1_3 = 3
};
Structs and unions are represented as value-semantics types inheriting
self-specialized prophy::detail::message
class template (CRTP),
thus fulfill the API of prophy message:
template <class T>
struct message
{
template <endianness E>
size_t encode(void* data) const;
size_t encode(void* data) const;
template <endianness E>
std::vector<uint8_t> encode() const;
std::vector<uint8_t> encode() const;
template <endianness E>
bool decode(const void* data, size_t size);
bool decode(const void* data, size_t size);
template <endianness E>
bool decode(const std::vector<uint8_t>& data);
bool decode(const std::vector<uint8_t>& data);
std::string print() const;
};
Encode and decode can be used as method templates to choose
specific prophy::endianness
to process data or as regular methods,
which use native
endianness (actual machine endianness):
enum endianness
{
native,
little,
big
};
Structs are represented as C++ structs with corresponding fields:
struct Test2
{
u32 a;
};
struct Test2 : public prophy::detail::message<Test2>
{
enum { encoded_byte_size = 4 };
uint32_t a;
Test2(): a() { }
Test2(uint32_t _1): a(_1) { }
size_t get_byte_size() const
{
return 4;
}
};
Arrays are represented as array
or vector
.
In case of limited arrays - exceeding limit is not
prohibited, but encoding will serialize only elements up to limit:
struct Test8
{
i32 a[3];
i32 b<>;
i32 c<3>;
i32 d<...>;
};
struct Test8 : public prophy::detail::message<Test8>
{
enum { encoded_byte_size = -1 };
array<int32_t, 3> a;
std::vector<int32_t> b;
std::vector<int32_t> c; /// limit 3
std::vector<int32_t> d; /// greedy
Test8(): a() { }
Test8(const array<int32_t, 3>& _1, const std::vector<int32_t>& _2, const std::vector<int32_t>& _3, const std::vector<int32_t>& _4): a(_1), b(_2), c(_3), d(_4) { }
size_t get_byte_size() const
{
return b.size() * 4 + d.size() * 4 + 32;
}
};
Optional fields are represented by optional
template class:
struct Test6
{
u32* a;
Test2* b;
};
struct Test6 : public prophy::detail::message<Test6>
{
enum { encoded_byte_size = 16 };
optional<uint32_t> a;
optional<Test2> b;
Test6() { }
Test6(const optional<uint32_t>& _1, const optional<Test2>& _2): a(_1), b(_2) { }
size_t get_byte_size() const
{
return 16;
}
};
Union representation is similar to struct one - it contains all arms as independent fields. Depending on current value of discriminator, chosen arm will be encoded or printed. Decoding overwrites discriminator as well as decoded arm:
union Test7
{
0: u32 a;
1: Test2 b;
};
struct Test7 : public prophy::detail::message<Test7>
{
enum { encoded_byte_size = 8 };
enum _discriminator
{
discriminator_a = 0,
discriminator_b = 1
} discriminator;
static const prophy::detail::int2type<discriminator_a> discriminator_a_t;
static const prophy::detail::int2type<discriminator_b> discriminator_b_t;
uint32_t a;
Test2 b;
Test7(): discriminator(discriminator_a), a() { }
Test7(prophy::detail::int2type<discriminator_a>, uint32_t _1): discriminator(discriminator_a), a(_1) { }
Test7(prophy::detail::int2type<discriminator_b>, const Test2& _1): discriminator(discriminator_b), b(_1) { }
size_t get_byte_size() const
{
return 8;
}
};
discriminator_<field_name>_t
variables are meant to facilitate C++11 brace-enclosed
initialization:
Test7 x{Test7::discriminator_a_t, 42};
Test7 y{Test7::discriminator_b_t, {13}};