diff options
-rw-r--r-- | variadic_to_common_type.cc | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/variadic_to_common_type.cc b/variadic_to_common_type.cc new file mode 100644 index 0000000..d8609b5 --- /dev/null +++ b/variadic_to_common_type.cc @@ -0,0 +1,132 @@ +#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 <array> +#include <type_traits> + +#include <cstddef> +#include <cstdint> + +// 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 <typename T> +constexpr bytes to_bytes(const T& val) { + static_assert(std::is_arithmetic_v<T>, ""); + return {.ptr = reinterpret_cast<const uint8_t*>(&val), .len = sizeof(val)}; +} + +template <typename T, size_t N> +constexpr bytes to_bytes(const T (&vals)[N]) { + static_assert(std::is_arithmetic_v<T>, ""); + static_assert(sizeof(vals) == sizeof(T) * N, ""); + return {.ptr = &vals, .len = sizeof(vals)}; +} + +template <typename T, size_t N> +constexpr bytes to_bytes(const std::array<T, N>& vals) { + static_assert(std::is_arithmetic_v<T>, ""); + return {.ptr = reinterpret_cast<const uint8_t*>(vals.data()), + .len = sizeof(T) * N}; +} + +// -- SCALAR GENERATOR & ALIAS ------------------------------------------------- + +// Utility to generate scalar values of a specific type. + +template <typename T> +struct scalar { + static_assert(std::is_arithmetic_v<T>, ""); + + template <typename F> + constexpr T operator()(F val) const { + static_assert(std::is_convertible_v<F, T>, ""); + return static_cast<T>(val); + } +}; + +inline constexpr scalar<uint64_t> u64; +inline constexpr scalar<double> f64; + +// -- ARRAY GENERATOR & ALIAS -------------------------------------------------- + +// Utility to generate array values of a specific type. + +template <typename T> +struct array { + static_assert(std::is_arithmetic_v<T>, ""); + + template <typename... F> + constexpr std::array<T, sizeof...(F)> operator()(F... val) const { + static_assert(is_convertible<F...>(), ""); + return {static_cast<T>(val)...}; + } + + // Conversion checker helper. + + template <typename F> + static constexpr bool is_convertible() { + return std::is_convertible_v<F, T>; + } + + template <typename F1, typename F2, typename... Fs> + static constexpr bool is_convertible() { + return std::is_convertible_v<F1, T> && is_convertible<F2, Fs...>(); + } +}; + +inline constexpr array<uint64_t> u64v; +inline constexpr array<double> f64v; + +// -- SERIALIZER --------------------------------------------------------------- + +// Example consumer of variadic arguments which are mapped to the common `bytes` +// type. + +#include <cstdio> + +#include <initializer_list> +#include <typeinfo> + +template <typename... Args> +void serialize(Args&&... args) { + // auto vals = {to_bytes(args)...}; + std::initializer_list<bytes> 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<bytes, const char*>{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)); +} |