• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 // This header defines the RepeatedFieldRef class template used to access
9 // repeated fields with protobuf reflection API.
10 #ifndef GOOGLE_PROTOBUF_REFLECTION_H__
11 #define GOOGLE_PROTOBUF_REFLECTION_H__
12 
13 #include <memory>
14 #include <type_traits>
15 
16 #include "google/protobuf/generated_enum_util.h"
17 #include "google/protobuf/descriptor.h"
18 
19 #ifdef SWIG
20 #error "You cannot SWIG proto headers"
21 #endif
22 
23 // Must be included last.
24 #include "google/protobuf/port_def.inc"
25 
26 namespace google {
27 namespace protobuf {
28 namespace internal {
29 template <typename T, typename Enable = void>
30 struct RefTypeTraits;
31 }  // namespace internal
32 
33 class Message;
34 
35 template <typename Dep, typename T>
36 using MakeDependent = std::conditional_t<true, T, Dep>;
37 
38 // Forward-declare RepeatedFieldRef templates. The second type parameter is
39 // used for SFINAE tricks. Users should ignore it.
40 template <typename T, typename Enable = void>
41 class RepeatedFieldRef;
42 
43 template <typename T, typename Enable = void>
44 class MutableRepeatedFieldRef;
45 
46 // RepeatedFieldRef definition for non-message types.
47 template <typename T>
48 class RepeatedFieldRef<
49     T, typename std::enable_if<!std::is_base_of<Message, T>::value>::type> {
50   typedef typename internal::RefTypeTraits<T>::iterator IteratorType;
51   typedef typename internal::RefTypeTraits<T>::AccessorType AccessorType;
52 
53  public:
empty()54   bool empty() const { return accessor_->IsEmpty(data_); }
size()55   int size() const { return accessor_->Size(data_); }
Get(int index)56   T Get(int index) const { return accessor_->template Get<T>(data_, index); }
57 
58   typedef IteratorType iterator;
59   typedef IteratorType const_iterator;
60   typedef T value_type;
61   typedef T& reference;
62   typedef const T& const_reference;
63   typedef int size_type;
64   typedef ptrdiff_t difference_type;
65 
begin()66   iterator begin() const { return iterator(data_, accessor_, true); }
end()67   iterator end() const { return iterator(data_, accessor_, false); }
68 
69  private:
70   friend class Reflection;
RepeatedFieldRef(const MakeDependent<T,Message> & message,const FieldDescriptor * field)71   RepeatedFieldRef(const MakeDependent<T, Message>& message,
72                    const FieldDescriptor* field) {
73     const auto* reflection = message.GetReflection();
74     data_ = reflection->RepeatedFieldData(
75         message, field, internal::RefTypeTraits<T>::cpp_type, nullptr);
76     accessor_ = reflection->RepeatedFieldAccessor(field);
77   }
78 
79   const void* data_;
80   const AccessorType* accessor_;
81 };
82 
83 // MutableRepeatedFieldRef definition for non-message types.
84 template <typename T>
85 class MutableRepeatedFieldRef<
86     T, typename std::enable_if<!std::is_base_of<Message, T>::value>::type> {
87   typedef typename internal::RefTypeTraits<T>::AccessorType AccessorType;
88 
89  public:
empty()90   bool empty() const { return accessor_->IsEmpty(data_); }
size()91   int size() const { return accessor_->Size(data_); }
Get(int index)92   T Get(int index) const { return accessor_->template Get<T>(data_, index); }
93 
Set(int index,const T & value)94   void Set(int index, const T& value) const {
95     accessor_->template Set<T>(data_, index, value);
96   }
Add(const T & value)97   void Add(const T& value) const { accessor_->template Add<T>(data_, value); }
RemoveLast()98   void RemoveLast() const { accessor_->RemoveLast(data_); }
Reserve(int size)99   void Reserve(int size) const { accessor_->Reserve(data_, size); }
SwapElements(int index1,int index2)100   void SwapElements(int index1, int index2) const {
101     accessor_->SwapElements(data_, index1, index2);
102   }
Clear()103   void Clear() const { accessor_->Clear(data_); }
104 
Swap(const MutableRepeatedFieldRef & other)105   void Swap(const MutableRepeatedFieldRef& other) const {
106     accessor_->Swap(data_, other.accessor_, other.data_);
107   }
108 
109   template <typename Container>
MergeFrom(const Container & container)110   void MergeFrom(const Container& container) const {
111     typedef typename Container::const_iterator Iterator;
112     for (Iterator it = container.begin(); it != container.end(); ++it) {
113       Add(*it);
114     }
115   }
116   template <typename Container>
CopyFrom(const Container & container)117   void CopyFrom(const Container& container) const {
118     Clear();
119     MergeFrom(container);
120   }
121 
122  private:
123   friend class Reflection;
MutableRepeatedFieldRef(MakeDependent<T,Message> * message,const FieldDescriptor * field)124   MutableRepeatedFieldRef(MakeDependent<T, Message>* message,
125                           const FieldDescriptor* field) {
126     const auto* reflection = message->GetReflection();
127     data_ = reflection->RepeatedFieldData(
128         message, field, internal::RefTypeTraits<T>::cpp_type, nullptr);
129     accessor_ = reflection->RepeatedFieldAccessor(field);
130   }
131 
132   void* data_;
133   const AccessorType* accessor_;
134 };
135 
136 // RepeatedFieldRef definition for message types.
137 template <typename T>
138 class RepeatedFieldRef<
139     T, typename std::enable_if<std::is_base_of<Message, T>::value>::type> {
140   typedef typename internal::RefTypeTraits<T>::iterator IteratorType;
141   typedef typename internal::RefTypeTraits<T>::AccessorType AccessorType;
142 
143  public:
empty()144   bool empty() const { return accessor_->IsEmpty(data_); }
size()145   int size() const { return accessor_->Size(data_); }
146   // This method returns a reference to the underlying message object if it
147   // exists. If a message object doesn't exist (e.g., data stored in serialized
148   // form), scratch_space will be filled with the data and a reference to it
149   // will be returned.
150   //
151   // Example:
152   //   RepeatedFieldRef<Message> h = ...
153   //   unique_ptr<Message> scratch_space(h.NewMessage());
154   //   const Message& item = h.Get(index, scratch_space.get());
Get(int index,T * scratch_space)155   const T& Get(int index, T* scratch_space) const {
156     return *static_cast<const T*>(accessor_->Get(data_, index, scratch_space));
157   }
158   // Create a new message of the same type as the messages stored in this
159   // repeated field. Caller takes ownership of the returned object.
NewMessage()160   T* NewMessage() const { return default_instance_->New(); }
161 
162   typedef IteratorType iterator;
163   typedef IteratorType const_iterator;
164   typedef T value_type;
165   typedef T& reference;
166   typedef const T& const_reference;
167   typedef int size_type;
168   typedef ptrdiff_t difference_type;
169 
begin()170   iterator begin() const {
171     return iterator(data_, accessor_, true, NewMessage());
172   }
end()173   iterator end() const {
174     // The end iterator must not be dereferenced, no need for scratch space.
175     return iterator(data_, accessor_, false, nullptr);
176   }
177 
178  private:
179   friend class Reflection;
RepeatedFieldRef(const MakeDependent<T,Message> & message,const FieldDescriptor * field)180   RepeatedFieldRef(const MakeDependent<T, Message>& message,
181                    const FieldDescriptor* field) {
182     const auto* reflection = message.GetReflection();
183     data_ = reflection->RepeatedFieldData(
184         message, field, internal::RefTypeTraits<T>::cpp_type,
185         internal::RefTypeTraits<T>::GetMessageFieldDescriptor());
186     accessor_ = reflection->RepeatedFieldAccessor(field);
187     default_instance_ = static_cast<const T*>(
188         reflection->GetMessageFactory()->GetPrototype(field->message_type()));
189   }
190 
191   const void* data_;
192   const AccessorType* accessor_;
193   const T* default_instance_;
194 };
195 
196 // MutableRepeatedFieldRef definition for message types.
197 template <typename T>
198 class MutableRepeatedFieldRef<
199     T, typename std::enable_if<std::is_base_of<Message, T>::value>::type> {
200   typedef typename internal::RefTypeTraits<T>::AccessorType AccessorType;
201 
202  public:
empty()203   bool empty() const { return accessor_->IsEmpty(data_); }
size()204   int size() const { return accessor_->Size(data_); }
205   // See comments for RepeatedFieldRef<Message>::Get()
Get(int index,T * scratch_space)206   const T& Get(int index, T* scratch_space) const {
207     return *static_cast<const T*>(accessor_->Get(data_, index, scratch_space));
208   }
209   // Create a new message of the same type as the messages stored in this
210   // repeated field. Caller takes ownership of the returned object.
NewMessage()211   T* NewMessage() const { return default_instance_->New(); }
212 
Set(int index,const T & value)213   void Set(int index, const T& value) const {
214     accessor_->Set(data_, index, &value);
215   }
Add(const T & value)216   void Add(const T& value) const { accessor_->Add(data_, &value); }
RemoveLast()217   void RemoveLast() const { accessor_->RemoveLast(data_); }
Reserve(int size)218   void Reserve(int size) const { accessor_->Reserve(data_, size); }
SwapElements(int index1,int index2)219   void SwapElements(int index1, int index2) const {
220     accessor_->SwapElements(data_, index1, index2);
221   }
Clear()222   void Clear() const { accessor_->Clear(data_); }
223 
Swap(const MutableRepeatedFieldRef & other)224   void Swap(const MutableRepeatedFieldRef& other) const {
225     accessor_->Swap(data_, other.accessor_, other.data_);
226   }
227 
228   template <typename Container>
MergeFrom(const Container & container)229   void MergeFrom(const Container& container) const {
230     typedef typename Container::const_iterator Iterator;
231     for (Iterator it = container.begin(); it != container.end(); ++it) {
232       Add(*it);
233     }
234   }
235   template <typename Container>
CopyFrom(const Container & container)236   void CopyFrom(const Container& container) const {
237     Clear();
238     MergeFrom(container);
239   }
240 
241  private:
242   friend class Reflection;
MutableRepeatedFieldRef(MakeDependent<T,Message> * message,const FieldDescriptor * field)243   MutableRepeatedFieldRef(MakeDependent<T, Message>* message,
244                           const FieldDescriptor* field) {
245     const auto* reflection = message->GetReflection();
246     data_ = reflection->RepeatedFieldData(
247         message, field, internal::RefTypeTraits<T>::cpp_type,
248         internal::RefTypeTraits<T>::GetMessageFieldDescriptor());
249     accessor_ = reflection->RepeatedFieldAccessor(field);
250     default_instance_ = static_cast<const T*>(
251         reflection->GetMessageFactory()->GetPrototype(field->message_type()));
252   }
253 
254   void* data_;
255   const AccessorType* accessor_;
256   const T* default_instance_;
257 };
258 
259 namespace internal {
260 // Interfaces used to implement reflection RepeatedFieldRef API.
261 // Reflection::GetRepeatedAccessor() should return a pointer to an singleton
262 // object that implements the below interface.
263 //
264 // This interface passes/returns values using void pointers. The actual type
265 // of the value depends on the field's cpp_type. Following is a mapping from
266 // cpp_type to the type that should be used in this interface:
267 //
268 //   field->cpp_type()      T                Actual type of void*
269 //   CPPTYPE_INT32        int32_t                 int32_t
270 //   CPPTYPE_UINT32       uint32_t                uint32_t
271 //   CPPTYPE_INT64        int64_t                 int64_t
272 //   CPPTYPE_UINT64       uint64_t                uint64_t
273 //   CPPTYPE_DOUBLE       double                  double
274 //   CPPTYPE_FLOAT        float                   float
275 //   CPPTYPE_BOOL         bool                    bool
276 //   CPPTYPE_ENUM         generated enum type     int32_t
277 //   CPPTYPE_STRING       string                  std::string
278 //   CPPTYPE_MESSAGE      generated message type  google::protobuf::Message
279 //                        or google::protobuf::Message
280 //
281 // Note that for enums we use int32_t in the interface.
282 //
283 // You can map from T to the actual type using RefTypeTraits:
284 //   typedef RefTypeTraits<T>::AccessorValueType ActualType;
285 class PROTOBUF_EXPORT RepeatedFieldAccessor {
286  public:
287   // Typedefs for clarity.
288   typedef void Field;
289   typedef void Value;
290   typedef void Iterator;
291 
292   virtual bool IsEmpty(const Field* data) const = 0;
293   virtual int Size(const Field* data) const = 0;
294   // Depends on the underlying representation of the repeated field, this
295   // method can return a pointer to the underlying object if such an object
296   // exists, or fill the data into scratch_space and return scratch_space.
297   // Callers of this method must ensure scratch_space is a valid pointer
298   // to a mutable object of the correct type.
299   virtual const Value* Get(const Field* data, int index,
300                            Value* scratch_space) const = 0;
301 
302   virtual void Clear(Field* data) const = 0;
303   virtual void Set(Field* data, int index, const Value* value) const = 0;
304   virtual void Add(Field* data, const Value* value) const = 0;
305   virtual void RemoveLast(Field* data) const = 0;
306   virtual void Reserve(Field* data, int size) const = 0;
307   virtual void SwapElements(Field* data, int index1, int index2) const = 0;
308   virtual void Swap(Field* data, const RepeatedFieldAccessor* other_mutator,
309                     Field* other_data) const = 0;
310 
311   // Create an iterator that points at the beginning of the repeated field.
312   virtual Iterator* BeginIterator(const Field* data) const = 0;
313   // Create an iterator that points at the end of the repeated field.
314   virtual Iterator* EndIterator(const Field* data) const = 0;
315   // Make a copy of an iterator and return the new copy.
316   virtual Iterator* CopyIterator(const Field* data,
317                                  const Iterator* iterator) const = 0;
318   // Move an iterator to point to the next element.
319   virtual Iterator* AdvanceIterator(const Field* data,
320                                     Iterator* iterator) const = 0;
321   // Compare whether two iterators point to the same element.
322   virtual bool EqualsIterator(const Field* data, const Iterator* a,
323                               const Iterator* b) const = 0;
324   // Delete an iterator created by BeginIterator(), EndIterator() and
325   // CopyIterator().
326   virtual void DeleteIterator(const Field* data, Iterator* iterator) const = 0;
327   // Like Get() but for iterators.
328   virtual const Value* GetIteratorValue(const Field* data,
329                                         const Iterator* iterator,
330                                         Value* scratch_space) const = 0;
331 
332   // Templated methods that make using this interface easier for non-message
333   // types.
334   template <typename T>
Get(const Field * data,int index)335   T Get(const Field* data, int index) const {
336     typedef typename RefTypeTraits<T>::AccessorValueType ActualType;
337     ActualType scratch_space;
338     return static_cast<T>(*reinterpret_cast<const ActualType*>(
339         Get(data, index, static_cast<Value*>(&scratch_space))));
340   }
341 
342   template <typename T, typename ValueType>
Set(Field * data,int index,const ValueType & value)343   void Set(Field* data, int index, const ValueType& value) const {
344     typedef typename RefTypeTraits<T>::AccessorValueType ActualType;
345     // In this RepeatedFieldAccessor interface we pass/return data using
346     // raw pointers. Type of the data these raw pointers point to should
347     // be ActualType. Here we have a ValueType object and want a ActualType
348     // pointer. We can't cast a ValueType pointer to an ActualType pointer
349     // directly because their type might be different (for enums ValueType
350     // may be a generated enum type while ActualType is int32_t). To be safe
351     // we make a copy to get a temporary ActualType object and use it.
352     ActualType tmp = static_cast<ActualType>(value);
353     Set(data, index, static_cast<const Value*>(&tmp));
354   }
355 
356   template <typename T, typename ValueType>
Add(Field * data,const ValueType & value)357   void Add(Field* data, const ValueType& value) const {
358     typedef typename RefTypeTraits<T>::AccessorValueType ActualType;
359     // In this RepeatedFieldAccessor interface we pass/return data using
360     // raw pointers. Type of the data these raw pointers point to should
361     // be ActualType. Here we have a ValueType object and want a ActualType
362     // pointer. We can't cast a ValueType pointer to an ActualType pointer
363     // directly because their type might be different (for enums ValueType
364     // may be a generated enum type while ActualType is int32_t). To be safe
365     // we make a copy to get a temporary ActualType object and use it.
366     ActualType tmp = static_cast<ActualType>(value);
367     Add(data, static_cast<const Value*>(&tmp));
368   }
369 
370  protected:
371   // We want the destructor to be completely trivial as to allow it to be
372   // a function local static. Hence we make it non-virtual and protected,
373   // this class only live as part of a global singleton and should not be
374   // deleted.
375   ~RepeatedFieldAccessor() = default;
376 };
377 
378 // Implement (Mutable)RepeatedFieldRef::iterator
379 template <typename T>
380 class RepeatedFieldRefIterator {
381   typedef typename RefTypeTraits<T>::AccessorValueType AccessorValueType;
382   typedef typename RefTypeTraits<T>::IteratorValueType IteratorValueType;
383   typedef typename RefTypeTraits<T>::IteratorPointerType IteratorPointerType;
384 
385  public:
386   using iterator_category = std::forward_iterator_tag;
387   using value_type = T;
388   using pointer = T*;
389   using reference = T&;
390   using difference_type = std::ptrdiff_t;
391 
392   // Constructor for non-message fields.
RepeatedFieldRefIterator(const void * data,const RepeatedFieldAccessor * accessor,bool begin)393   RepeatedFieldRefIterator(const void* data,
394                            const RepeatedFieldAccessor* accessor, bool begin)
395       : data_(data),
396         accessor_(accessor),
397         iterator_(begin ? accessor->BeginIterator(data)
398                         : accessor->EndIterator(data)),
399         // The end iterator must not be dereferenced, no need for scratch space.
400         scratch_space_(begin ? new AccessorValueType : nullptr) {}
401   // Constructor for message fields.
RepeatedFieldRefIterator(const void * data,const RepeatedFieldAccessor * accessor,bool begin,AccessorValueType * scratch_space)402   RepeatedFieldRefIterator(const void* data,
403                            const RepeatedFieldAccessor* accessor, bool begin,
404                            AccessorValueType* scratch_space)
405       : data_(data),
406         accessor_(accessor),
407         iterator_(begin ? accessor->BeginIterator(data)
408                         : accessor->EndIterator(data)),
409         scratch_space_(scratch_space) {}
~RepeatedFieldRefIterator()410   ~RepeatedFieldRefIterator() { accessor_->DeleteIterator(data_, iterator_); }
411   RepeatedFieldRefIterator operator++(int) {
412     RepeatedFieldRefIterator tmp(*this);
413     iterator_ = accessor_->AdvanceIterator(data_, iterator_);
414     return tmp;
415   }
416   RepeatedFieldRefIterator& operator++() {
417     iterator_ = accessor_->AdvanceIterator(data_, iterator_);
418     return *this;
419   }
420   IteratorValueType operator*() const {
421     return static_cast<IteratorValueType>(
422         *static_cast<const AccessorValueType*>(accessor_->GetIteratorValue(
423             data_, iterator_, scratch_space_.get())));
424   }
425   IteratorPointerType operator->() const {
426     return static_cast<IteratorPointerType>(
427         accessor_->GetIteratorValue(data_, iterator_, scratch_space_.get()));
428   }
429   bool operator!=(const RepeatedFieldRefIterator& other) const {
430     assert(data_ == other.data_);
431     assert(accessor_ == other.accessor_);
432     return !accessor_->EqualsIterator(data_, iterator_, other.iterator_);
433   }
434   bool operator==(const RepeatedFieldRefIterator& other) const {
435     return !this->operator!=(other);
436   }
437 
RepeatedFieldRefIterator(const RepeatedFieldRefIterator & other)438   RepeatedFieldRefIterator(const RepeatedFieldRefIterator& other)
439       : data_(other.data_),
440         accessor_(other.accessor_),
441         iterator_(accessor_->CopyIterator(data_, other.iterator_)) {}
442   RepeatedFieldRefIterator& operator=(const RepeatedFieldRefIterator& other) {
443     if (this != &other) {
444       accessor_->DeleteIterator(data_, iterator_);
445       data_ = other.data_;
446       accessor_ = other.accessor_;
447       iterator_ = accessor_->CopyIterator(data_, other.iterator_);
448     }
449     return *this;
450   }
451 
452  protected:
453   const void* data_;
454   const RepeatedFieldAccessor* accessor_;
455   void* iterator_;
456   std::unique_ptr<AccessorValueType> scratch_space_;
457 };
458 
459 // TypeTraits that maps the type parameter T of RepeatedFieldRef or
460 // MutableRepeatedFieldRef to corresponding iterator type,
461 // RepeatedFieldAccessor type, etc.
462 template <typename T>
463 struct PrimitiveTraits {
464   static constexpr bool is_primitive = false;
465 };
466 #define DEFINE_PRIMITIVE(TYPE, type)                 \
467   template <>                                        \
468   struct PrimitiveTraits<type> {                     \
469     static const bool is_primitive = true;           \
470     static const FieldDescriptor::CppType cpp_type = \
471         FieldDescriptor::CPPTYPE_##TYPE;             \
472   };
473 DEFINE_PRIMITIVE(INT32, int32_t)
474 DEFINE_PRIMITIVE(UINT32, uint32_t)
475 DEFINE_PRIMITIVE(INT64, int64_t)
476 DEFINE_PRIMITIVE(UINT64, uint64_t)
477 DEFINE_PRIMITIVE(FLOAT, float)
478 DEFINE_PRIMITIVE(DOUBLE, double)
479 DEFINE_PRIMITIVE(BOOL, bool)
480 #undef DEFINE_PRIMITIVE
481 
482 template <typename T>
483 struct RefTypeTraits<
484     T, typename std::enable_if<PrimitiveTraits<T>::is_primitive>::type> {
485   typedef RepeatedFieldRefIterator<T> iterator;
486   typedef RepeatedFieldAccessor AccessorType;
487   typedef T AccessorValueType;
488   typedef T IteratorValueType;
489   typedef T* IteratorPointerType;
490   static constexpr FieldDescriptor::CppType cpp_type =
491       PrimitiveTraits<T>::cpp_type;
492   static const Descriptor* GetMessageFieldDescriptor() { return nullptr; }
493 };
494 
495 template <typename T>
496 struct RefTypeTraits<
497     T, typename std::enable_if<is_proto_enum<T>::value>::type> {
498   typedef RepeatedFieldRefIterator<T> iterator;
499   typedef RepeatedFieldAccessor AccessorType;
500   // We use int32_t for repeated enums in RepeatedFieldAccessor.
501   typedef int32_t AccessorValueType;
502   typedef T IteratorValueType;
503   typedef int32_t* IteratorPointerType;
504   static constexpr FieldDescriptor::CppType cpp_type =
505       FieldDescriptor::CPPTYPE_ENUM;
506   static const Descriptor* GetMessageFieldDescriptor() { return nullptr; }
507 };
508 
509 template <typename T>
510 struct RefTypeTraits<
511     T, typename std::enable_if<std::is_same<std::string, T>::value>::type> {
512   typedef RepeatedFieldRefIterator<T> iterator;
513   typedef RepeatedFieldAccessor AccessorType;
514   typedef std::string AccessorValueType;
515   typedef const std::string IteratorValueType;
516   typedef const std::string* IteratorPointerType;
517   static constexpr FieldDescriptor::CppType cpp_type =
518       FieldDescriptor::CPPTYPE_STRING;
519   static const Descriptor* GetMessageFieldDescriptor() { return nullptr; }
520 };
521 
522 template <typename T>
523 struct MessageDescriptorGetter {
524   static const Descriptor* get() {
525     return T::default_instance().GetDescriptor();
526   }
527 };
528 template <>
529 struct MessageDescriptorGetter<Message> {
530   static const Descriptor* get() { return nullptr; }
531 };
532 
533 template <typename T>
534 struct RefTypeTraits<
535     T, typename std::enable_if<std::is_base_of<Message, T>::value>::type> {
536   typedef RepeatedFieldRefIterator<T> iterator;
537   typedef RepeatedFieldAccessor AccessorType;
538   typedef Message AccessorValueType;
539   typedef const T& IteratorValueType;
540   typedef const T* IteratorPointerType;
541   static constexpr FieldDescriptor::CppType cpp_type =
542       FieldDescriptor::CPPTYPE_MESSAGE;
543   static const Descriptor* GetMessageFieldDescriptor() {
544     return MessageDescriptorGetter<T>::get();
545   }
546 };
547 }  // namespace internal
548 }  // namespace protobuf
549 }  // namespace google
550 
551 #include "google/protobuf/port_undef.inc"
552 
553 #endif  // GOOGLE_PROTOBUF_REFLECTION_H__
554