1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #ifndef DOM_CANVAS_TIED_FIELDS_H
6 #define DOM_CANVAS_TIED_FIELDS_H
8 #include "TupleUtils.h"
15 * TiedFields(T&) -> std::tuple<Fields&...>
16 * TiedFields(const T&) -> std::tuple<const Fields&...>
18 * You can also overload TiedFields without adding T::MutTiedFields:
20 * inline auto TiedFields<gfx::IntSize>(gfx::IntSize& a) {
21 * return std::tie(a.width, a.height);
25 constexpr auto TiedFields(T
& t
) {
26 const auto fields
= t
.MutTiedFields();
29 template <class T
, class... Args
, class Tup
= std::tuple
<Args
&...>>
30 constexpr auto TiedFields(const T
& t
) {
31 // Uncast const to get mutable-fields tuple, but reapply const to tuple args.
32 // We should do better than this when C++ gets a solution other than macros.
33 const auto mutFields
= TiedFields(const_cast<T
&>(t
));
34 return ToTupleOfConstRefs(mutFields
);
38 * Returns true if all bytes in T are accounted for via size of all tied fields.
39 * Returns false if there's bytes unaccounted for, which might indicate either
40 * unaccounted-for padding or missing fields.
41 * The goal is to check that TiedFields returns every field in T, and this
42 * returns false if it suspects there are bytes that are not accounted for by
45 * `constexpr` effectively cannot do math on pointers, so it's not possible to
46 * figure out via `constexpr` whether fields are consecutive or dense.
47 * However, we can at least compare `sizeof(T)` to the sum of `sizeof(Args...)`
48 * for `TiedFields(T) -> std::tuple<Args...>`.
50 * See TiedFieldsExamples.
53 constexpr bool AreAllBytesTiedFields() {
54 using fieldsT
= decltype(TiedFields(std::declval
<T
>()));
55 const auto fields_size_sum
= SizeofTupleArgs
<fieldsT
>::value
;
56 const auto t_size
= sizeof(T
);
57 return fields_size_sum
== t_size
;
60 // It's also possible to determine AreAllBytesRecursiveTiedFields:
61 // https://hackmd.io/@jgilbert/B16qa0Fa9
66 * Padding<T> can be used to pad out a struct so that it's not implicitly
67 * padded by struct rules.
68 * You can also just add your padding to TiedFields, but by explicitly typing
69 * padding like this, serialization can make a choice whether to copy Padding,
70 * or instead to omit the copy.
72 * Omitting the copy isn't always faster.
75 * Padding<uint16_t> padding;
77 * auto MutTiedFields() { return std::tie(key, padding, val); }
79 * If you serialize Padding, the serialized size is 8, and the compiler will
80 * optimize serialization to a single 8-byte memcpy.
81 * If your serialization omits Padding, the serialized size of Entry shrinks
82 * by 25%. If you have a big list of Entrys, maybe this is a big savings!
83 * However, by optimizing for size here you sacrifice speed, because this splits
84 * the single memcpy into two: a 2-byte memcpy and a 4-byte memcpy.
86 * Explicitly marking padding gives callers the option of choosing.
92 friend constexpr bool operator==(const Padding
&, const Padding
&) {
95 friend constexpr bool operator<(const Padding
&, const Padding
&) {
99 static_assert(sizeof(Padding
<bool>) == 1);
100 static_assert(sizeof(Padding
<bool[2]>) == 2);
101 static_assert(sizeof(Padding
<int>) == 4);
105 namespace TiedFieldsExamples
{
111 constexpr auto MutTiedFields() { return std::tie(i
, b
); }
113 static_assert(sizeof(Cat
) == 8);
114 static_assert(!AreAllBytesTiedFields
<Cat
>());
120 constexpr auto MutTiedFields() { return std::tie(i
, b
); }
122 static_assert(sizeof(Dog
) == 8);
123 static_assert(!AreAllBytesTiedFields
<Dog
>());
130 constexpr auto MutTiedFields() { return std::tie(i
, b
, padding
); }
132 static_assert(sizeof(Fish
) == 8);
133 static_assert(AreAllBytesTiedFields
<Fish
>());
135 struct Eel
{ // Like a Fish, but you can skip serializing the padding.
137 Padding
<bool> padding
[3];
140 constexpr auto MutTiedFields() { return std::tie(i
, b
, padding
); }
142 static_assert(sizeof(Eel
) == 8);
143 static_assert(AreAllBytesTiedFields
<Eel
>());
147 // #define LETS_USE_BIT_FIELDS
148 #ifdef LETS_USE_BIT_FIELDS
149 # undef LETS_USE_BIT_FIELDS
156 constexpr auto MutTiedFields() {
157 return std::tie(s
, s2
, i
); // Error: Can't take reference to bit-field.
169 constexpr auto MutTiedFields() { return std::tie(f
, i2
); }
171 static_assert(sizeof(FishTank
) == 12);
172 static_assert(AreAllBytesTiedFields
<FishTank
>());
178 constexpr auto MutTiedFields() { return std::tie(c
, i2
); }
180 static_assert(sizeof(CatCarrier
) == 12);
181 static_assert(AreAllBytesTiedFields
<CatCarrier
>());
183 !AreAllBytesTiedFields
<decltype(CatCarrier::c
)>()); // BUT BEWARE THIS!
184 // For example, if we had AreAllBytesRecursiveTiedFields:
185 // static_assert(!AreAllBytesRecursiveTiedFields<CatCarrier>());
187 } // namespace TiedFieldsExamples
188 } // namespace mozilla
190 #endif // DOM_CANVAS_TIED_FIELDS_H