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
108namespace 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.
114template <typename Struct>
115struct 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.
124template <typename Struct, typename Member>
125struct FieldSpec {
126  using MemberType = Member;
127
128  constexpr FieldSpec(uint32_t field_number, MemberType Struct::* member)
129      : kFieldNumber(field_number), kMember(member) {}
130
131  const MemberType& Get(const Struct& object) const {
132    return object.*kMember;
133  }
134
135  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.
148template <typename Struct, typename Member>
149constexpr 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.
156template <typename TagType, typename... Member>
157class 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.
164template <typename Struct, typename TagType, typename... Member>
165struct OneOfFieldSpec
166    : public FieldSpec<Struct, TaggedUnion<TagType, Member...>> {
167  using TaggedUnionType = TaggedUnion<TagType, Member...>;
168
169  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.
181template <typename Struct, typename TagType, typename... Member>
182constexpr 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.
193template <typename... FieldSpec>
194struct FieldSpecList;
195
196namespace {
197
198// A helper template that extracts the field spec at |index| from a field spec
199// list.
200template <size_t index, typename... FieldSpec>
201struct 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.
205template <size_t index, typename FieldSpec, typename... Tail>
206struct 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|.
216template <typename FieldSpec, typename... Tail>
217struct 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.
227template <size_t index>
228struct 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.
247template <typename FieldSpec, typename... Tail>
248struct 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.
269template <>
270struct 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.
276template <typename... FieldSpec>
277constexpr 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