From 9bb990c9a663edc43aebb87ed84b00e6d90685c0 Mon Sep 17 00:00:00 2001 From: Adrian Kummerlaender Date: Tue, 17 Jan 2017 20:44:31 +0100 Subject: Update markdown syntax to use pandoc's peculiarities --- ...res_as_tuples_using_template_metaprogramming.md | 35 +++++++++------------- 1 file changed, 14 insertions(+), 21 deletions(-) (limited to 'articles/2013-11-03_mapping_binary_structures_as_tuples_using_template_metaprogramming.md') diff --git a/articles/2013-11-03_mapping_binary_structures_as_tuples_using_template_metaprogramming.md b/articles/2013-11-03_mapping_binary_structures_as_tuples_using_template_metaprogramming.md index 5c82f05..e2b4784 100644 --- a/articles/2013-11-03_mapping_binary_structures_as_tuples_using_template_metaprogramming.md +++ b/articles/2013-11-03_mapping_binary_structures_as_tuples_using_template_metaprogramming.md @@ -17,7 +17,7 @@ differences in endianness and In-place modification of the structure fields. To be able to easily work with structure definitions using template metaprogramming I am relying on the standard library's [_std::tuple_](http://en.cppreference.com/w/cpp/utility/tuple) template. -~~~ +```cpp template class BinaryMapping { public: @@ -42,8 +42,7 @@ class BinaryMapping { Tuple tuple_; }; -~~~ -{: .language-cpp} +``` This implementation of a template class _BinaryMapping_ provides _get_ and _set_ template methods for accessing values in a given binary buffer using the mapping provided by a given Tuple template argument. The most notable element of this class is the usage of the _decltype_ keyword which was introduced in C++11. This keyword makes it easier to declare types @@ -55,7 +54,7 @@ as its return type is also dependent on the template arguments. As you may have noticed the above template class is not complete as I have not included a implementation of the _TupleReader::read_ method which does the actual work of mapping the binary buffer as a tuple. This mapping is achieved by the following recursive template methods: -~~~ +```cpp struct TupleReader { template static inline typename std::enable_if< @@ -77,8 +76,7 @@ struct TupleReader { ); } }; -~~~ -{: .language-cpp} +``` Template metaprogramming in C++ offers a Turing-complete language which is fully executed during compilation. This means that any problem we may solve during the runtime of a _normal_ program may also be solved during compilation using template metaprogramming techniques. This kind of programming is comparable to functional programming as we have to rely on recursion and pattern @@ -106,7 +104,7 @@ is always available as a template argument. The classes _TupleReader_ and _BinaryMapping_ are enough to map a binary structure as a _std::tuple_ instantiation like in the following example where I define a _TestRecord_ tuple containing a pointer to _uint64\_t_ and _uint16\_t_ integers: -~~~ +```cpp typedef std::tuple TestRecord; uint8_t* buffer = reinterpret_cast( @@ -120,8 +118,7 @@ mapping.set<1>(1337); std::cout << mapping.get<0>() << std::endl; std::cout << mapping.get<1>() << std::endl; -~~~ -{: .language-cpp} +``` ## Endianness @@ -129,7 +126,7 @@ As you may remember this does not take endianness into account as I defined as a _BinaryMapping_ template class which worked, but led to problems as soon as I mixed calls to _get_ and _set_. The resulting problems could of course have been fixed but this would probably have conflicted with In-place modifications of the buffer. Because of that I chose to implement endianness support in a separate set of templates. -~~~ +```cpp struct BigEndianSorter { template static void write(uint8_t*, const Key&); @@ -137,25 +134,23 @@ struct BigEndianSorter { template static Key read(uint8_t* buffer); }; -~~~ -{: .language-cpp} +``` To be able to work with different byte orderings I abstracted the basic operations down to _read_ and _write_ template methods contained in a _struct_ so I would be able to provide separate implementations of these methods for each type of endianness. The following template specialization of the _write_ method which does an In-place reordering of a _uint64\_t_ should be enough to understand the basic principle: -~~~ +```cpp template <> void BigEndianSorter::write(uint8_t* buffer, const uint64_t& number) { *reinterpret_cast(buffer) = htobe64(number); } -~~~ -{: .language-cpp} +``` As soon as I had the basic endianness conversion methods implemented in a manner which could be used to specialize other template classes I was able to build a generic implementation of a serializer which respects the structure defined by a given _std::tuple_ instantiation: -~~~ +```cpp template struct Serializer { template @@ -205,8 +200,7 @@ struct Serializer { ); } }; -~~~ -{: .language-cpp} +``` It should be evident that the way both the _serialize_ and _deserialize_ template methods are implemented is very similar to the _TupleReader_ implementation. In fact the only difference is that no actual _std::tuple_ instantiation instance is touched and instead of setting pointers to the buffer we are only reordering the bytes of each section of the buffer corresponding to @@ -216,7 +210,7 @@ a tuple element. This results in a complete In-place conversion between differen At last I am now able to do everything I planned in the beginning in a very compact way using the _Serializer_, _TupleReader_ and _BinaryMapping_ templates. In practice this now looks like this: -~~~ +```cpp typedef std::tuple TestRecord; uint8_t* buffer = reinterpret_cast( @@ -245,8 +239,7 @@ std::cout << testMapping.get<1>() << std::endl; std::free(buffer); std::free(testBuffer); -~~~ -{: .language-cpp} +``` The above coding makes use of all features provided by the described templates by first setting two values using _BinaryMapping_ specialized on the _TestRecord_ tuple, serializing them using _Serializer_ specialized on the _BigEndianSorter_, deserializing the buffer back to the host byte ordering and reading the values using another _BinaryMapping_. -- cgit v1.2.3