xdrpp
RFC4506 XDR compiler and message library
types.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 
3 /** \file types.h Type definitions for xdrc compiler output. */
4 
5 #ifndef _XDRC_TYPES_H_HEADER_INCLUDED_
6 #define _XDRC_TYPES_H_HEADER_INCLUDED_ 1
7 
8 #include <array>
9 #include <cassert>
10 #include <cstdint>
11 #include <cstring>
12 #include <memory>
13 #include <stdexcept>
14 #include <string>
15 #include <tuple>
16 #include <utility>
17 #include <vector>
18 #include <limits>
19 
20 #include <xdrpp/endian.h>
21 
22 //! Most of the xdrpp library is encapsulated in the xdr namespace.
23 namespace xdr {
24 
25 using std::uint32_t;
26 
27 inline uint32_t
28 size32(std::size_t s)
29 {
30  uint32_t r {uint32_t(s)};
31  assert(s == r);
32  return r;
33 }
34 
35 
36 ////////////////////////////////////////////////////////////////
37 // Exception types
38 ////////////////////////////////////////////////////////////////
39 
40 //! Generic class of XDR unmarshaling errors.
41 struct xdr_runtime_error : std::runtime_error {
42  using std::runtime_error::runtime_error;
43 };
44 
45 //! Attempt to exceed the bounds of a variable-length array or string.
47  using xdr_runtime_error::xdr_runtime_error;
48 };
49 
50 //! Message not multiple of 4 bytes, or cannot fully be parsed.
52  using xdr_runtime_error::xdr_runtime_error;
53 };
54 
55 //! Attempt to set invalid value for a union discriminant.
57  using xdr_runtime_error::xdr_runtime_error;
58 };
59 
60 //! Padding bytes that should have contained zero don't.
62  using xdr_runtime_error::xdr_runtime_error;
63 };
64 
65 //! Exception for use by \c xdr::xdr_validate.
67  using xdr_runtime_error::xdr_runtime_error;
68 };
69 
70 //! Attempt to access wrong field of a union. Note that this is not
71 //! an \c xdr_runtime_error, because it cannot result from
72 //! unmarshalling garbage arguments. Rather it is a logic error in
73 //! application code that neglected to check the union discriminant
74 //! before accessing the wrong field.
75 struct xdr_wrong_union : std::logic_error {
76  using std::logic_error::logic_error;
77 };
78 
79 
80 ////////////////////////////////////////////////////////////////
81 // Templates for XDR traversal and processing
82 ////////////////////////////////////////////////////////////////
83 
84 namespace detail {
85 // If a class actually contains a method called validate, then the
86 // validate function might as well call it. So we use the standard
87 // gross overload resolution hack that argument 0 will match a
88 // pointer-to-method argument before it will match a ... argument.
89 template<typename T> inline void
90 call_validate(const T &t, decltype(&T::validate))
91 {
92  t.validate();
93 }
94 
95 // Conventional wisdom holds that varargs don't inline, but, perhpas
96 // because we don't actually call va_start, both gcc and clang seem
97 // able to make this compile to nothing (with optimization).
98 template<typename T> inline void
99 call_validate(const T &, ...)
100 {
101 }
102 }
103 
104 //! If this function template is specialized, it provides a means of
105 //! placing extra restrictions on XDR data structures (beyond those of
106 //! the XDR specification). When a specialized \c xdr::validate
107 //! function detects a bad data argument, it should throw an exception
108 //! of type \c xdr::xdr_invariant_failed. Note this mechanism only
109 //! works for user-defined XDR structs and unions. It does not work
110 //! for enums, typedef aliases, or built-in types (int, hyper, string,
111 //! vectors, etc.).
112 template<typename T> inline void
113 validate(const T &t)
114 {
115  detail::call_validate(t, 0);
116 }
117 
118 //! This is used to apply an archive to a field. It is designed as a
119 //! template class that can be specialized to various archive formats,
120 //! since some formats may want the fied name and others not. Other
121 //! uses include translating types to supertypes, e.g., so an archive
122 //! can handle \c std::string instead of \c xdr::xstring. Never
123 //! invoke \c archive_adapter::apply directly. Instead, call \c
124 //! xdr::archive, as the latter may be specialized for certain types.
125 template<typename Archive> struct archive_adapter {
126  template<typename T> static void apply(Archive &ar, T &&t, const char *) {
127  ar(std::forward<T>(t));
128  }
129 };
130 
131 //! By default, this function simply applies \c ar (which must be a
132 //! function object) to \c t. However, it does so via the \c
133 //! xdr::archive_adapter template class, which can be specialized to
134 //! capture the field name as well. Never specialize or overload this
135 //! function on \c Archive (specialize \c xdr::archive_adapter
136 //! instead). However, in special cases (such as \c
137 //! xdr::transparent_ptr) it is reasonable to specialize this function
138 //! template on \c T.
139 template<typename Archive, typename T> inline void
140 archive(Archive &ar, T &&t, const char *name = nullptr)
141 {
142  archive_adapter<Archive>::apply(ar, std::forward<T>(t), name);
143 }
144 
145 //! Metadata for all marshalable XDR types.
146 template<typename T> struct xdr_traits {
147  //! \c T is a valid XDR type that can be serialized.
148  static Constexpr const bool valid = false;
149  //! \c T is defined by xdrpp/xdrc (as opposed to native or std types).
150  static Constexpr const bool xdr_defined = false;
151  //! \c T is an \c xstring, \c opaque_array, or \c opaque_vec.
152  static Constexpr const bool is_bytes = false;
153  //! \c T is an XDR struct.
154  static Constexpr const bool is_struct = false;
155  //! \c T is an XDR union.
156  static Constexpr const bool is_union = false;
157  //! \c T is an XDR struct or union.
158  static Constexpr const bool is_class = false;
159  //! \c T is an XDR enum or bool (traits have enum_name).
160  static Constexpr const bool is_enum = false;
161  //! \c T is an xdr::pointer, xdr::xarray, or xdr::xvector (with load/save).
162  static Constexpr const bool is_container = false;
163  //! \c T is one of [u]int{32,64}_t, float, or double.
164  static Constexpr const bool is_numeric = false;
165  //! \c T has a fixed size.
166  static Constexpr const bool has_fixed_size = false;
167 };
168 
169 namespace detail {
170 // When a type T includes itself recursively (for instance because it
171 // contains a vector of itself), xdr_traits<T> will be incomplete at
172 // some points where one needs to know if the structure has a fixed
173 // size. However, such recursive structures don't have a fixed size,
174 // anyway, so it is okay to short-circuit and return a false
175 // has_fixed_size. The point of has_fixed_size_t is to allow
176 // specializations (notably for xvector<T>) that short-cirtuit to
177 // false.
178 template<typename T> struct has_fixed_size_t
179  : std::integral_constant<bool, xdr_traits<T>::has_fixed_size> {};
180 }
181 
182 //! Return the marshaled size of an XDR data type.
183 template<typename T> std::size_t
184 xdr_size(const T&t)
185 {
186  return xdr_traits<T>::serial_size(t);
187 }
188 
189 //! Default xdr_traits values for actual XDR types, used as a
190 //! supertype for most xdr::xdr_traits specializations.
192  static Constexpr const bool valid = true;
193  static Constexpr const bool xdr_defined = true;
194  static Constexpr const bool is_bytes = false;
195  static Constexpr const bool is_class = false;
196  static Constexpr const bool is_enum = false;
197  static Constexpr const bool is_container = false;
198  static Constexpr const bool is_numeric = false;
199  static Constexpr const bool is_struct = false;
200  static Constexpr const bool is_union = false;
201  static Constexpr const bool has_fixed_size = false;
202 };
203 
204 
205 ////////////////////////////////////////////////////////////////
206 // Support for numeric types and bool
207 ////////////////////////////////////////////////////////////////
208 
209 //! A reinterpret-cast like function that works between types such as
210 //! floating-point and integers of the same size. Used in marshaling,
211 //! so that a single set of byteswap routines can be used on all
212 //! numeric types including floating point. Uses a union to avoid
213 //! strict pointer aliasing problems.
214 template<typename To, typename From> inline To
216 {
217  static_assert(sizeof(To) == sizeof(From),
218  "xdr_reinterpret with different sized objects");
219  union {
220  From from;
221  To to;
222  };
223  from = f;
224  return to;
225 }
226 
227 //! Default traits for use as supertype of specializations of \c
228 //! xdr_traits for integral types.
229 template<typename T, typename U> struct xdr_integral_base : xdr_traits_base {
230  using type = T;
231  using uint_type = U;
232  static Constexpr const bool xdr_defined = false;
233  static Constexpr const bool is_numeric = true;
234  static Constexpr const bool has_fixed_size = true;
235  static Constexpr const std::size_t fixed_size = sizeof(uint_type);
236  static Constexpr const std::size_t serial_size(type) { return fixed_size; }
237  static uint_type to_uint(type t) { return t; }
238  static type from_uint(uint_type u) {
239  return xdr_reinterpret<type>(u);
240  }
241 };
242 template<> struct xdr_traits<std::int32_t>
243  : xdr_integral_base<std::int32_t, std::uint32_t> {
244  // Numeric type for case labels in switch statements
245  using case_type = std::int32_t;
246 };
247 template<> struct xdr_traits<std::uint32_t>
248  : xdr_integral_base<std::uint32_t, std::uint32_t> {
249  using case_type = std::uint32_t;
250 };
251 template<> struct xdr_traits<std::int64_t>
252  : xdr_integral_base<std::int64_t, std::uint64_t> {};
253 template<> struct xdr_traits<std::uint64_t>
254  : xdr_integral_base<std::uint64_t, std::uint64_t> {};
255 
256 //! Default traits for use as supertype of specializations of \c
257 //! xdr_traits for floating-point types.
258 template<typename T, typename U> struct xdr_fp_base : xdr_traits_base {
259  using type = T;
260  using uint_type = U;
261  static_assert(sizeof(type) == sizeof(uint_type),
262  "Cannot reinterpret between float and int of different size");
263  static Constexpr const bool xdr_defined = false;
264  static Constexpr const bool is_numeric = true;
265  static Constexpr const bool has_fixed_size = true;
266  static Constexpr const std::size_t fixed_size = sizeof(uint_type);
267  static Constexpr std::size_t serial_size(type) { return fixed_size; }
268 
269  static uint_type to_uint(type t) { return xdr_reinterpret<uint_type>(t); }
270  static type from_uint(uint_type u) { return xdr_reinterpret<type>(u); }
271 };
272 template<> struct xdr_traits<float>
273  : xdr_fp_base<float, std::uint32_t> {};
274 template<> struct xdr_traits<double>
275  : xdr_fp_base<double, std::uint64_t> {};
276 
277 
278 template<> struct xdr_traits<bool>
279  : xdr_integral_base<bool, std::uint32_t> {
280  using case_type = std::int32_t;
281  static Constexpr const bool xdr_defined = false;
282  static Constexpr const bool is_enum = true;
283  static Constexpr const bool is_numeric = false;
284  static type from_uint(uint_type u) { return u != 0; }
285  static Constexpr const char *enum_name(uint32_t b) {
286  return b == 0 ? "FALSE" : b == 1 ? "TRUE" : nullptr;
287  }
288  static const std::vector<int32_t> &enum_values() {
289  static const std::vector<int32_t> v = { 0, 1 };
290  return v;
291  }
292 };
293 
294 
295 ////////////////////////////////////////////////////////////////
296 // XDR containers (xvector, xarrray, pointer) and bytes (xstring,
297 // opaque_vec, opaque_array)
298 ////////////////////////////////////////////////////////////////
299 
300 //! Maximum length of vectors. (The RFC says 0xffffffff, but out of
301 //! paranoia for integer overflows we chose something that still fits
302 //! in 32 bits when rounded up to a multiple of four.)
303 static Constexpr const uint32_t XDR_MAX_LEN = 0xfffffffc;
304 
305 namespace detail {
306 //! Convenience supertype for traits of the three container types
307 //! (xarray, xvectors, and pointer).
308 template<typename T, bool variable,
309  bool VFixed = detail::has_fixed_size_t<typename T::value_type>::value>
310 struct xdr_container_base : xdr_traits_base {
311  using value_type = typename T::value_type;
312  static Constexpr const bool is_container = true;
313  //! Container has variable number of elements
314  static Constexpr const bool variable_nelem = variable;
315  static Constexpr const bool has_fixed_size = false;
316 
317  template<typename Archive> static void save(Archive &a, const T &t) {
318  if (variable)
319  archive(a, size32(t.size()));
320  for (const value_type &v : t)
321  archive(a, v);
322  }
323  template<typename Archive> static void load(Archive &a, T &t) {
324  uint32_t n;
325  if (variable) {
326  archive(a, n);
327  t.check_size(n);
328  if (t.size() > n)
329  t.resize(n);
330  }
331  else
332  n = size32(t.size());
333  for (uint32_t i = 0; i < n; ++i)
334  archive(a, t.extend_at(i));
335  }
336  static std::size_t serial_size(const T &t) {
337  std::size_t s = variable ? 4 : 0;
338  for (const value_type &v : t)
340  return s;
341  }
342 };
343 
344 template<typename T> struct xdr_container_base<T, true, true>
345  : xdr_container_base<T, true, false> {
346  static std::size_t serial_size(const T &t) {
348  }
349 };
350 
351 template<typename T> struct xdr_container_base<T, false, true>
352  : xdr_container_base<T, false, false> {
353  static Constexpr const bool has_fixed_size = true;
354  static Constexpr const std::size_t fixed_size =
355  T::container_fixed_nelem * xdr_traits<typename T::value_type>::fixed_size;
356  static std::size_t serial_size(const T &) { return fixed_size; }
357 };
358 
359 //! Placeholder type to avoid clearing array
360 struct no_clear_t {
361  Constexpr no_clear_t() {}
362 };
363 Constexpr const no_clear_t no_clear;
364 } // namespace detail
365 
366 //! XDR arrays are implemented using std::array as a supertype.
367 template<typename T, uint32_t N> struct xarray
368  : std::array<T, size_t(N)> {
369  using array = std::array<T, size_t(N)>;
370  xarray() { array::fill(T{}); }
371  xarray(detail::no_clear_t) {}
372  xarray(const xarray &) = default;
373  xarray &operator=(const xarray &) = default;
374 
375  static Constexpr const std::size_t container_fixed_nelem = N;
376  static Constexpr const std::size_t size() { return N; }
377  static void validate() {}
378  static void check_size(uint32_t i) {
379  if (i != N)
380  throw xdr_overflow("invalid size in xdr::xarray");
381  }
382  static void resize(uint32_t i) {
383  if (i != N)
384  throw xdr_overflow("invalid resize in xdr::xarray");
385  }
386  T &extend_at(uint32_t i) {
387  if (i >= N)
388  throw xdr_overflow("attempt to access invalid position in xdr::xarray");
389  return (*this)[i];
390  }
391 };
392 
393 template<typename T, uint32_t N>
394 struct xdr_traits<xarray<T,N>>
395  : detail::xdr_container_base<xarray<T,N>, false> {};
396 
397 //! XDR \c opaque is represented as std::uint8_t;
398 template<uint32_t N = XDR_MAX_LEN> struct opaque_array
399  : xarray<std::uint8_t,N> {
401  using xarray::xarray;
402  // Pay a little performance to avoid heartbleed-type errors...
403  opaque_array() : xarray(detail::no_clear) { std::memset(this->data(), 0, N); }
404  opaque_array(detail::no_clear_t) : xarray(detail::no_clear) {}
405 };
406 template<uint32_t N> struct xdr_traits<opaque_array<N>> : xdr_traits_base {
407  static Constexpr const bool is_bytes = true;
408  static Constexpr const std::size_t has_fixed_size = true;
409  static Constexpr const std::size_t fixed_size =
410  (std::size_t(N) + std::size_t(3)) & ~std::size_t(3);
411  static std::size_t serial_size(const opaque_array<N> &) { return fixed_size; }
412  static Constexpr const bool variable_nelem = false;
413 };
414 
415 
416 //! A vector with a maximum size (returned by xvector::max_size()).
417 //! Note that you can exceed the size, but an error will happen when
418 //! marshaling or unmarshaling the data structure.
419 template<typename T, uint32_t N = XDR_MAX_LEN>
420 struct xvector : std::vector<T> {
421  using vector = std::vector<T>;
422  using vector::vector;
423 
424  //! Return the maximum size allowed by the type.
425  static Constexpr uint32_t max_size() { return N; }
426 
427  //! Check whether a size is in bounds
428  static void check_size(size_t n) {
429  if (n > max_size())
430  throw xdr_overflow("xvector overflow");
431  }
432 
433  void append(const T *elems, std::size_t n) {
434  check_size(this->size() + n);
435  this->insert(this->end(), elems, elems + n);
436  }
437  T &extend_at(uint32_t i) {
438  if (i >= N)
439  throw xdr_overflow("attempt to access invalid position in xdr::xvector");
440  if (i == this->size())
441  this->emplace_back();
442  return (*this)[i];
443  }
444  void resize(uint32_t n) {
445  check_size(n);
446  vector::resize(n);
447  }
448 };
449 
450 namespace detail {
451 template<typename T> struct has_fixed_size_t<xvector<T>> : std::false_type {};
452 }
453 
454 template<typename T, uint32_t N> struct xdr_traits<xvector<T,N>>
455  : detail::xdr_container_base<xvector<T,N>, true> {};
456 
457 //! Variable-length opaque data is just a vector of std::uint8_t.
458 template<uint32_t N = XDR_MAX_LEN> using opaque_vec = xvector<std::uint8_t, N>;
459 template<uint32_t N>
460 struct xdr_traits<xvector<std::uint8_t, N>> : xdr_traits_base {
461  static Constexpr const bool is_bytes = true;
462  static Constexpr const bool has_fixed_size = false;;
463  static Constexpr std::size_t serial_size(const opaque_vec<N> &a) {
464  return (std::size_t(a.size()) + std::size_t(7)) & ~std::size_t(3);
465  }
466  static Constexpr const bool variable_nelem = true;
467 };
468 
469 
470 //! A string with a maximum length (returned by xstring::max_size()).
471 //! Note that you can exceed the size, but an error will happen when
472 //! marshaling or unmarshaling the data structure.
473 template<uint32_t N = XDR_MAX_LEN> struct xstring : std::string {
474  using string = std::string;
475 
476  //! Return the maximum size allowed by the type.
477  static Constexpr uint32_t max_size() { return N; }
478 
479  //! Check whether a size is in bounds
480  static void check_size(size_t n) {
481  if (n > max_size())
482  throw xdr_overflow("xstring overflow");
483  }
484 
485  //! Check that the string length is not greater than the maximum
486  //! size. \throws std::out_of_range and clears the contents of the
487  //! string if it is too long.
488  void validate() const { check_size(size()); }
489 
490 #if !MSVC
491  xstring() = default;
492  xstring(const xstring &) = default;
493  xstring(xstring &&) = default;
494  xstring &operator=(const xstring &) = default;
495  xstring &operator=(xstring &&) = default;
496 #endif // !MSVC
497 
498  template<typename...Args> xstring(Args&&...args)
499  : string(std::forward<Args>(args)...) { validate(); }
500 
501  using string::data;
502  char *data() { return &(*this)[0]; } // protobufs does this, so probably ok
503 
504 //! \hideinitializer
505 #define ASSIGN_LIKE(method) \
506  template<typename...Args> xstring &method(Args&&...args) { \
507  string::method(std::forward<Args>(args)...); \
508  validate(); \
509  return *this; \
510  }
511  ASSIGN_LIKE(operator=)
512  ASSIGN_LIKE(operator+=)
513  ASSIGN_LIKE(append)
514  ASSIGN_LIKE(push_back)
515  ASSIGN_LIKE(assign)
516  ASSIGN_LIKE(insert)
517  ASSIGN_LIKE(replace)
518  ASSIGN_LIKE(swap)
519 #undef ASSIGN_LIKE
520 
521  void resize(size_type n) { check_size(n); string::resize(n); }
522  void resize(size_type n, char ch) { check_size(n); string::resize(n, ch); }
523 };
524 
525 template<uint32_t N> struct xdr_traits<xstring<N>> : xdr_traits_base {
526  static Constexpr const bool is_bytes = true;
527  static Constexpr const bool has_fixed_size = false;;
528  static Constexpr std::size_t serial_size(const xstring<N> &a) {
529  return (std::size_t(a.size()) + std::size_t(7)) & ~std::size_t(3);
530  }
531  static Constexpr const bool variable_nelem = true;
532 };
533 
534 
535 //! Optional data (represented with pointer notation in XDR source).
536 template<typename T> struct pointer : std::unique_ptr<T> {
537  using value_type = T;
538  using std::unique_ptr<T>::unique_ptr;
539  using std::unique_ptr<T>::get;
540  pointer() = default;
541  pointer(const pointer &p) : std::unique_ptr<T>(p ? new T(*p) : nullptr) {}
542  pointer(pointer &&p) = default;
543  pointer &operator=(const pointer &up) {
544  if (const T *tp = up.get()) {
545  if (T *selfp = this->get())
546  *selfp = *tp;
547  else
548  this->reset(new T(*tp));
549  }
550  else
551  this->reset();
552  return *this;
553  }
554  pointer &operator=(pointer &&) = default;
555 
556  static void check_size(uint32_t n) {
557  if (n > 1)
558  throw xdr_overflow("xdr::pointer size must be 0 or 1");
559  }
560  uint32_t size() const { return *this ? 1 : 0; }
561  T *begin() { return get(); }
562  const T *begin() const { return get(); }
563  T *end() { return begin() + size(); }
564  const T *end() const { return begin() + size(); }
565  T &extend_at(uint32_t i) {
566  if (i != 0)
567  throw xdr_overflow("attempt to access position > 0 in xdr::pointer");
568  if (!size())
569  this->reset(new T);
570  return **this;
571  }
572  void resize(uint32_t n) {
573  if (n == size())
574  return;
575  switch(n) {
576  case 0:
577  this->reset();
578  break;
579  case 1:
580  this->reset(new T);
581  break;
582  default:
583  throw xdr_overflow("xdr::pointer::resize: valid sizes are 0 and 1");
584  }
585  }
586  T &activate() {
587  if (!*this)
588  this->reset(new T{});
589  return *this->get();
590  }
591 
592  //! Compare by value, rather than looking at the value of the pointer.
593  friend bool operator==(const pointer &a, const pointer &b) {
594  return (!a && !b) || (a && b && *a == *b);
595  }
596  friend bool operator!=(const pointer &a, const pointer &b) {
597  return !(a == b);
598  }
599  friend bool operator<(const pointer &a, const pointer &b) {
600  return (!a && b) || (a && b && *a < *b);
601  }
602  friend bool operator>(const pointer &a, const pointer &b) {
603  return b < a;
604  }
605  friend bool operator<=(const pointer &a, const pointer &b) {
606  return !(b < a);
607  }
608  friend bool operator>=(const pointer &a, const pointer &b) {
609  return !(a < b);
610  }
611 };
612 
613 // Note an explicit third template argument (VFixed = false) is
614 // required because pointers are used recursively, so we might not
615 // have xdr_traits<T> available at the time we instantiate
616 // xdr_traits<pointer<T>>.
617 template<typename T> struct xdr_traits<pointer<T>>
618  : detail::xdr_container_base<pointer<T>, true, false> {};
619 
620 
621 ////////////////////////////////////////////////////////////////
622 // Support for XDR struct types
623 ////////////////////////////////////////////////////////////////
624 
625 //! Type-level representation of a pointer-to-member value. When used
626 //! as a function object, dereferences the field, and returns it as
627 //! the same reference type as its argument (lvalue rference, const
628 //! lvalue reference, or const rvalue reference).
629 template<typename T, typename F, F T::*Ptr> struct field_ptr {
630  using class_type = T;
631  using field_type = F;
632  using value_type = F T::*;
633  //static constexpr value_type value = Ptr;
634  static Constexpr value_type value() { return Ptr; }
635  F &operator()(T &t) const { return t.*Ptr; }
636  const F &operator()(const T &t) const { return t.*Ptr; }
637  F &operator()(T &&t) const { return std::move(t.*Ptr); }
638 };
639 
640 template<typename ...Fields> struct xdr_struct_base;
641 
642 namespace detail {
643 //! Default traits for fixed-size structures.
644 template<typename FP, typename ...Fields>
645  struct xdr_struct_base_fs : xdr_struct_base<Fields...> {
646  static Constexpr const bool has_fixed_size = true;
647  static Constexpr const std::size_t fixed_size =
649  + xdr_struct_base<Fields...>::fixed_size);
650  static Constexpr std::size_t serial_size(const typename FP::class_type &) {
651  return fixed_size;
652  }
653 };
654 //! Default traits for variable-size structures.
655 template<typename FP, typename ...Fields>
656  struct xdr_struct_base_vs : xdr_struct_base<Fields...> {
657  static Constexpr const bool has_fixed_size = false;
658  static std::size_t serial_size(const typename FP::class_type &t) {
659  return (xdr_size(t.*(FP::value()))
661  }
662 };
663 }
664 
665 //! Supertype to construct XDR traits of structure objects, used in
666 //! output of the \c xdrc compiler.
667 template<> struct xdr_struct_base<> : xdr_traits_base {
668  static Constexpr const bool is_class = true;
669  static Constexpr const bool is_struct = true;
670  static Constexpr const bool has_fixed_size = true;
671  static Constexpr const std::size_t fixed_size = 0;
672  template<typename T> static Constexpr std::size_t serial_size(const T&) {
673  return fixed_size;
674  }
675 };
676 template<typename FP, typename ...Rest> struct xdr_struct_base<FP, Rest...>
677  : std::conditional<(detail::has_fixed_size_t<typename FP::field_type>::value
678  && xdr_struct_base<Rest...>::has_fixed_size),
679  detail::xdr_struct_base_fs<FP, Rest...>,
680  detail::xdr_struct_base_vs<FP, Rest...>>::type {
681  using field_info = FP;
682  using next_field = xdr_struct_base<Rest...>;
683 };
684 
685 
686 ////////////////////////////////////////////////////////////////
687 // XDR-compatible representations of std::tuple and xdr_void
688 ////////////////////////////////////////////////////////////////
689 
690 //! Placeholder type used to contain a parameter pack of tuple
691 //! indices, so as to unpack a tuple in function call arguments.
692 template<std::size_t...N> struct indices {
693  Constexpr indices() {}
694 };
695 
696 namespace detail {
697 
698 template<typename T, typename U> struct cat_indices;
699 template<std::size_t...M, std::size_t...N>
700 struct cat_indices<indices<M...>, indices<N...>> {
701  using type = indices<M..., N...>;
702 };
703 
704 template<std::size_t N> struct all_indices_helper {
705  using type = typename cat_indices<typename all_indices_helper<N-1>::type,
706  indices<N-1>>::type;
707 };
708 template<> struct all_indices_helper<0> {
709  using type = indices<>;
710 };
711 
712 //! A type representing all tuple indices from 0 to N-1.
713 template<std::size_t N> using all_indices =
714  typename all_indices_helper<N>::type;
715 
716 template<std::size_t N, typename T> struct tuple_base;
717 
718 template<std::size_t N, typename T> struct tuple_base_fs;
719 template<std::size_t N, typename...T>
720 struct tuple_base_fs<N, std::tuple<T...>> : xdr_traits_base {
721  using type = std::tuple<T...>;
722  using elem_type = typename std::remove_cv<
723  typename std::remove_reference<
724  typename std::tuple_element<N-1, type>::type>::type>::type;
725  using next = tuple_base<N-1, type>;
726  static Constexpr const bool xdr_defined = false;
727  static Constexpr const bool is_class = true;
728  static Constexpr const bool is_struct = true;
729  static Constexpr const bool has_fixed_size = true;
730  static Constexpr const std::uint32_t fixed_size =
731  xdr_traits<elem_type>::fixed_size + next::fixed_size;
732  static Constexpr std::size_t serial_size(const type &) { return fixed_size; }
733 };
734 
735 template<std::size_t N, typename T> struct tuple_base_vs;
736 template<std::size_t N, typename...T>
737 struct tuple_base_vs<N, std::tuple<T...>> : xdr_traits_base {
738  using type = std::tuple<T...>;
739  using elem_type = typename std::remove_cv<
740  typename std::remove_reference<
741  typename std::tuple_element<N-1, type>::type>::type>::type;
742  using next = tuple_base<N-1, type>;
743  static Constexpr const bool xdr_defined = false;
744  static Constexpr const bool is_class = true;
745  static Constexpr const bool is_struct = true;
746  static Constexpr const bool has_fixed_size = false;
747  static std::size_t serial_size(const type &t) {
748  return (xdr_traits<elem_type>::serial_size(std::get<N-1>(t))
749  + next::serial_size(t));
750  }
751 };
752 
753 template<typename...T> struct tuple_base<0, std::tuple<T...>>
754  : xdr_traits_base {
755  using type = std::tuple<T...>;
756  static Constexpr const bool xdr_defined = false;
757  static Constexpr const bool is_class = true;
758  static Constexpr const bool is_struct = true;
759  static Constexpr const bool has_fixed_size = true;
760  static Constexpr const std::size_t fixed_size = 0;
761  static Constexpr std::size_t serial_size(const type &) { return fixed_size; }
762 
763  template<typename Archive> static void save(Archive &ar, const type &obj) {}
764  template<typename Archive> static void load(Archive &ar, type &obj) {}
765 };
766 
767 template<std::size_t N, typename...T> struct tuple_suffix_fixed_size {
768  using type = std::tuple<T...>;
769  using elem_type = typename std::tuple_element<N-1, type>::type;
770  static Constexpr const bool value =
772  && tuple_suffix_fixed_size<N-1, T...>::value;
773 };
774 template<typename...T> struct tuple_suffix_fixed_size<0, T...> {
775  static Constexpr const bool value = true;
776 };
777 
778 template<std::size_t N, typename...T> struct tuple_base<N, std::tuple<T...>>
779  : std::conditional<tuple_suffix_fixed_size<N, T...>::value,
780  tuple_base_fs<N, std::tuple<T...>>,
781  tuple_base_vs<N, std::tuple<T...>>>::type {
782  using type = std::tuple<T...>;
783 
784 #if MSVC
785  static const char *name() { return "<size unavailable on broken compiler>"; }
786 #else // !MSVC
787  static const char *name() {
788  static std::string n = "<" + std::to_string(N-1) + ">";
789  return n.c_str();
790  }
791 #endif // !MSVC
792  template<typename Archive> static void save(Archive &ar, const type &obj) {
793  tuple_base<N-1, type>::save(ar, obj);
794  archive(ar, std::get<N-1>(obj), name());
795  }
796  template<typename Archive> static void load(Archive &ar, type &obj) {
797  tuple_base<N-1, type>::load(ar, obj);
798  archive(ar, std::get<N-1>(obj), name());
799  }
800 };
801 }
802 
803 //! A type representing all the indices of a particuar tuple.
804 template<typename T> using all_indices_of =
805  typename detail::all_indices<std::tuple_size<
806  typename std::remove_reference<T>::type>::value>;
807 
808 template<typename...T> struct xdr_traits<std::tuple<T...>>
809  : detail::tuple_base<sizeof...(T), std::tuple<T...>> {};
810 
811 //! Placehoder type representing void values marshaled as 0 bytes.
812 using xdr_void = std::tuple<>;
813 
814 
815 ////////////////////////////////////////////////////////////////
816 // XDR union types
817 ////////////////////////////////////////////////////////////////
818 
819 namespace detail {
820 //! Dereference a pointer to a member of type \c F of a class of type
821 //! \c T, preserving reference types. Hence applying to an lvalue
822 //! reference \c T returns an lvalue reference \c F, while an rvalue
823 //! reference \c T produces an rvalue reference \c F.
824 template<typename T, typename F> inline F &
825 member(T &t, F T::*mp)
826 {
827  return t.*mp;
828 }
829 template<typename T, typename F> inline const F &
830 member(const T &t, F T::*mp)
831 {
832  return t.*mp;
833 }
834 template<typename T, typename F> inline F &&
835 member(T &&t, F T::*mp)
836 {
837  return std::move(t.*mp);
838 }
839 } // namespace detail
840 
842  Constexpr field_constructor_t() {}
843  template<typename T, typename F> void operator()(F T::*mp, T &t) const {
844  new (&(t.*mp)) F{};
845  }
846  template<typename T, typename F, typename TT> void
847  operator()(F T::*mp, T &t, TT &&tt) const {
848  new (&(t.*mp)) F (detail::member(std::forward<TT>(tt), mp));
849  }
850 };
851 //! Passed to the auto-generated _xdr_with_mem_ptr static method to
852 //! construct the active union field (or at least the union field
853 //! corresponding to the second argument to _xdr_with_mem_ptr, which
854 //! should be the active union field).
856 
858  Constexpr field_destructor_t() {}
859  template<typename T, typename F> void
860  operator()(F T::*mp, T &t) const { detail::member(t, mp).~F(); }
861 };
862 //! Passed to _xdr_with_mem_ptr to destroy the active union field.
864 
866  Constexpr field_assigner_t() {}
867  template<typename T, typename F, typename TT> void
868  operator()(F T::*mp, T &t, TT &&tt) const {
869  detail::member(t, mp) = detail::member(std::forward<TT>(tt), mp);
870  }
871 };
872 //! Passed to _xdr_with_mem_ptr to assign to the active union field.
873 Constexpr const field_assigner_t field_assigner {};
874 
876  Constexpr field_archiver_t() {}
877 
878  template<typename F, typename T, typename Archive> void
879  operator()(F T::*mp, Archive &ar, T &t, const char *name) const {
880  archive(ar, detail::member(t, mp), name);
881  }
882  template<typename F, typename T, typename Archive> void
883  operator()(F T::*mp, Archive &ar, const T &t, const char *name) const {
884  archive(ar, detail::member(t, mp), name);
885  }
886 };
887 //! Passed to _xdr_with_mem_ptr to archive the active union field.
888 Constexpr const field_archiver_t field_archiver {};
889 
890 struct field_size_t {
891  Constexpr field_size_t() {}
892  template<typename F, typename T> void
893  operator()(F T::*mp, const T &t, std::size_t &size) const {
894  size = xdr_traits<F>::serial_size(detail::member(t, mp));
895  }
896 };
897 //! Passed to _xdr_with_mem_ptr to compute the size of the active
898 //! union field.
899 Constexpr const field_size_t field_size {};
900 
901 
902 ////////////////////////////////////////////////////////////////
903 // Comparison operators
904 ////////////////////////////////////////////////////////////////
905 
906 namespace detail {
907 template<typename T, typename F> struct struct_equal_helper {
908  static bool equal(const T &a, const T &b) {
909  Constexpr const typename F::field_info fi {};
910  if (!(fi(a) == fi(b)))
911  return false;
912  return struct_equal_helper<T, typename F::next_field>::equal(a, b);
913  }
914 };
915 template<typename T> struct struct_equal_helper<T, xdr_struct_base<>> {
916  static bool equal(const T &, const T &) { return true; }
917 };
918 
919 template<typename T, typename F> struct struct_lt_helper {
920  static bool lt(const T &a, const T &b) {
921  Constexpr const typename F::field_info fi {};
922  if ((fi(a) < fi(b)))
923  return true;
924  if ((fi(b) < fi(a)))
925  return false;
926  return struct_lt_helper<T, typename F::next_field>::lt(a, b);
927  }
928 };
929 template<typename T> struct struct_lt_helper<T, xdr_struct_base<>> {
930  static bool lt(const T &, const T &) { return false; }
931 };
932 } // namespace detail
933 
934 
935 //! Equality for XDR structures. To use this operator, you will have
936 //! to include using declaration <tt>using xdr::operator==</tt> in the
937 //! namespace of your XDR file. Note that a <tt>using namespace
938 //! xdr</tt> using \e directive (as opposed to \e declaration) is
939 //! insufficient, because the C++ standard explicitly prevents using
940 //! directives from impacting argument-dependent lookup. A <tt>using
941 //! namespace xdr</tt> directive at global scope is also insufficient,
942 //! though more subtly, because standard library functions for
943 //! comparing vectors will fail.
944 template<typename T> inline typename
945 std::enable_if<xdr_traits<T>::is_struct && xdr_traits<T>::xdr_defined,
946  bool>::type
947 operator==(const T &a, const T &b)
948 {
949  return detail::struct_equal_helper<T, xdr_traits<T>>::equal(a, b);
950 }
951 
952 template<typename T> inline typename
953 std::enable_if<xdr_traits<T>::is_struct && xdr_traits<T>::xdr_defined,
954  bool>::type
955 operator!=(const T &a, const T &b)
956 {
957  return !(a == b);
958 }
959 
960 //! Ordering of XDR structures. See note at \c xdr::operator==.
961 template<typename T> inline typename
962 std::enable_if<xdr_traits<T>::is_struct && xdr_traits<T>::xdr_defined,
963  bool>::type
964 operator<(const T &a, const T &b)
965 {
966  return detail::struct_lt_helper<T, xdr_traits<T>>::lt(a, b);
967 }
968 
969 template<typename T> inline typename
970 std::enable_if<xdr_traits<T>::is_struct && xdr_traits<T>::xdr_defined,
971  bool>::type
972 operator>(const T &a, const T &b)
973 {
974  return b < a;
975 }
976 
977 template<typename T> inline typename
978 std::enable_if<xdr_traits<T>::is_struct && xdr_traits<T>::xdr_defined,
979  bool>::type
980 operator<=(const T &a, const T &b)
981 {
982  return !(b < a);
983 }
984 
985 template<typename T> inline typename
986 std::enable_if<xdr_traits<T>::is_struct && xdr_traits<T>::xdr_defined,
987  bool>::type
988 operator>=(const T &a, const T &b)
989 {
990  return !(a < b);
991 }
992 
993 namespace detail {
994 struct union_field_equal_t {
995  Constexpr union_field_equal_t() {}
996  template<typename T, typename F>
997  void operator()(F T::*mp, const T &a, const T &b, bool &out) const {
998  out = a.*mp == b.*mp;
999  }
1000 };
1001 Constexpr const union_field_equal_t union_field_equal {};
1002 
1003 struct union_field_lt_t {
1004  Constexpr union_field_lt_t() {}
1005  template<typename T, typename F>
1006  void operator()(F T::*mp, const T &a, const T &b, bool &out) const {
1007  out = a.*mp < b.*mp;
1008  }
1009 };
1010 Constexpr const union_field_lt_t union_field_lt {};
1011 } // namespace detail
1012 
1013 //! Equality of XDR unions. See note at \c xdr::operator== for XDR
1014 //! structs.
1015 template<typename T> inline typename
1016 std::enable_if<xdr_traits<T>::is_union, bool>::type
1017 operator==(const T &a, const T &b)
1018 {
1019  if (a._xdr_discriminant() == b._xdr_discriminant()) {
1020  bool r{true};
1021  a._xdr_with_mem_ptr(detail::union_field_equal,
1022  a._xdr_discriminant(), a, b, r);
1023  return r;
1024  }
1025  return false;
1026 }
1027 
1028 //! Ordering of XDR unions. See note at \c xdr::operator==.
1029 template<typename T> inline typename
1030 std::enable_if<xdr_traits<T>::is_union, bool>::type
1031 operator<(const T &a, const T &b)
1032 {
1033  if (a._xdr_discriminant() < b._xdr_discriminant())
1034  return true;
1035  if (b._xdr_discriminant() < a._xdr_discriminant())
1036  return false;
1037 
1038  bool r{false};
1039  a._xdr_with_mem_ptr(detail::union_field_lt,
1040  a._xdr_discriminant(), a, b, r);
1041  return r;
1042 }
1043 
1044 } // namespace xdr
1045 
1046 #endif // !_XDRC_TYPES_H_HEADER_INCLUDED_
1047 
std::size_t xdr_size(const T &t)
Return the marshaled size of an XDR data type.
Definition: types.h:184
Constexpr const field_constructor_t field_constructor
Passed to the auto-generated _xdr_with_mem_ptr static method to construct the active union field (or ...
Definition: types.h:855
std::tuple<> xdr_void
Placehoder type representing void values marshaled as 0 bytes.
Definition: types.h:812
This is used to apply an archive to a field.
Definition: types.h:125
Constexpr const field_size_t field_size
Passed to _xdr_with_mem_ptr to compute the size of the active union field.
Definition: types.h:899
Attempt to access wrong field of a union.
Definition: types.h:75
Attempt to exceed the bounds of a variable-length array or string.
Definition: types.h:46
Constexpr const field_destructor_t field_destructor
Passed to _xdr_with_mem_ptr to destroy the active union field.
Definition: types.h:863
Most of the xdrpp library is encapsulated in the xdr namespace.
Definition: arpc.cc:4
Definition: socket.h:47
XDR arrays are implemented using std::array as a supertype.
Definition: types.h:367
A string with a maximum length (returned by xstring::max_size()).
Definition: types.h:473
std::enable_if< xdr_traits< T >::is_struct &&xdr_traits< T >::xdr_defined, bool >::type operator==(const T &a, const T &b)
Equality for XDR structures.
Definition: types.h:947
Exception for use by xdr::xdr_validate.
Definition: types.h:66
Placeholder type used to contain a parameter pack of tuple indices, so as to unpack a tuple in functi...
Definition: types.h:692
Constexpr const field_assigner_t field_assigner
Passed to _xdr_with_mem_ptr to assign to the active union field.
Definition: types.h:873
Default traits for use as supertype of specializations of xdr_traits for integral types...
Definition: types.h:229
Default traits for use as supertype of specializations of xdr_traits for floating-point types...
Definition: types.h:258
Generic class of XDR unmarshaling errors.
Definition: types.h:41
Optional data (represented with pointer notation in XDR source).
Definition: types.h:536
XDR opaque is represented as std::uint8_t;.
Definition: types.h:398
Attempt to set invalid value for a union discriminant.
Definition: types.h:56
static void check_size(size_t n)
Check whether a size is in bounds.
Definition: types.h:480
Default xdr_traits values for actual XDR types, used as a supertype for most xdr::xdr_traits speciali...
Definition: types.h:191
std::enable_if< xdr_traits< T >::is_struct &&xdr_traits< T >::xdr_defined, bool >::type operator<(const T &a, const T &b)
Ordering of XDR structures. See note at xdr::operator==.
Definition: types.h:964
Low-level byteswap and miscellaneous OS compatibility routines.
Type-level representation of a pointer-to-member value.
Definition: types.h:629
Metadata for all marshalable XDR types.
Definition: types.h:146
A vector with a maximum size (returned by xvector::max_size()).
Definition: types.h:420
void validate(const T &t)
If this function template is specialized, it provides a means of placing extra restrictions on XDR da...
Definition: types.h:113
Message not multiple of 4 bytes, or cannot fully be parsed.
Definition: types.h:51
To xdr_reinterpret(From f)
A reinterpret-cast like function that works between types such as floating-point and integers of the ...
Definition: types.h:215
friend bool operator==(const pointer &a, const pointer &b)
Compare by value, rather than looking at the value of the pointer.
Definition: types.h:593
Padding bytes that should have contained zero don&#39;t.
Definition: types.h:61
void validate() const
Check that the string length is not greater than the maximum size.
Definition: types.h:488
static void check_size(size_t n)
Check whether a size is in bounds.
Definition: types.h:428
Constexpr const field_archiver_t field_archiver
Passed to _xdr_with_mem_ptr to archive the active union field.
Definition: types.h:888
typename detail::all_indices< std::tuple_size< typename std::remove_reference< T >::type >::value > all_indices_of
A type representing all the indices of a particuar tuple.
Definition: types.h:806