1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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));
}
|