xdrpp
RFC4506 XDR compiler and message library
printer.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 
3 //! \file printer.h Support for pretty-printing XDR data types.
4 //! Function xdr::xdr_to_string converts an arbitrary XDR data type to
5 //! a string. In addition, if you say <tt>using
6 //! xdr::operator<<;</tt>, you can use the standard \c operator<< to
7 //! print XDR types.
8 //!
9 //! You can customize how a particular user-defined type \c T gets
10 //! printed by defining a function <tt>std::string xdr_printer(const
11 //! T&)</tt> in the same namespace as <tt>T</tt>.
12 
13 #ifndef _XDRPP_PRINT_H_HEADER_INCLUDED_
14 #define _XDRPP_PRINT_H_HEADER_INCLUDED_ 1
15 
16 #include <sstream>
17 #include <xdrpp/types.h>
18 
19 namespace xdr {
20 
21 //! Poor man's version of C++14 enable_if_t.
22 #undef ENABLE_IF
23 #define ENABLE_IF(expr) typename std::enable_if<expr>::type
24 
25 //! Use hex escapes for non-printable characters, and prefix
26 //! backslashes and quotes with backslash.
27 std::string escape_string(const std::string &s);
28 //! Turn a string into a double-length sequence of hex nibbles.
29 std::string hexdump(const void *data, size_t len);
30 
31 namespace detail {
32 
33 struct Printer {
34  std::ostringstream buf_;
35  int indent_{0};
36  bool skipnl_{true};
37  bool comma_{true};
38 
39  Printer() {}
40  Printer(int indent) : indent_(indent) {}
41 
42  std::ostream &bol(const char *name = nullptr);
43  void p(const char *field, const char *s) { bol(field) << s; }
44  void p(const char *field, const std::string &s) { bol(field) << s; }
45 
46  void operator()(const char *field, xdr_void) { bol(field) << "void"; }
47 
48  template<std::uint32_t N> void
49  operator()(const char *field, const xstring<N> &s) {
50  p(field, escape_string(s));
51  }
52  template<std::uint32_t N> void
53  operator()(const char *field, const opaque_array<N> &v) {
54  p(field, hexdump(v.data(), v.size()));
55  }
56  template<std::uint32_t N>
57  void operator()(const char *field, const opaque_vec<N> &v) {
58  p(field, hexdump(v.data(), v.size()));
59  }
60 
61  template<typename T> ENABLE_IF(xdr_traits<T>::is_enum)
62  operator()(const char *field, T t) {
63  if (const char *n = xdr_traits<T>::enum_name(t))
64  p(field, n);
65  else
66  p(field, std::to_string(t));
67  }
68 
69  template<typename T> ENABLE_IF(xdr_traits<T>::is_numeric)
70  operator()(const char *field, T t) { p(field, std::to_string(t)); }
71 
72  // Don't print 1-tuple as tuple (even Haskell doesn't have 1-tuples).
73  template<typename T> void
74  operator()(const char *field, const std::tuple<T> &t) {
75  archive(*this, std::get<0>(t), field);
76  }
77 
78  template<typename T> void operator()(const char *field, const pointer<T> &t) {
79  if (t)
80  archive(*this, *t, field);
81  else
82  bol(field) << "NULL";
83  }
84 
85  template<typename T> ENABLE_IF(xdr_traits<T>::is_class)
86  operator()(const char *field, const T &t) {
87  bool skipnl = !field;
88  bol(field) << "{";
89  if (skipnl)
90  buf_ << ' ';
91  comma_ = false;
92  skipnl_ = skipnl;
93  indent_ += 2;
94  xdr_traits<T>::save(*this, t);
95  if (skipnl) {
96  buf_ << " }";
97  indent_ -= 2;
98  }
99  else {
100  comma_ = false;
101  indent_ -= 2;
102  bol() << "}";
103  }
104  }
105 
106  template<typename T> ENABLE_IF(xdr_traits<T>::is_container)
107  operator()(const char *field, const T &t) {
108  bool skipnl = !field;
109  bol(field) << '[';
110  if (skipnl)
111  buf_ << ' ';
112  comma_ = false;
113  skipnl_ = skipnl;
114  indent_ += 2;
115  for (const auto &o : t)
116  archive(*this, o);
117  if (skipnl) {
118  buf_ << " ]";
119  indent_ -= 2;
120  }
121  else {
122  comma_ = false;
123  indent_ -= 2;
124  bol() << "]";
125  }
126  }
127 };
128 
129 template<typename T> class has_xdr_printer {
130  template<typename U> static std::true_type
131  test(decltype(xdr_printer(*(U*)0)) *);
132 
133  template<typename U> static std::false_type test(...);
134 public:
135  static constexpr bool value = decltype(test<T>(0))::value;
136 };
137 
138 } // namespace detail
139 
140 template<> struct archive_adapter<detail::Printer> {
141  using Printer = detail::Printer;
142 
143  template<typename T> static
144  typename std::enable_if<!detail::has_xdr_printer<T>::value>::type
145  apply(Printer &p, const T &obj, const char *field) {
146  p(field, obj);
147  }
148 
149  template<typename T> static
150  typename std::enable_if<detail::has_xdr_printer<T>::value>::type
151  apply(Printer &p, const T &obj, const char *field) {
152  p.p(field, xdr_printer(obj));
153  }
154 };
155 
156 //! Return a std::string containing a pretty-printed version an XDR
157 //! data type. The string will contain multiple lines and end with a
158 //! newline. \arg name if non-NULL, the string begins with the name
159 //! and an equals sign. \arg indent specifies a non-zero minimum
160 //! indentation.
161 template<typename T> std::string
162 xdr_to_string(const T &t, const char *name = nullptr, int indent = 0)
163 {
164  detail::Printer p(indent);
165  archive(p, t, name);
166  p.buf_ << std::endl;
167  return p.buf_.str();
168 }
169 
170 //! Print an arbitrary XDR structure to a \c std::ostream. To use
171 //! this function, you will have to say <tt>using xdr::operator<<</tt>
172 //! within the namespace of your XDR file. As per the C++ standard, a
173 //! using \e directive (i.e., <tt>using namespace xdr</tt>) will not
174 //! allow argument-dependent lookup.
175 template<typename T>
176 inline typename std::enable_if<xdr_traits<T>::valid, std::ostream &>::type
177 operator<<(std::ostream &os, const T &t)
178 {
179  return os << xdr_to_string(t);
180 }
181 
182 } // namespace xdr
183 
184 #endif // !_XDRPP_PRINT_H_HEADER_INCLUDED_
std::string escape_string(const std::string &s)
Use hex escapes for non-printable characters, and prefix backslashes and quotes with backslash...
Definition: printer.cc:8
Type definitions for xdrc compiler output.
std::tuple<> xdr_void
Placehoder type representing void values marshaled as 0 bytes.
Definition: types.h:812
static Constexpr const bool is_class
T is an XDR struct or union.
Definition: types.h:158
#define ENABLE_IF(expr)
Poor man&#39;s version of C++14 enable_if_t.
Definition: printer.h:23
Most of the xdrpp library is encapsulated in the xdr namespace.
Definition: arpc.cc:4
std::enable_if< xdr_traits< T >::valid, std::ostream & >::type operator<<(std::ostream &os, const T &t)
Print an arbitrary XDR structure to a std::ostream.
Definition: printer.h:177
static Constexpr const bool is_enum
T is an XDR enum or bool (traits have enum_name).
Definition: types.h:160
static Constexpr const bool is_container
T is an xdr::pointer, xdr::xarray, or xdr::xvector (with load/save).
Definition: types.h:162
static Constexpr const bool is_numeric
T is one of [u]int{32,64}_t, float, or double.
Definition: types.h:164
std::string hexdump(const void *_data, size_t len)
Turn a string into a double-length sequence of hex nibbles.
Definition: printer.cc:43
std::string xdr_to_string(const T &t, const char *name=nullptr, int indent=0)
Return a std::string containing a pretty-printed version an XDR data type.
Definition: printer.h:162