Mercurial > code
changeset 292:30a969b658c1
MFD
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 13 Nov 2014 13:55:36 +0100 |
parents | 8847c0acd944 (current diff) f4fc723429fe (diff) |
children | 8af39c019eb6 |
files | |
diffstat | 4 files changed, 789 insertions(+), 209 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/Pack.cpp Thu Nov 13 13:23:53 2014 +0100 +++ b/C++/Pack.cpp Thu Nov 13 13:55:36 2014 +0100 @@ -31,3 +31,85 @@ } // !namespace const Pack::Endian Pack::mode = checkMode(); + +/* -------------------------------------------------------- + * PackReader + * -------------------------------------------------------- */ + +PackReader::PackReader(Pack::Endian endian) + : m_endian(endian) +{ +} + +/* -------------------------------------------------------- + * PackFileReader + * -------------------------------------------------------- */ + +PackFileReader::PackFileReader(const std::string &path, Pack::Endian endian) + : PackReader(endian) +{ + m_in.open(path, std::ifstream::in); +} + +std::istream &PackFileReader::stream() +{ + return m_in; +} + +/* -------------------------------------------------------- + * PackStringReader + * -------------------------------------------------------- */ + +PackStringReader::PackStringReader(std::string input, Pack::Endian endian) + : PackReader(endian) + , m_in(std::move(input)) +{ +} + +std::istream &PackStringReader::stream() +{ + return m_in; +} + +/* -------------------------------------------------------- + * PackWriter + * -------------------------------------------------------- */ + +PackWriter::PackWriter(Pack::Endian endian) + : m_endian(endian) +{ +} + +/* -------------------------------------------------------- + * PackFileWriter + * -------------------------------------------------------- */ + +PackFileWriter::PackFileWriter(const std::string &path, Pack::Endian endian) + : PackWriter(endian) +{ + m_out.open(path, std::ofstream::out); +} + +std::ostream &PackFileWriter::stream() +{ + return m_out; +} + +/* -------------------------------------------------------- + * PackStringWriter + * -------------------------------------------------------- */ + +PackStringWriter::PackStringWriter(Pack::Endian endian) + : PackWriter(endian) +{ +} + +std::ostream &PackStringWriter::stream() +{ + return m_out; +} + +std::string PackStringWriter::buffer() const +{ + return m_out.str(); +}
--- a/C++/Pack.h Thu Nov 13 13:23:53 2014 +0100 +++ b/C++/Pack.h Thu Nov 13 13:55:36 2014 +0100 @@ -21,7 +21,8 @@ #include <cstdint> #include <fstream> -#include <stdexcept> +#include <memory> +#include <sstream> #include <string> /** @@ -36,6 +37,25 @@ * uint64_t */ class Pack { +private: + template <typename T> + struct IsContainer + { + using Yes = char [1]; + using No = char [2]; + + template <typename U> + static Yes &test(typename U::value_type *); + + template <typename U> + static No &test(...); + + static constexpr const bool value = sizeof (test<T>(0)) == sizeof (Yes); + }; + + friend class PackWriter; + friend class PackReader; + public: /** * @enum Endian @@ -60,146 +80,311 @@ */ template <typename T> struct TypeInfo { - static const bool supported = false; + static constexpr const bool convertible{false}; + static constexpr const bool serializable{false}; + }; + + /** + * Helper to mark a specialization convertible. + * + * Already done for: + * uint8_t + * uint16_t + * uint32_t + * uint64_t + * + * The specialization must have the following function: + * + * static constexpr void convert(T &value) noexcept + */ + struct Convertible { + static constexpr const bool convertible{true}; + }; + + /** + * Helper to mark a specialization serializable. + * + * The specialisation must have the following functions: + * + * static void serialize(PackWriter &writer, const T &) + * static void unserialize(PackReader &reader, T &) + */ + struct Serializable { + static constexpr const bool serializable{true}; }; /** - * Convert data only if the requested endian is different. + * Convert data inplace. * - * @param value the value to convert if needed - * @param endian the endian mode - * @return the converted value + * @param value the value */ template <typename T> - static inline T convert(T value, Endian endian) + static inline void convert(T &value) noexcept { - static_assert(TypeInfo<T>::supported, "unsupported type"); + static_assert(TypeInfo<T>::convertible, "unsupported type"); + + TypeInfo<T>::convert(value); + } +}; + +/** + * @class PackReader + * @brief Base abstract reader class + */ +class PackReader { +protected: + Pack::Endian m_endian; + + PackReader(Pack::Endian endian); + + virtual std::istream &stream() = 0; - if (endian != mode) - return TypeInfo<T>::convert(value); +public: + /** + * Default destructor. + */ + virtual ~PackReader() = default; - return value; + /** + * Read a primitive convertible type. + * + * @param value the value destination + * @return *this + */ + template <typename T, typename std::enable_if<Pack::TypeInfo<T>::convertible>::type * = nullptr> + PackReader &operator>>(T &value) + { + stream().read(reinterpret_cast<char *>(&value), sizeof (T)); + + if (m_endian != Pack::mode) + Pack::convert(value); + + return *this; } /** - * Write nothing, stop recursion. - */ - static void write(std::ostream &, Endian) - { - } - - /** - * Write binary data to the stream. + * Read a serializable type. * - * @param out the output stream - * @param endian the endian mode - * @param args the arguments - * @throw std::runtime_exception on error + * @param value the value destination + * @return *this */ - template <typename T, typename... Args> - static void write(std::ostream &out, Endian endian, const T &value, const Args&... args) + template <typename T, typename std::enable_if<Pack::TypeInfo<T>::serializable>::type * = nullptr> + PackReader &operator>>(T &value) { - auto ret = convert(value, endian); - out.write(reinterpret_cast<const char *>(&ret), TypeInfo<T>::size); - write(out, endian, args...); - } + Pack::TypeInfo<T>::unserialize(*this, value); - /** - * Read nothing, stop recursion. - */ - static void read(std::istream &, Endian) - { + return *this; } /** - * Read binary data from the stream. + * Read an array. + * + * This operator is a little bit tricky because you don't know in + * advance how much data you want to read. Because of that, this + * function looks the capacity of the container and reads that number + * of data. * - * @param in the input stream - * @param endian the endian mode - * @param args the arguments - * @throw std::runtime_exception on error + * Because it looks for capacity, you can't use a container which + * already have some data, they will be overriden. + * + * If this is a concern, you should roll your own loop to fill up + * your container. + * + * @param container the container (all previous data will be lost) + * @return *this */ - template <typename T, typename... Args> - static void read(std::istream &in, Endian endian, T &value, Args&... args) + template <typename T, typename std::enable_if<Pack::IsContainer<T>::value>::type * = nullptr> + PackReader &operator>>(T &container) { - in.read(reinterpret_cast<char *>(&value), TypeInfo<T>::size); - value = convert(value, endian); - read(in, endian, args...); + typename T::value_type v; + + T copy; + + for (size_t i = 0; i < container.capacity(); ++i) { + (*this) >> v; + copy.push_back(v); + } + + container = std::move(copy); + + return *this; + } +}; + +/** + * @class PackWriter + * @brief Base abstract writer class + */ +class PackWriter { +protected: + Pack::Endian m_endian; + + PackWriter(Pack::Endian endian); + + virtual std::ostream &stream() = 0; + +public: + /** + * Default destructor. + */ + virtual ~PackWriter() = default; + + /** + * Write a convertible type to the stream. + * + * @param value the value + * @return *this + */ + template <typename T, typename std::enable_if<Pack::TypeInfo<T>::convertible>::type * = nullptr> + PackWriter &operator<<(T value) + { + if (m_endian != Pack::mode) + Pack::convert(value); + + stream().write(reinterpret_cast<const char *>(&value), sizeof (T)); + + return *this; + } + + /** + * Write a serializable type to the stream. + * + * @param value the value + * @return *this + */ + template <typename T, typename std::enable_if<Pack::TypeInfo<T>::serializable>::type * = nullptr> + PackWriter &operator<<(const T &value) + { + Pack::TypeInfo<T>::serialize(*this, value); + + return *this; } /** - * Read array and store it to the output iterator. Because the container - * is not allocated, we use an integer based size to determine the - * number of bytes to read. + * Write a container to the stream. * - * @param in the input stream - * @param endian the endian mode - * @param out the output it - * @param count the number of bytes to read - * @throw std::runtime_error on error + * @param container the container + * @return *this + */ + template <typename T, typename std::enable_if<Pack::IsContainer<T>::value>::type * = nullptr> + PackWriter &operator<<(const T &container) + { + for (const auto &v : container) + (*this) << v; + + return *this; + } +}; + +/** + * @class PackFileReader + * @brief Extract binary data from a file + */ +class PackFileReader : public PackReader { +private: + std::ifstream m_in; + +protected: + std::istream &stream() override; + +public: + /** + * Read a file. + * + * @param path the path + * @param endian the endian requested */ - template <typename OutputIt> - static void readArray(std::istream &in, Endian endian, OutputIt out, unsigned count) - { - typename OutputIt::container_type::value_type byte; + PackFileReader(const std::string &path, Pack::Endian endian); +}; + +/** + * @class PackStringReader + * @brief Extract binary data from a string + */ +class PackStringReader : public PackReader { +private: + std::istringstream m_in; + + std::istream &stream() override; + +public: + /** + * Read a string. + * + * @param input the input string + * @param endian the endian requested + */ + PackStringReader(std::string input, Pack::Endian endian); +}; + +/** + * @class PackFileWriter + * @brief Write binary data to a string + */ +class PackFileWriter : public PackWriter { +private: + std::ofstream m_out; - for (unsigned i = 0; i < count; ++i) { - read(in, endian, byte); - *out++ = byte; - } - } +protected: + std::ostream &stream() override; + +public: + /** + * Write to a file. + * + * @param path the path + * @param endian the endian requested + */ + PackFileWriter(const std::string &path, Pack::Endian endian); +}; + +/** + * @class PackStringWriter + * @brief Write binary data to a string + */ +class PackStringWriter : public PackWriter { +private: + std::ostringstream m_out; + + std::ostream &stream() override; + +public: + /** + * Write to a string + * + * @param endian the endian requested + */ + PackStringWriter(Pack::Endian endian); /** - * Read a container from an input iterator and write it to output stream. + * The current buffer. Returns a copy of the string. * - * @param out the output stream - * @param endian the endian mode - * @param in the input iterator - * @param count the number of bytes to write - * @throw std::runtime_error on error + * @return the string */ - template <typename InputIt> - static void writeArray(std::ostream &out, Endian endian, InputIt in, unsigned count) - { - typename std::iterator_traits<InputIt>::value_type byte; + std::string buffer() const; +}; - for (unsigned i = 0; i < count; ++i) { - byte = *in++; - write(out, endian, byte); - } +template <> +struct Pack::TypeInfo<uint8_t> : public Pack::Convertible { + static constexpr void convert(uint8_t &) noexcept + { + // uint8_t are endian independent } }; template <> -struct Pack::TypeInfo<uint8_t> { - static constexpr const bool supported = true; - static constexpr const size_t size = sizeof (uint8_t); - - static constexpr uint8_t convert(uint8_t v) +struct Pack::TypeInfo<uint16_t> : public Pack::Convertible { + static constexpr void convert(uint16_t &v) { - return v; + v = (((v >> 8) & 0x00FFL) | ((v << 8) & 0xFF00L)); } }; template <> -struct Pack::TypeInfo<uint16_t> { - static constexpr const bool supported = true; - static constexpr const size_t size = sizeof (uint16_t); - - static constexpr uint16_t convert(uint16_t v) +struct Pack::TypeInfo<uint32_t> : public Pack::Convertible { + static constexpr void convert(uint32_t &v) { - return (((v >> 8) & 0x00FFL) | ((v << 8) & 0xFF00L)); - } -}; - -template <> -struct Pack::TypeInfo<uint32_t> { - static constexpr const bool supported = true; - static constexpr const size_t size = sizeof (uint32_t); - - static constexpr uint32_t convert(uint32_t v) - { - return ((((v) >> 24) & 0x000000FFL) + v = ((((v) >> 24) & 0x000000FFL) | (((v) >> 8) & 0x0000FF00L) | (((v) << 8) & 0x00FF0000L) | (((v) << 24) & 0xFF000000L)); @@ -207,13 +392,10 @@ }; template <> -struct Pack::TypeInfo<uint64_t> { - static constexpr const bool supported = true; - static constexpr const size_t size = sizeof (uint64_t); - - static constexpr uint64_t convert(uint64_t v) +struct Pack::TypeInfo<uint64_t> : public Pack::Convertible { + static constexpr void convert(uint64_t &v) { - return ((((v) & 0xff00000000000000ull) >> 56) + v = ((((v) & 0xff00000000000000ull) >> 56) | (((v) & 0x00ff000000000000ull) >> 40) | (((v) & 0x0000ff0000000000ull) >> 24) | (((v) & 0x000000ff00000000ull) >> 8 )
--- a/C++/Tests/Pack/main.cpp Thu Nov 13 13:23:53 2014 +0100 +++ b/C++/Tests/Pack/main.cpp Thu Nov 13 13:55:36 2014 +0100 @@ -23,6 +23,31 @@ #include <Pack.h> +struct Point +{ + uint32_t width{}; + uint32_t height{}; + + inline bool operator==(const Point &other) const + { + return width == other.width && height == other.height; + } +}; + +template <> +struct Pack::TypeInfo<Point> : public Pack::Serializable +{ + static void serialize(PackWriter &writer, const Point &point) + { + writer << point.width << point.height; + } + + static void unserialize(PackReader &reader, Point &point) + { + reader >> point.width >> point.height; + } +}; + TEST(File, simpleLittleEndian) { uint8_t u8(1), r8; @@ -30,16 +55,16 @@ uint32_t u32(3), r32; uint64_t u64(4), r64; + remove("output.bin"); + try { - std::ofstream out; - out.open("simple-little.bin", std::ostream::out); - Pack::write(out, Pack::Little, u8, u16, u32, u64); - out.close(); + { + PackFileWriter writer{"output.bin", Pack::Little}; + writer << u8 << u16 << u32 << u64; + } - std::ifstream in; - in.open("simple-little.bin", std::ostream::in); - Pack::read(in, Pack::Little, r8, r16, r32, r64); - in.close(); + PackFileReader reader{"output.bin", Pack::Little}; + reader >> r8 >> r16 >> r32 >> r64; ASSERT_EQ(u8, r8); ASSERT_EQ(u16, r16); @@ -57,16 +82,84 @@ uint32_t u32(3), r32; uint64_t u64(4), r64; + remove("output.bin"); + try { - std::ofstream out; - out.open("simple-big.bin", std::ostream::out); - Pack::write(out, Pack::Big, u8, u16, u32, u64); - out.close(); + { + PackFileWriter writer{"output.bin", Pack::Big}; + writer << u8 << u16 << u32 << u64; + } + + PackFileReader reader{"output.bin", Pack::Big}; + reader >> r8 >> r16 >> r32 >> r64; + + ASSERT_EQ(u8, r8); + ASSERT_EQ(u16, r16); + ASSERT_EQ(u32, r32); + ASSERT_EQ(u64, r64); + } catch (const std::exception &error) { + FAIL() << error.what(); + } +} + +TEST(File, arrayLittleEndian) +{ + std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 }; + std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 }; + std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 }; + std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 }; + + std::vector<uint8_t> r8(8); + std::vector<uint16_t> r16(8); + std::vector<uint32_t> r32(8); + std::vector<uint64_t> r64(8); + + remove("output.bin"); + + try { + { + PackFileWriter writer{"output.bin", Pack::Little}; + + writer << u8 << u16 << u32 << u64; + } - std::ifstream in; - in.open("simple-big.bin", std::ostream::in); - Pack::read(in, Pack::Big, r8, r16, r32, r64); - in.close(); + PackFileReader reader{"output.bin", Pack::Little}; + + reader >> r8 >> r16 >> r32 >> r64; + + ASSERT_EQ(u8, r8); + ASSERT_EQ(u16, r16); + ASSERT_EQ(u32, r32); + ASSERT_EQ(u64, r64); + } catch (const std::exception &error) { + FAIL() << error.what(); + } +} + +TEST(File, arrayBigEndian) +{ + std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 }; + std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 }; + std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 }; + std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 }; + + std::vector<uint8_t> r8(8); + std::vector<uint16_t> r16(8); + std::vector<uint32_t> r32(8); + std::vector<uint64_t> r64(8); + + remove("output.bin"); + + try { + { + PackFileWriter writer{"output.bin", Pack::Big}; + + writer << u8 << u16 << u32 << u64; + } + + PackFileReader reader{"output.bin", Pack::Big}; + + reader >> r8 >> r16 >> r32 >> r64; ASSERT_EQ(u8, r8); ASSERT_EQ(u16, r16); @@ -77,6 +170,102 @@ } } +TEST(File, serializeSimpleLittleEndian) +{ + Point point{200, 400}; + Point result; + + remove("output.bin"); + + try { + { + PackFileWriter writer{"output.bin", Pack::Little}; + + writer << point; + } + + PackFileReader reader{"output.bin", Pack::Little}; + + reader >> result; + + ASSERT_EQ(point, result); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } +} + +TEST(File, serializeSimpleBigEndian) +{ + Point point{200, 400}; + Point result; + + remove("output.bin"); + + try { + { + PackFileWriter writer{"output.bin", Pack::Big}; + + writer << point; + } + + PackFileReader reader{"output.bin", Pack::Big}; + + reader >> result; + + ASSERT_EQ(point, result); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } +} + +TEST(File, serializeArrayLittleEndian) +{ + std::vector<Point> points{{10, 20}, {30, 40}}; + std::vector<Point> rpoints(2); + + remove("output.bin"); + + try { + { + PackFileWriter writer{"output.bin", Pack::Little}; + + writer << points; + } + + PackFileReader reader{"output.bin", Pack::Little}; + + reader >> rpoints; + + ASSERT_EQ(points, rpoints); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } +} + +TEST(File, serializeArrayBigEndian) +{ + std::vector<Point> points{{10, 20}, {30, 40}}; + std::vector<Point> rpoints(2); + + remove("output.bin"); + + try { + { + PackFileWriter writer{"output.bin", Pack::Big}; + + writer << points; + } + + PackFileReader reader{"output.bin", Pack::Big}; + + reader >> rpoints; + + ASSERT_EQ(points, rpoints); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } +} + TEST(String, simpleLittleEndian) { uint8_t u8(1), r8; @@ -85,11 +274,16 @@ uint64_t u64(4), r64; try { - std::ostringstream out; - Pack::write(out, Pack::Little, u8, u16, u32, u64); + std::string input; - std::istringstream in(out.str()); - Pack::read(in, Pack::Little, r8, r16, r32, r64); + { + PackStringWriter writer{Pack::Little}; + writer << u8 << u16 << u32 << u64; + input = writer.buffer(); + } + + PackStringReader reader{std::move(input), Pack::Little}; + reader >> r8 >> r16 >> r32 >> r64; ASSERT_EQ(u8, r8); ASSERT_EQ(u16, r16); @@ -108,11 +302,86 @@ uint64_t u64(4), r64; try { - std::ostringstream out; - Pack::write(out, Pack::Big, u8, u16, u32, u64); + std::string input; + + { + PackStringWriter writer{Pack::Big}; + writer << u8 << u16 << u32 << u64; + input = writer.buffer(); + } + + PackStringReader reader{std::move(input), Pack::Big}; + reader >> r8 >> r16 >> r32 >> r64; + + ASSERT_EQ(u8, r8); + ASSERT_EQ(u16, r16); + ASSERT_EQ(u32, r32); + ASSERT_EQ(u64, r64); + } catch (const std::exception &error) { + FAIL() << error.what(); + } +} + +TEST(String, arrayLittleEndian) +{ + std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 }; + std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 }; + std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 }; + std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 }; + + std::vector<uint8_t> r8(8); + std::vector<uint16_t> r16(8); + std::vector<uint32_t> r32(8); + std::vector<uint64_t> r64(8); + + try { + std::string input; + + { + PackStringWriter writer{Pack::Little}; - std::istringstream in(out.str()); - Pack::read(in, Pack::Big, r8, r16, r32, r64); + writer << u8 << u16 << u32 << u64; + input = writer.buffer(); + } + + PackStringReader reader{std::move(input), Pack::Little}; + + reader >> r8 >> r16 >> r32 >> r64; + + ASSERT_EQ(u8, r8); + ASSERT_EQ(u16, r16); + ASSERT_EQ(u32, r32); + ASSERT_EQ(u64, r64); + } catch (const std::exception &error) { + FAIL() << error.what(); + } +} + +TEST(String, arrayBigEndian) +{ + std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 }; + std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 }; + std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 }; + std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 }; + + std::vector<uint8_t> r8(8); + std::vector<uint16_t> r16(8); + std::vector<uint32_t> r32(8); + std::vector<uint64_t> r64(8); + + try { + std::string input; + + { + PackStringWriter writer{Pack::Big}; + + writer << u8 << u16 << u32 << u64; + input = writer.buffer(); + } + + PackStringReader reader{std::move(input), Pack::Big}; + + reader >> r8 >> r16 >> r32 >> r64; ASSERT_EQ(u8, r8); ASSERT_EQ(u16, r16); @@ -123,73 +392,103 @@ } } -TEST(Array, simpleLittleEndian) +TEST(String, serializeSimpleLittleEndian) { - std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 }; - std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 }; - std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 }; - std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 }; - - std::vector<uint8_t> r8; - std::vector<uint16_t> r16; - std::vector<uint32_t> r32; - std::vector<uint64_t> r64; + Point point{200, 400}; + Point result; try { - std::ostringstream out; - Pack::writeArray(out, Pack::Little, u8.cbegin(), u8.size()); - Pack::writeArray(out, Pack::Little, u16.cbegin(), u16.size()); - Pack::writeArray(out, Pack::Little, u32.cbegin(), u32.size()); - Pack::writeArray(out, Pack::Little, u64.cbegin(), u64.size()); + std::string input; + + { + PackStringWriter writer{Pack::Little}; - std::istringstream in(out.str()); - Pack::readArray(in, Pack::Little, std::back_inserter(r8), 8); - Pack::readArray(in, Pack::Little, std::back_inserter(r16), 8); - Pack::readArray(in, Pack::Little, std::back_inserter(r32), 8); - Pack::readArray(in, Pack::Little, std::back_inserter(r64), 8); + writer << point; + input = writer.buffer(); + } + PackStringReader reader{std::move(input), Pack::Little}; - ASSERT_EQ(u8, r8); - ASSERT_EQ(u16, r16); - ASSERT_EQ(u32, r32); - ASSERT_EQ(u64, r64); - } catch (const std::exception &error) { - FAIL() << error.what(); + reader >> result; + + ASSERT_EQ(point, result); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; } } -TEST(Array, simpleBigEndian) +TEST(String, serializeSimpleBigEndian) { - std::vector<uint8_t> u8 { 1, 2, 3, 4, 5, 6, 7, 8 }; - std::vector<uint16_t> u16 { 10, 20, 30, 40, 50, 60, 70, 80 }; - std::vector<uint32_t> u32 { 100, 200, 300, 400, 500, 600, 700, 800 }; - std::vector<uint64_t> u64 { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 }; + Point point{200, 400}; + Point result; + + try { + std::string input; + + { + PackStringWriter writer{Pack::Big}; + + writer << point; + input = writer.buffer(); + } - std::vector<uint8_t> r8; - std::vector<uint16_t> r16; - std::vector<uint32_t> r32; - std::vector<uint64_t> r64; + PackStringReader reader{std::move(input), Pack::Big}; + + reader >> result; + + ASSERT_EQ(point, result); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } +} + +TEST(String, serializeArrayLittleEndian) +{ + std::vector<Point> points{{10, 20}, {30, 40}}; + std::vector<Point> rpoints(2); try { - std::ostringstream out; - Pack::writeArray(out, Pack::Big, u8.cbegin(), u8.size()); - Pack::writeArray(out, Pack::Big, u16.cbegin(), u16.size()); - Pack::writeArray(out, Pack::Big, u32.cbegin(), u32.size()); - Pack::writeArray(out, Pack::Big, u64.cbegin(), u64.size()); + std::string input; + + { + PackStringWriter writer{Pack::Little}; + + writer << points; + input = writer.buffer(); + } + + PackStringReader reader{std::move(input), Pack::Little}; + + reader >> rpoints; + + ASSERT_EQ(points, rpoints); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; + } +} - std::istringstream in(out.str()); - Pack::readArray(in, Pack::Big, std::back_inserter(r8), 8); - Pack::readArray(in, Pack::Big, std::back_inserter(r16), 8); - Pack::readArray(in, Pack::Big, std::back_inserter(r32), 8); - Pack::readArray(in, Pack::Big, std::back_inserter(r64), 8); +TEST(String, serializeArrayBigEndian) +{ + std::vector<Point> points{{10, 20}, {30, 40}}; + std::vector<Point> rpoints(2); + try { + std::string input; + + { + PackStringWriter writer{Pack::Big}; - ASSERT_EQ(u8, r8); - ASSERT_EQ(u16, r16); - ASSERT_EQ(u32, r32); - ASSERT_EQ(u64, r64); - } catch (const std::exception &error) { - FAIL() << error.what(); + writer << points; + input = writer.buffer(); + } + + PackStringReader reader{std::move(input), Pack::Big}; + + reader >> rpoints; + + ASSERT_EQ(points, rpoints); + } catch (const std::exception &ex) { + std::cerr << "warning: " << ex.what() << std::endl; } }
--- a/C++/TreeNode.h Thu Nov 13 13:23:53 2014 +0100 +++ b/C++/TreeNode.h Thu Nov 13 13:55:36 2014 +0100 @@ -52,14 +52,17 @@ template <typename U> class HasEqualsTo { public: + using Yes = char [2]; + using No = char [1]; + static_assert(sizeof (char) != sizeof (long), "buy a new compiler"); template <typename Value> - static constexpr auto check(Value *u) -> decltype(*u == *u, char(0)); + static constexpr Yes &check(Value *u, decltype(*u == *u) * = nullptr); - static constexpr long check(...); + static constexpr No &check(...); - static constexpr const bool value = sizeof (check((U *)0)) == sizeof (char); + static constexpr const bool value = sizeof (check((U *)0)) == sizeof (Yes); }; public: @@ -78,39 +81,53 @@ template <typename T> class TreeNode { private: - struct Object { - std::unique_ptr<T> value; - + class Object { + public: + virtual T &get() = 0; virtual std::unique_ptr<Object> clone() const = 0; }; - template <typename U> + template <typename Value> struct Proxy final : public Object { - inline Proxy(U &&u) + private: + Value m_value; + + Proxy(const Proxy &) = delete; + Proxy &operator=(const Proxy &) = delete; + Proxy(Proxy &&) = delete; + Proxy &operator=(Proxy &&) = delete; + + public: + inline Proxy(Value &&value) + : m_value(std::move(value)) { - this->value = std::make_unique<U>(std::move(u)); } - inline Proxy(const U &u) + inline Proxy(const Value &value) + : m_value(value) { - this->value = std::make_unique<U>(u); } template <typename... Args> inline Proxy(Args&&... args) + : m_value(std::forward<Args>(args)...) { - this->value = std::make_unique<U>(std::forward<Args>(args)...); + } + + T &get() override + { + return m_value; } std::unique_ptr<Object> clone() const override { - return std::make_unique<Proxy<U>>(static_cast<const U &>(*this->value)); + return std::make_unique<Proxy<Value>>(m_value); } }; using Container = std::deque<std::unique_ptr<Object>>; - TreeNode *m_parent { nullptr }; + TreeNode *m_parent{nullptr}; Container m_children; public: @@ -134,7 +151,7 @@ { for (const auto &c : other.m_children) { m_children.push_back(c->clone()); - m_children.back()->value->m_parent = this; + m_children.back()->get().m_parent = this; } } @@ -149,7 +166,7 @@ { // Update children to update to *this for (auto &c : m_children) - c->value->m_parent = this; + c->get().m_parent = this; other.m_children.clear(); } @@ -165,7 +182,7 @@ for (const auto &c : other.m_children) { m_children.push_back(c->clone()); - m_children.back()->value->m_parent = this; + m_children.back()->get().m_parent = this; } return *this; @@ -182,7 +199,7 @@ // Update children to update to *this for (auto &c : m_children) - c->value->m_parent = this; + c->get().m_parent = this; other.m_children.clear(); @@ -198,7 +215,7 @@ inline void push(const Value &child) { m_children.push_front(std::make_unique<Proxy<Value>>(child)); - m_children.front()->value->m_parent = this; + m_children.front()->get().m_parent = this; } /** @@ -212,7 +229,7 @@ using Type = typename std::decay<Value>::type; m_children.push_front(std::make_unique<Proxy<Type>>(std::move(child))); - m_children.front()->value->m_parent = this; + m_children.front()->get().m_parent = this; } /** @@ -224,7 +241,7 @@ void pushNew(Args&&... args) { m_children.emplace_front(std::make_unique<Proxy<T>>(std::forward<Args>(args)...)); - m_children.front()->value->m_parent = this; + m_children.front()->get().m_parent = this; } /** @@ -236,7 +253,7 @@ inline void append(const Value &child) { m_children.push_back(std::make_unique<Proxy<Value>>(child)); - m_children.back()->value->m_parent = this; + m_children.back()->get().m_parent = this; } /** @@ -250,7 +267,7 @@ using Type = typename std::decay<Value>::type; m_children.push_back(std::make_unique<Proxy<Type>>(std::move(child))); - m_children.back()->value->m_parent = this; + m_children.back()->get().m_parent = this; } /** @@ -262,7 +279,7 @@ void appendNew(Args&&... args) { m_children.emplace_back(std::make_unique<Proxy<T>>(std::forward<Args>(args)...)); - m_children.back()->value->m_parent = this; + m_children.back()->get().m_parent = this; } /** @@ -347,7 +364,7 @@ void remove(T &value) { m_children.erase(std::remove_if(m_children.begin(), m_children.end(), [&] (auto &p) { - return p->value.get() == &value; + return &p->get() == &value; }), m_children.end()); } @@ -361,7 +378,7 @@ void removeSame(const Value &value, typename std::enable_if<TypeTraits<Value>::equalityComparable>::type * = nullptr) { m_children.erase(std::remove_if(m_children.begin(), m_children.end(), [&] (auto &p) { - return *p->value == value; + return p->get() == value; }), m_children.end()); } @@ -383,7 +400,7 @@ int indexOf(const T &child) const noexcept { for (unsigned i = 0; i < m_children.size(); ++i) - if (m_children[i]->value.get() == &child) + if (&m_children[i]->get() == &child) return i; return -1; @@ -400,7 +417,7 @@ int indexOfSame(const Value &value, typename std::enable_if<TypeTraits<Value>::equalityComparable>::type * = nullptr) const noexcept { for (unsigned i = 0; i < m_children.size(); ++i) - if (*m_children[i]->value == value) + if (m_children[i]->get() == value) return i; return -1; @@ -415,7 +432,7 @@ */ T &operator[](int index) { - return static_cast<T &>(*m_children.at(index)->value); + return static_cast<T &>(m_children.at(index)->get()); } /** @@ -427,7 +444,7 @@ */ const T &operator[](int index) const { - return static_cast<const T &>(*m_children.at(index)->value); + return static_cast<const T &>(m_children.at(index)->get()); } };