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