|
| 1 | +#include <cmath> |
| 2 | +#include <cstdint> |
| 3 | +#include <iostream> |
| 4 | +#include <string_view> |
1 | 5 | #include <tuple> |
| 6 | +#include <type_traits> |
2 | 7 |
|
3 | | -std::tuple<int, char, double> get_tuple(); |
4 | | - |
| 8 | +// Domain model: a telemetry packet |
5 | 9 | struct Data { |
6 | | - int a; |
7 | | - char b; |
8 | | - double c; |
| 10 | + int i; // e.g., sensor id |
| 11 | + char c; // e.g., status code |
| 12 | + double d; // e.g., measurement |
9 | 13 | }; |
10 | 14 |
|
11 | | -Data get_data(); |
| 15 | +// Simulated runtime acquisition |
| 16 | +Data get_data() { |
| 17 | + return Data{42, 'O', 23.75}; |
| 18 | +} |
| 19 | + |
| 20 | +// Compile-time "golden" packet (build-time contract) |
| 21 | +consteval Data get_constexpr_data() { |
| 22 | + return Data{1, 'a', 1.0}; |
| 23 | +} |
| 24 | + |
| 25 | + |
| 26 | +// Field reflection (standard C++): tuple-of-references |
| 27 | + |
| 28 | +constexpr auto as_tuple(Data& x) { |
| 29 | + return std::tie(x.i, x.c, x.d); |
| 30 | +} |
| 31 | + |
| 32 | +constexpr auto as_tuple(const Data& x) { |
| 33 | + return std::tie(x.i, x.c, x.d); |
| 34 | +} |
| 35 | + |
| 36 | +// Apply a callable to each field of Data |
| 37 | +template <class F> |
| 38 | +constexpr void for_each_field(Data& x, F&& f) { |
| 39 | + std::apply([&](auto&... fields) { (f(fields), ...); }, as_tuple(x)); |
| 40 | +} |
| 41 | +template <class F> |
| 42 | +constexpr void for_each_field(const Data& x, F&& f) { |
| 43 | + std::apply([&](const auto&... fields) { (f(fields), ...); }, as_tuple(x)); |
| 44 | +} |
| 45 | + |
| 46 | + |
| 47 | +// Logging & validation (runtime) |
| 48 | + |
| 49 | +void use(int v) { |
| 50 | + std::cout << "int : " << v << "\n"; |
| 51 | +} |
| 52 | +void use(char v) { |
| 53 | + std::cout << "char : '" << v << "'\n"; |
| 54 | +} |
| 55 | +void use(double v) { |
| 56 | + std::cout << "double : " << v << "\n"; |
| 57 | +} |
| 58 | + |
| 59 | +struct Validation { |
| 60 | + bool ok = true; |
| 61 | + std::string_view first_error{}; |
| 62 | +}; |
| 63 | + |
| 64 | +inline void require(bool cond, Validation& out, std::string_view msg) { |
| 65 | + if (!cond && out.ok) { |
| 66 | + out.ok = false; |
| 67 | + out.first_error = msg; |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +Validation validate(const Data& x) { |
| 72 | + Validation v{}; |
| 73 | + require(x.i >= 0, v, "sensor id must be non-negative"); |
| 74 | + require(std::isprint(static_cast<unsigned char>(x.c)) != 0, v, "status must be printable ASCII"); |
| 75 | + require(std::isfinite(x.d) != 0, v, "measurement must be finite"); |
| 76 | + // Example domain rule: |
| 77 | + require(x.d >= -1000.0 && x.d <= 1000.0, v, "measurement out of expected range"); |
| 78 | + return v; |
| 79 | +} |
| 80 | + |
| 81 | + |
| 82 | +// Compile-time checksum (build-time self-test) |
| 83 | + |
| 84 | +consteval std::uint64_t mix_u64(std::uint64_t h, std::uint64_t x) { |
| 85 | + // Simple 64-bit mixing (not cryptographic) |
| 86 | + h ^= x + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2); |
| 87 | + return h; |
| 88 | +} |
| 89 | + |
| 90 | +template <auto Value> |
| 91 | +consteval std::uint64_t fold_value(std::uint64_t h) { |
| 92 | + using T = decltype(Value); |
| 93 | + |
| 94 | + if constexpr (std::is_same_v<T, int>) { |
| 95 | + return mix_u64(h, static_cast<std::uint64_t>(static_cast<std::int64_t>(Value))); |
| 96 | + } else if constexpr (std::is_same_v<T, char>) { |
| 97 | + return mix_u64(h, static_cast<std::uint64_t>(static_cast<unsigned char>(Value))); |
| 98 | + } else if constexpr (std::is_same_v<T, double>) { |
| 99 | + // Convert double to an integer-ish stable representation for checksum purposes. |
| 100 | + // Here we quantize to micro-units; adjust to your tolerance requirements. |
| 101 | + const long long q = static_cast<long long>(Value * 1'000'000.0); |
| 102 | + return mix_u64(h, static_cast<std::uint64_t>(q)); |
| 103 | + } else { |
| 104 | + static_assert(!sizeof(T), "Unsupported field type in checksum"); |
| 105 | + } |
| 106 | +} |
| 107 | + |
| 108 | +consteval std::uint64_t golden_checksum() { |
| 109 | + constexpr Data g = get_constexpr_data(); |
| 110 | + std::uint64_t h = 0xcbf29ce484222325ULL; |
12 | 111 |
|
13 | | -void use(double); |
14 | | -void use(char); |
15 | | -void use(int); |
| 112 | + // Standard C++ compile-time fold over fields: |
| 113 | + // (we can’t literally “loop” over heterogeneous fields without a helper) |
| 114 | + h = fold_value<g.i>(h); |
| 115 | + h = fold_value<g.c>(h); |
| 116 | + h = fold_value<g.d>(h); |
| 117 | + |
| 118 | + return h; |
| 119 | +} |
| 120 | + |
| 121 | +// If someone changes golden data values or field order, this will break the build. |
| 122 | +static_assert(golden_checksum() == golden_checksum(), "checksum sanity"); // tautology |
| 123 | +// In a real project, pin the expected value once, then keep it stable: |
| 124 | +// static_assert(golden_checksum() == 0xDEADBEEF..., "Golden packet changed unexpectedly"); |
| 125 | + |
| 126 | + |
| 127 | +// Application entry |
16 | 128 |
|
17 | 129 | int main() { |
18 | | - template for (const auto& element : get_tuple()) { |
19 | | - // Do something with element |
20 | | - use(element); |
| 130 | + std::cout << "=== Telemetry Packet Logger ===\n"; |
| 131 | + |
| 132 | + Data sample = get_data(); |
| 133 | + |
| 134 | + // Validate first |
| 135 | + if (auto v = validate(sample); !v.ok) { |
| 136 | + std::cout << "Invalid packet: " << v.first_error << "\n"; |
| 137 | + return 1; |
21 | 138 | } |
| 139 | + |
| 140 | + // Log fields one-by-one (your “iterate fields” intent) |
| 141 | + std::cout << "Packet fields:\n"; |
| 142 | + for_each_field(sample, [](const auto& field) { use(field); }); |
| 143 | + |
| 144 | + // Example: derive a simple runtime checksum too (for transmission integrity) |
| 145 | + // std::uint64_t h = 0xcbf29ce484222325ULL; |
| 146 | + // for_each_field(sample, [&](const auto& field) { |
| 147 | + // using T = std::remove_cvref_t<decltype(field)>; |
| 148 | + // if constexpr (std::is_same_v<T, int>) { |
| 149 | + // h = mix_u64(h, static_cast<std::uint64_t>(static_cast<std::int64_t>(field))); |
| 150 | + // } else if constexpr (std::is_same_v<T, char>) { |
| 151 | + // h = mix_u64(h, static_cast<std::uint64_t>(static_cast<unsigned char>(field))); |
| 152 | + // } else if constexpr (std::is_same_v<T, double>) { |
| 153 | + // const long long q = static_cast<long long>(field * 1'000'000.0); |
| 154 | + // h = mix_u64(h, static_cast<std::uint64_t>(q)); |
| 155 | + // } |
| 156 | + // }); |
| 157 | + |
| 158 | + // std::cout << "Runtime checksum: 0x" << std::hex << h << std::dec << "\n"; |
| 159 | + std::cout << "Golden checksum : 0x" << std::hex << golden_checksum() << std::dec << "\n"; |
| 160 | + |
22 | 161 | return 0; |
23 | 162 | } |
0 commit comments