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
65 template <class StructT
, size_t FieldId
, size_t PrevFieldBeginOffset
,
66 class PrevFieldT
, size_t PrevFieldEndOffset
, class FieldT
,
67 size_t FieldAlignment
= alignof(FieldT
)>
68 struct FieldDebugInfoT
{
69 static constexpr bool IsTightlyPacked() {
70 return PrevFieldEndOffset
% FieldAlignment
== 0;
74 template <class StructT
, class TupleOfFields
, size_t FieldId
>
75 struct TightlyPackedFieldEndOffsetT
{
77 using FieldTAt
= std::remove_reference_t
<
78 typename
std::tuple_element
<I
, TupleOfFields
>::type
>;
80 static constexpr size_t Fn() {
81 constexpr auto num_fields
= std::tuple_size_v
<TupleOfFields
>;
82 static_assert(FieldId
< num_fields
);
84 using PrevFieldT
= FieldTAt
<FieldId
- 1>;
85 using FieldT
= FieldTAt
<FieldId
>;
86 constexpr auto prev_field_end_offset
=
87 TightlyPackedFieldEndOffsetT
<StructT
, TupleOfFields
, FieldId
- 1>::Fn();
88 constexpr auto prev_field_begin_offset
=
89 prev_field_end_offset
- sizeof(PrevFieldT
);
91 using FieldDebugInfoT
=
92 FieldDebugInfoT
<StructT
, FieldId
, prev_field_begin_offset
, PrevFieldT
,
93 prev_field_end_offset
, FieldT
>;
94 static_assert(FieldDebugInfoT::IsTightlyPacked(),
95 "This field was not tightly packed. Is there padding between "
96 "it and its predecessor?");
98 return prev_field_end_offset
+ sizeof(FieldT
);
102 template <class StructT
, class TupleOfFields
>
103 struct TightlyPackedFieldEndOffsetT
<StructT
, TupleOfFields
, 0> {
104 static constexpr size_t Fn() {
105 using FieldT
= typename
std::tuple_element
<0, TupleOfFields
>::type
;
106 return sizeof(FieldT
);
109 template <class StructT
, class TupleOfFields
>
110 struct TightlyPackedFieldEndOffsetT
<StructT
, TupleOfFields
, size_t(-1)> {
111 static constexpr size_t Fn() {
112 // -1 means tuple_size_v<TupleOfFields> -> 0.
113 static_assert(sizeof(StructT
) == 0);
118 template <class StructT
>
119 constexpr bool AssertTiedFieldsAreExhaustive() {
120 using TupleOfFields
= decltype(std::declval
<StructT
>().MutTiedFields());
121 constexpr auto num_fields
= std::tuple_size_v
<TupleOfFields
>;
122 constexpr auto end_offset_of_last_field
=
123 TightlyPackedFieldEndOffsetT
<StructT
, TupleOfFields
,
124 num_fields
- 1>::Fn();
126 end_offset_of_last_field
== sizeof(StructT
),
127 "Incorrect field list in MutTiedFields()? (or not tightly-packed?)");
128 return true; // Support `static_assert(AssertTiedFieldsAreExhaustive())`.
134 * PaddingField<T,N=1> can be used to pad out a struct so that it's not
135 * implicitly padded by struct rules, but also can't be accidentally initialized
136 * via Aggregate Initialization. (TiedFields serialization checks rely on object
137 * fields leaving no implicit padding bytes, but explicit padding fields are
138 * fine) While you can use e.g. `uint8_t _padding[3];`, consider instead
139 * `PaddingField<uint8_t,3> _padding;` for clarity and to move the `3` nearer
142 template <class T
, size_t N
= 1>
143 struct PaddingField
{
144 static_assert(!std::is_array_v
<T
>, "Use PaddingField<T,N> not <T[N]>.");
146 std::array
<T
, N
> ignored
= {};
150 friend constexpr bool operator==(const PaddingField
&, const PaddingField
&) {
153 friend constexpr bool operator<(const PaddingField
&, const PaddingField
&) {
157 auto MutTiedFields() { return std::tie(ignored
); }
159 static_assert(sizeof(PaddingField
<bool>) == 1);
160 static_assert(sizeof(PaddingField
<bool, 2>) == 2);
161 static_assert(sizeof(PaddingField
<int>) == 4);
165 namespace TiedFieldsExamples
{
171 constexpr auto MutTiedFields() { return std::tie(i
, b
); }
173 static_assert(sizeof(Cat
) == 8);
174 static_assert(!AreAllBytesTiedFields
<Cat
>());
180 constexpr auto MutTiedFields() { return std::tie(i
, b
); }
182 static_assert(sizeof(Dog
) == 8);
183 static_assert(!AreAllBytesTiedFields
<Dog
>());
190 constexpr auto MutTiedFields() { return std::tie(i
, b
, padding
); }
192 static_assert(sizeof(Fish
) == 8);
193 static_assert(AreAllBytesTiedFields
<Fish
>());
195 struct Eel
{ // Like a Fish, but you can skip serializing the padding.
197 PaddingField
<bool, 3> padding
;
200 constexpr auto MutTiedFields() { return std::tie(i
, b
, padding
); }
202 static_assert(sizeof(Eel
) == 8);
203 static_assert(AreAllBytesTiedFields
<Eel
>());
207 // #define LETS_USE_BIT_FIELDS
208 #ifdef LETS_USE_BIT_FIELDS
209 # undef LETS_USE_BIT_FIELDS
216 constexpr auto MutTiedFields() {
217 return std::tie(s
, s2
, i
); // Error: Can't take reference to bit-field.
229 constexpr auto MutTiedFields() { return std::tie(f
, i2
); }
231 static_assert(sizeof(FishTank
) == 12);
232 static_assert(AreAllBytesTiedFields
<FishTank
>());
238 constexpr auto MutTiedFields() { return std::tie(c
, i2
); }
240 static_assert(sizeof(CatCarrier
) == 12);
241 static_assert(AreAllBytesTiedFields
<CatCarrier
>());
243 !AreAllBytesTiedFields
<decltype(CatCarrier::c
)>()); // BUT BEWARE THIS!
244 // For example, if we had AreAllBytesRecursiveTiedFields:
245 // static_assert(!AreAllBytesRecursiveTiedFields<CatCarrier>());
247 } // namespace TiedFieldsExamples
248 } // namespace mozilla
250 #endif // DOM_CANVAS_TIED_FIELDS_H