1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // This file provides facilities to declare compile-time descriptors for C++
18 // struct types. This enables generic code to access the declared struct
19 // members in an object.
20 //
21 // For example, consider the following struct type:
22 //
23 //   struct Employee {
24 //     uint32_t id;
25 //     std::string name;
26 //     std::vector<uint32_t> reports;
27 //   };
28 //
29 // The descriptor is declared as follows, providing access to |Employee|'s
30 // members and assigning a unique field number to each of them:
31 //
32 //   template <>
33 //   struct DescriptorForType<Employee> {
34 //     static constexpr auto kFields =
35 //         MakeFieldList(MakeField(1, &Employee::id),
36 //                       MakeField(2, &Employee::name),
37 //                       MakeField(3, &Employee::reports));
38 //   };
39 //
40 // Note that the |kFields| member is a constexpr, which creates a compile-time
41 // constant, so the field meta data can be used in compile-time computations and
42 // as template parameters.
43 //
44 // To access the declared members, there is a |Get()| member function template
45 // on the declared field list, which allows to retrieve one of the field
46 // specifications by index (zero-based declaration index, *not* field number).
47 // Once you have the field spec for a field, you can use |FieldSpec::Get()| to
48 // get a reference to the member within a struct instance. This can be used to
49 // implement generic algorithms that make use of the descriptor behind the
50 // scenes. Here is an example that shows how to build a generic comparator:
51 //
52 //   template <typename Struct>
53 //   struct StructCompare {
54 //     template <typename Member>
55 //     int compareMember(const Member& left, const Member& right) {
56 //       return left < right ? -1 : (right < left ? 1 : 0);
57 //     }
58 //
59 //     template <size_t... indices>
60 //     int compare(const Struct& left,
61 //                 const Struct& right,
62 //                 index_sequence<indices...>) {
63 //       constexpr auto kFieldSpecList = DescriptorForType<Struct>::kFields;
64 //       int results[] = {compareMember(
65 //           kFieldSpecList.template Get<indices>().Get(left),
66 //           kFieldSpecList.template Get<indices>().Get(right))...};
67 //       for (int result : results) {
68 //         if (result != 0) {
69 //           return result;
70 //         }
71 //       }
72 //
73 //       return 0;
74 //     }
75 //
76 //     bool operator()(const Struct& left, const Struct& right) {
77 //       constexpr auto kFieldSpecList = DescriptorForType<Struct>::kFields;
78 //       return compare(left, right,
79 //                      make_index_sequence<kFieldSpecList.kSize>()) < 0;
80 //     }
81 //   };
82 //
83 // You can now use |StructCompare| as a key comparison function with std::set
84 // like this:
85 //
86 //   std::set<Employee, StructCompare<Employee>> employees;
87 //   employees.emplace(std::move(new_employee));
88 //
89 // The ability to write generic algorithms that can process arbitrarily-typed
90 // struct fields comes at the cost of heavy usage of template constructs.
91 // However, potential alternatives are not without drawbacks:
92 //  * Avoiding generic code entirely and writing the necessary operations for
93 //    each struct type manually is tedious and error-prone.
94 //  * Tool-generated code is just as hard to comprehend and maintain, and code
95 //    making use of the generated constructs may need to be generated as well.
96 //  * For the intended use in message serialization, there are existing message
97 //    serialization solutions such as protobuf. Unfortunately, our serialization
98 //    code needs to run in resource-constrained environments that don't provide
99 //    a C++ standard library (which is a dependency of the regular protobuf
100 //    implementation), and the library weighs in as a non-trivial dependency in
101 //    terms of code size.
102 
103 #ifndef NVRAM_MESSAGES_STRUCT_H_
104 #define NVRAM_MESSAGES_STRUCT_H_
105 
106 #include <nvram/messages/type_traits.h>
107 
108 namespace nvram {
109 
110 // This class template is used to resolve struct types to their corresponding
111 // descriptors, which provide a list of struct fields that includes the field
112 // numbers as well as the corresponding C++ struct members in |Struct|. See the
113 // file comment above for an example.
114 template <typename Struct>
115 struct DescriptorForType;
116 
117 // |FieldSpec| describes a member field of the struct type |Struct|. The
118 // template parameters capture the C++ |Member| type of the |Struct| member that
119 // holds the field's data.
120 //
121 // Note that this class template is a literal type, i.e. can be used with
122 // constexpr. As an implication, |FieldSpec| instances can be used as
123 // compile-time data.
124 template <typename Struct, typename Member>
125 struct FieldSpec {
126   using MemberType = Member;
127 
FieldSpecFieldSpec128   constexpr FieldSpec(uint32_t field_number, MemberType Struct::* member)
129       : kFieldNumber(field_number), kMember(member) {}
130 
GetFieldSpec131   const MemberType& Get(const Struct& object) const {
132     return object.*kMember;
133   }
134 
GetFieldSpec135   MemberType& Get(Struct& object) const {
136     return object.*kMember;
137   }
138 
139   // The field number for this field.
140   const uint32_t kFieldNumber;
141 
142   // A member pointer to the |Struct| member that holds the field data.
143   MemberType Struct::*const kMember;
144 };
145 
146 // A helper function template that enables template argument deduction to be
147 // used to construct |FieldSpec| instances.
148 template <typename Struct, typename Member>
MakeField(uint32_t field_number,Member Struct::* member)149 constexpr FieldSpec<Struct, Member> MakeField(uint32_t field_number,
150                                               Member Struct::*member) {
151   return FieldSpec<Struct, Member>(field_number, member);
152 };
153 
154 // Forward declaration for |TaggedUnion|, so we don't have to include the full
155 // header.
156 template <typename TagType, typename... Member>
157 class TaggedUnion;
158 
159 // A special field specification type for protobuf fields belonging to a "oneof"
160 // construct, of which one field may be active at a time. This is represented by
161 // a |TaggedUnion| struct member. In addition to the field number and member
162 // pointer, the field specification also records the |TaggedUnion| tag value
163 // that selects the |TaggedUnion| member which corresponds to the field.
164 template <typename Struct, typename TagType, typename... Member>
165 struct OneOfFieldSpec
166     : public FieldSpec<Struct, TaggedUnion<TagType, Member...>> {
167   using TaggedUnionType = TaggedUnion<TagType, Member...>;
168 
OneOfFieldSpecOneOfFieldSpec169   constexpr OneOfFieldSpec(uint32_t field_number,
170                            TaggedUnionType Struct::*member,
171                            TagType tag)
172       : FieldSpec<Struct, TaggedUnionType>(field_number, member), kTag(tag) {}
173 
174   // The |TaggedUnion| tag corresponding to the |TaggedUnion| member that holds
175   // the field's data.
176   const TagType kTag;
177 };
178 
179 // A helper function template that simplifies |OneOfFieldSpec| creation by
180 // enabling template argument type deduction.
181 template <typename Struct, typename TagType, typename... Member>
MakeOneOfField(uint32_t field_number,TaggedUnion<TagType,Member...> Struct::* member,TagType tag)182 constexpr OneOfFieldSpec<Struct, TagType, Member...> MakeOneOfField(
183     uint32_t field_number,
184     TaggedUnion<TagType, Member...> Struct::*member,
185     TagType tag) {
186   return OneOfFieldSpec<Struct, TagType, Member...>(field_number, member, tag);
187 };
188 
189 // A simple type list intended to hold field specification values.
190 //
191 // Note that |FieldSpecList| is a literal type so can be used with constexpr to
192 // hold compile-time data.
193 template <typename... FieldSpec>
194 struct FieldSpecList;
195 
196 namespace {
197 
198 // A helper template that extracts the field spec at |index| from a field spec
199 // list.
200 template <size_t index, typename... FieldSpec>
201 struct FieldSpecLookup;
202 
203 // Recursion step: This specialization matches if |index| is larger than 0, and
204 // the |Get()| definition just forwards to the list tail.
205 template <size_t index, typename FieldSpec, typename... Tail>
206 struct FieldSpecLookup<index, FieldSpec, Tail...> {
207   using Prev = FieldSpecLookup<index - 1, Tail...>;
208   using Type = typename Prev::Type;
209   static constexpr Type Get(FieldSpecList<FieldSpec, Tail...> self) {
210     return Prev::Get(self.kTail);
211   }
212 };
213 
214 // Recursion base case: |index| as reached 0, so |Get()| returns the field spec
215 // corresponding to the current |FieldSpec|.
216 template <typename FieldSpec, typename... Tail>
217 struct FieldSpecLookup<0, FieldSpec, Tail...> {
218   using Type = FieldSpec;
219   static constexpr Type Get(FieldSpecList<FieldSpec, Tail...> self) {
220     return self.kFieldSpec;
221   }
222 };
223 
224 // Produces an error message in case the provided |index| is too large, i.e.
225 // doesn't match any field. This specialization only matches once the
226 // |FieldSpec| parameters are exhausted.
227 template <size_t index>
228 struct FieldSpecLookup<index> {
229   // Note that |index < 0| will never be satisfied, so this static assert
230   // triggers unconditionally if this template specialization ever gets
231   // instantiated. It will only be instantiated if |index| exceeds the number of
232   // declared fields.
233   //
234   // Just putting |false| as the static_assert condition would seem a saner
235   // alternative, but doesn't work since the static_assert would then be
236   // evaluated at declaration time. Using the |index| parameter in the condition
237   // forces evaluation to take place at template instantiation time.
238   static_assert(index < 0, "Out-of-bounds |index| in field spec lookup.");
239 };
240 
241 }  // namespace
242 
243 // |FieldSpecList| specialization that holds the data of the front-most element
244 // of |FieldSpecList|'s |Fields| arguments. Note that this class contains a
245 // nested |FieldSpecList| instance with the front-most element removed, thus
246 // inheriting the members for subsequent |Fields| arguments.
247 template <typename FieldSpec, typename... Tail>
248 struct FieldSpecList<FieldSpec, Tail...> {
249   using List = FieldSpecList<FieldSpec, Tail...>;
250   using TailList = FieldSpecList<Tail...>;
251 
252   constexpr explicit FieldSpecList(FieldSpec field_spec, Tail... tail)
253       : kFieldSpec(field_spec), kTail(tail...) {}
254 
255   template <size_t index>
256   constexpr typename FieldSpecLookup<index, FieldSpec, Tail...>::Type Get()
257       const {
258     return FieldSpecLookup<index, FieldSpec, Tail...>::Get(*this);
259   }
260 
261   static constexpr size_t kSize = TailList::kSize + 1;
262   const FieldSpec kFieldSpec;
263   const TailList kTail;
264 };
265 
266 // |FieldSpecList| specialization acting as the recursion base case. This
267 // doesn't have further members and thus stops the expansion of
268 // |FieldSpecList|'s |Fields| parameter.
269 template <>
270 struct FieldSpecList<> {
271   static constexpr size_t kSize = 0;
272 };
273 
274 // Helper function template that enables convenient creation of |FieldSpecList|
275 // instances by enabling template argument deduction.
276 template <typename... FieldSpec>
277 constexpr FieldSpecList<FieldSpec...> MakeFieldList(FieldSpec... field_spec) {
278   return FieldSpecList<FieldSpec...>(field_spec...);
279 }
280 
281 }  // namespace nvram
282 
283 #endif  // NVRAM_MESSAGES_STRUCT_H_
284