#if 0 set -xe trap 'rm -f a.out' EXIT g++ -std=c++17 -Wall -Wextra -fsanitize=address -fsanitize=undefined variadic_to_common_type.cc && ./a.out | c++filt -t clang++ -std=c++17 -Wall -Wextra -fsanitize=address -fsanitize=undefined variadic_to_common_type.cc && ./a.out | c++filt -t g++ -std=c++17 -Wall -Wextra variadic_to_common_type.cc && ./a.out | c++filt -t which valgrind && valgrind ./a.out exit 0 #endif #include #include #include #include // Variadic template arguments to common type. // // This shows a method how to map variadic template arguments to a common type // which can be used further down the processing pipeline. struct bytes { const uint8_t* ptr; size_t len; }; // -- TO_BYTES CONVERTER ------------------------------------------------------- template constexpr bytes to_bytes(const T& val) { static_assert(std::is_arithmetic_v, ""); return {.ptr = reinterpret_cast(&val), .len = sizeof(val)}; } template constexpr bytes to_bytes(const T (&vals)[N]) { static_assert(std::is_arithmetic_v, ""); static_assert(sizeof(vals) == sizeof(T) * N, ""); return {.ptr = &vals, .len = sizeof(vals)}; } template constexpr bytes to_bytes(const std::array& vals) { static_assert(std::is_arithmetic_v, ""); return {.ptr = reinterpret_cast(vals.data()), .len = sizeof(T) * N}; } // -- SCALAR GENERATOR & ALIAS ------------------------------------------------- // Utility to generate scalar values of a specific type. template struct scalar { static_assert(std::is_arithmetic_v, ""); template constexpr T operator()(F val) const { static_assert(std::is_convertible_v, ""); return static_cast(val); } }; inline constexpr scalar u64; inline constexpr scalar f64; // -- ARRAY GENERATOR & ALIAS -------------------------------------------------- // Utility to generate array values of a specific type. template struct array { static_assert(std::is_arithmetic_v, ""); template constexpr std::array operator()(F... val) const { static_assert(is_convertible(), ""); return {static_cast(val)...}; } // Conversion checker helper. template static constexpr bool is_convertible() { return std::is_convertible_v; } template static constexpr bool is_convertible() { return std::is_convertible_v && is_convertible(); } }; inline constexpr array u64v; inline constexpr array f64v; // -- SERIALIZER --------------------------------------------------------------- // Example consumer of variadic arguments which are mapped to the common `bytes` // type. #include #include #include template void serialize(Args&&... args) { // auto vals = {to_bytes(args)...}; std::initializer_list vals = {to_bytes(args)...}; for (auto b : vals) { std::printf("bytes: .ptr=%p .len=%zu -> first by %u\n", b.ptr, b.len, *b.ptr); } auto vals_with_type = { std::pair{to_bytes(args), typeid(args).name()}...}; for (auto p : vals_with_type) { auto b = p.first; auto n = p.second; // Pipe to `c++filt -t` to also interpret type mangling. std::printf("bytes: .ptr=%p .len=%zu -> type=%s\n", b.ptr, b.len, n); } } // -- TEST ME ------------------------------------------------------------------ int main() { serialize(u64(1), f64(2), u64v(10, 11, 12, 13), f64v(20.0, 21.0, 22.0)); }