c++ - Function taking a variable list of arguments and converting them to a series of bytes? -
i want create function, pack()
, takes variable list of arguments , converts them series of bytes, e.g., std::vector
.
given char c = 0x10, int x = 4, char *s = "aaa"
, pack()
, should behave like:
pack(c, x, s) = 0x10, 0x04, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41
.
(here assume little-endian byte ordering)
how program such function?
i've been thinking c's va_list
or c++'s template mechanisms, i've trouble implementing this.
what "best" way of programming such function? code snippets demonstrating suitable technique?
let me share solution. advantage on proposed once work types: fundamental types, static arrays, custom objects, containers (vector, list, string...), c-strings (both literal , dynamically allocated).
if want limit types (say, not allow packing pointers) can add more sfinae :) or static_assert
...
// byte_pack.h #include <vector> #include <type_traits> // small trait check if possible iterate on t template<typename t, typename = void> constexpr bool is_iterable = false; template<typename t> constexpr bool is_iterable<t, decltype( std::begin(std::declval<t&>()) != std::end(std::declval<t&>()), void())> = true; typedef std::vector<std::uint8_t> byte_pack; // vector of bytes template<typename t, std::enable_if_t<(!is_iterable<t>)>* = nullptr> void pack(byte_pack& bytes, const t& value) // not iteratable values (int, double, custom objects, etc.) { typedef const std::uint8_t byte_array[sizeof value]; for(auto& byte : reinterpret_cast<byte_array&>(value)) { bytes.push_back(byte); } } template<typename t, std::enable_if_t<is_iterable<t>>* = nullptr> void pack(byte_pack& bytes, const t& values) // iteratable values (string, vector, etc.) { for(const auto& value : values) { pack(bytes, value); } } template<> inline void pack(byte_pack& bytes, const char* const & c_str) // c-strings { for(auto = 0; c_str[i]; ++i) { bytes.push_back(c_str[i]); } } template<> inline void pack(byte_pack& bytes, char* const & c_str) { // c-strings pack(bytes, static_cast<const char*>(c_str)); } template<typename t, size_t n> void pack(byte_pack& bytes, const t (&values) [n]) // static arrays { for(auto = 0u; < n; ++i) { pack(bytes, values[i]); } } // variadic overload template<typename... args> byte_pack pack(const args&... args) { byte_pack bytes; int dummy[] = { 0, (pack(bytes, args), 0) ... }; return bytes; }
tests:
#include "byte_pack.h" void cout_bytes(const std::vector<std::uint8_t>& bytes) { for(unsigned byte : bytes) { std::cout << "0x" << std::setfill('0') << std::setw(2) << std::hex << byte << " "; } std::cout << std::endl; } int main() { // example char c = 0x10; int x = 4; const char* s = "aaa"; cout_bytes(pack(c, x, s)); // static arrays , iterateble objects char matrix1[2][2] = { {0x01, 0x01}, {0xff, 0xff} }; std::vector<std::vector<char>> matrix2 = { {(char) 0x01, (char) 0x01}, {(char) 0xff, (char) 0xff} }; cout_bytes(pack(matrix1, matrix2)); // strings char* str2 = new char[4] { "aaa" }; std::string str1 = "aaa"; cout_bytes(pack(str1, str2)); // custom objects (remember alignment!) struct { char = 0x01; short b = 0xff; } object1; struct { short = 0x01ff; char b = 0x01; } object2; cout_bytes(pack(object1, object2)); return 0; }
output:
0x10 0x04 0x00 0x00 0x00 0x41 0x41 0x41 0x01 0x01 0xff 0xff 0x01 0x01 0xff 0xff 0x41 0x41 0x41 0x41 0x41 0x41 0x01 0x00 0xff 0x00 0xff 0x01 0x01 0x00
Comments
Post a Comment