Okay, so, just to see how I'd do, I've tried to reach the optimum sizes I calculated on the back of my napkin:
I can see how you'd expect 57, 63, or 75 bytes
mProtocolVersion = 1*10000+14*100+4; // 2 bytes
mSessionFlags = 1; // 2 bytes
mMaxResponseLength = 0; // 2 bytes
mMake = "MyMake"; // 6 bytes + length
mModel = "MyModel"; // 7 bytes + length
mSerialNumber = "10000"; // 5 bytes + length
mTrackDelay = 0; // 1 byte
mHeadUnitModel = "Headunit"; // 8 bytes + length
mCarModelYear = "2014"; // 4 bytes + length
mVin = "1234567980"; // 10 bytes + length
mVehicleMileage = 1000; // 2 byte
mShoutFormat = 3; // 1 byte
mNotificationInterval = 1; // 1 byte
// -------------------------------------- // 51 bytes + 6 x length
In this instance, I created binary serialization code using Boost Spirit (Karma for serialization and Qi for de-serialization). I made the size of the length field configurable (8,16,32 or 64 bit unsigned).
Here's a working proof of concept: Live On Coliru
generate()
The const generate member function delegates the work to helper functions in a separate namespace:
template <typename Container>
bool generate(Container& bytes) const {
auto out = std::back_inserter(bytes);
using my_serialization_helpers::do_generate;
return do_generate(out, mProtocolVersion)
&& do_generate(out, mSessionFlags)
&& do_generate(out, mMaxResponseLength)
&& do_generate(out, mMake)
&& do_generate(out, mModel)
&& do_generate(out, mSerialNumber)
&& do_generate(out, mTrackDelay)
&& do_generate(out, mHeadUnitModel)
&& do_generate(out, mCarModelYear)
&& do_generate(out, mVin)
&& do_generate(out, mVehicleMileage)
&& do_generate(out, mShoutFormat)
&& do_generate(out, mNotificationInterval);
}
Note that
do_generate
overloads can be freely added as required for future types
- the container can easily be switched from e.g.
std::vector<unsigned char>
, to e.g. boost::interprocess::containers::string<char, char_traits<char>, boost::interprocess::allocator<char, boost::interprocess::managed_shared_memory::segment_manager> >
.
parse()
The parse method is very similar except it delegates to do_parse
overloads to do the work.
Testing
The test program roundtrips with all possible configurations:
- 8-bit length field, net 57 bytes, with boost serialization: 70
- 16-bit length field, net 63 bytes, with boost serialization: 76
- 32-bit length field, net 75 bytes, with boost serialization: 88
- 64-bit length field, net 99 bytes, with boost serialization: 112
As you can see it's not even that outrageous that the natural Boost Serialization solution would take 107 bytes on my system (it's only 8 bytes more than my last configuration).
Note also, that since the Karma generators all take any output iterator, it should be relatively easy to wire it directly into the low-level Boost Archive operations for performance and to avoid allocating intermediate storage.