Home | History | Annotate | Download | only in messages
      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 
    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.
    148 template <typename Struct, typename 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 
    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.
    181 template <typename Struct, typename TagType, typename... Member>
    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