Apache Mesos
jsonify.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_JSONIFY__
14 #define __STOUT_JSONIFY__
15 
16 #define RAPIDJSON_HAS_STDSTRING 1
17 
18 // TODO(bmahler): Consider enabling UTF-8 validation when writing
19 // json. Prior to the introduction of rapidjson, we performed no
20 // validation, so we maintain this status quo for now.
21 //
22 // #define RAPIDJSON_WRITE_DEFAULT_FLAGS 1 // kWriteValidateEncodingFlag
23 
24 // TODO(bmahler): Consider enabling SIMD for rapidjson, unfortunately
25 // it showed slightly slower results on the serialization path when
26 // benchmarked so I've left it disabled.
27 //
28 // #if defined(__SSE4_2__)
29 // # define RAPIDJSON_SSE42
30 // #elif defined(__SSE2__)
31 // # define RAPIDJSON_SSE2
32 // #elif defined(_MSC_VER) // Turn on SSE4.2 for VC
33 // # define RAPIDJSON_SSE42
34 // #endif
35 
36 #include <rapidjson/stringbuffer.h>
37 #include <rapidjson/writer.h>
38 
39 #include <cstddef>
40 #include <functional>
41 #include <ostream>
42 #include <string>
43 #include <type_traits>
44 #include <utility>
45 
46 #include <stout/check.hpp>
47 #include <stout/result_of.hpp>
48 #include <stout/strings.hpp>
49 
50 // `jsonify` takes an instance of a C++ object and returns a light-weight proxy
51 // object that can either be implicitly converted to a `std::string`, or
52 // directly inserted into an output stream.
53 //
54 // `jsonify(const T&)` is implemented by calling the function `json`.
55 // We perform unqualified function call so that it can detect overloads via
56 // argument dependent lookup. That is, we will search for, and use a free
57 // function named `json` in the same namespace as `T`.
58 //
59 // NOTE: This relationship is similar to `boost::hash` and `hash_value`.
60 //
61 // IMPORTANT: The output stream must not be exception-enabled. This is because
62 // the writer definitions below insert into the output stream in their
63 // destructors.
64 //
65 // Refer to https://github.com/apache/mesos/tree/master/3rdparty/stout#jsonify
66 // for more information.
67 
68 // Forward declaration of `JSON::Proxy`.
69 namespace JSON { class Proxy; }
70 
71 // Forward declaration of `jsonify`.
72 template <typename T>
73 JSON::Proxy jsonify(const T&);
74 
75 namespace JSON {
76 
77 // The result of `jsonify`. This is a light-weight proxy object that can either
78 // be implicitly converted to a `std::string`, or directly inserted into an
79 // output stream.
80 //
81 // In order to make this object light-weight, variables are captured by
82 // reference. This gives rise to similar semantics as `std::forward_as_tuple`.
83 // If the arguments are temporaries, `JSON::Proxy` does not extend their
84 // lifetime; they have to be used before the end of the full expression.
85 class Proxy
86 {
87 public:
88  operator std::string() &&
89  {
90  rapidjson::StringBuffer buffer;
91  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
92 
93  write(&writer);
94 
95  return {buffer.GetString(), buffer.GetSize()};
96  }
97 
98 private:
99  Proxy(std::function<void(rapidjson::Writer<rapidjson::StringBuffer>*)> write)
100  : write(std::move(write)) {}
101 
102  // We declare copy/move constructors `private` to prevent statements that try
103  // to "save" an instance of `Proxy` such as:
104  //
105  // ```
106  // std::string F();
107  // Proxy proxy = jsonify(F());
108  // ```
109  //
110  // Since `proxy` in the above example would be holding onto a reference to a
111  // temporary string returned by `F()`.
112  Proxy(const Proxy&) = default;
113  Proxy(Proxy&&) = default;
114 
115  template <typename T>
116  friend Proxy (::jsonify)(const T&);
117 
118  friend std::ostream& operator<<(std::ostream& stream, Proxy&& that);
119 
120 public:
121  // This is public in order to enable the `ObjectWriter` and `ArrayWriter`
122  // to continue writing to the same writer.
123  std::function<void(rapidjson::Writer<rapidjson::StringBuffer>*)> write;
124 };
125 
126 
127 inline std::ostream& operator<<(std::ostream& stream, Proxy&& that)
128 {
129  return stream << std::string(std::move(that));
130 }
131 
132 
133 // The boolean writer. If `set` is not called at all, a false value is
134 // written. If `set` is called more than once, only the last value is
135 // written.
137 {
138 public:
139  BooleanWriter(rapidjson::Writer<rapidjson::StringBuffer>* writer)
140  : writer_(writer), value_(false) {}
141 
142  BooleanWriter(const BooleanWriter&) = delete;
143  BooleanWriter(BooleanWriter&&) = delete;
144 
145  ~BooleanWriter() { CHECK(writer_->Bool(value_)); }
146 
147  BooleanWriter& operator=(const BooleanWriter&) = delete;
148  BooleanWriter& operator=(BooleanWriter&&) = delete;
149 
150  void set(bool value) { value_ = value; }
151 
152 private:
153  rapidjson::Writer<rapidjson::StringBuffer>* writer_;
154  bool value_;
155 };
156 
157 
158 // The number writer. If `set` is not called at all, `0` is written.
159 // If `set` is called more than once, only the last value is written.
161 {
162 public:
163  NumberWriter(rapidjson::Writer<rapidjson::StringBuffer>* writer)
164  : writer_(writer), type_(INT), int_(0) {}
165 
166  NumberWriter(const NumberWriter&) = delete;
167  NumberWriter(NumberWriter&&) = delete;
168 
170  {
171  switch (type_) {
172  case INT: CHECK(writer_->Int64(int_)); break;
173  case UINT: CHECK(writer_->Uint64(uint_)); break;
174  case DOUBLE: CHECK(writer_->Double(double_)); break;
175  }
176  }
177 
178  NumberWriter& operator=(const NumberWriter&) = delete;
179  NumberWriter& operator=(NumberWriter&&) = delete;
180 
181  // NOTE 1: We enumerate overloads for all of the integral types here to avoid
182  // ambiguities between signed and unsigned conversions. If we were to only
183  // overload for `long long int` and `unsigned long long int`, passing an
184  // argument of `0` would be ambiguous since `0` has type `int`, and cost of
185  // conversion to `long long int` or `unsigned long long int` is equivalent.
186 
187  // NOTE 2: We use the various modifiers on `int` as opposed to fixed size
188  // types such as `int32_t` and `int64_t` because these types do not cover all
189  // of the integral types. For example, `uint32_t` may map to `unsigned int`,
190  // and `uint64_t` to `unsigned long long int`. If `size_t` maps to `unsigned
191  // long int`, it is ambiguous to pass an instance of `size_t`. defining an
192  // overload for `size_t` would solve the problem on a specific platform, but
193  // we can run into issues again on another platform if `size_t` maps to
194  // `unsigned long long int`, since we would get a redefinition error.
195 
196  void set(short int value) { set(static_cast<long long int>(value)); }
197 
198  void set(int value) { set(static_cast<long long int>(value)); }
199 
200  void set(long int value) { set(static_cast<long long int>(value)); }
201 
202  void set(long long int value)
203  {
204  type_ = INT;
205  int_ = value;
206  }
207 
208  void set(unsigned short int value)
209  {
210  set(static_cast<unsigned long long int>(value));
211  }
212 
213  void set(unsigned int value)
214  {
215  set(static_cast<unsigned long long int>(value));
216  }
217 
218  void set(unsigned long int value)
219  {
220  set(static_cast<unsigned long long int>(value));
221  }
222 
223  void set(unsigned long long int value)
224  {
225  type_ = UINT;
226  uint_ = value;
227  }
228 
229  void set(float value) { set(static_cast<double>(value)); }
230 
231  void set(double value)
232  {
233  type_ = DOUBLE;
234  double_ = value;
235  }
236 
237 private:
238  rapidjson::Writer<rapidjson::StringBuffer>* writer_;
239 
240  enum { INT, UINT, DOUBLE } type_;
241 
242  union
243  {
244  long long int int_;
245  unsigned long long int uint_;
246  double double_;
247  };
248 };
249 
250 
251 // The string writer. `set` is used to write a string and must only
252 // be called once. If `set` is not called at all, an empty JSON
253 // string is written.
255 {
256 public:
257  StringWriter(rapidjson::Writer<rapidjson::StringBuffer>* writer)
258  : writer_(writer), empty_(true) {}
259 
260  StringWriter(const StringWriter&) = delete;
261  StringWriter(StringWriter&&) = delete;
262 
263  ~StringWriter() { if (empty_) { CHECK(writer_->String("")); } }
264 
265  StringWriter& operator=(const StringWriter&) = delete;
266  StringWriter& operator=(StringWriter&&) = delete;
267 
268  template <std::size_t N>
269  void set(const char (&value)[N])
270  {
271  empty_ = false;
272 
273  // This check will fail if we enable write validation in rapidjson;
274  // we'll need to figure out a way to surface the error.
275  CHECK(writer_->String(value, N-1));
276  }
277 
278  void set(const std::string& value)
279  {
280  empty_ = false;
281 
282  // This check will fail if we enable write validation in rapidjson;
283  // we'll need to figure out a way to surface the error.
284  CHECK(writer_->String(value));
285  }
286 
287 private:
288  rapidjson::Writer<rapidjson::StringBuffer>* writer_;
289  bool empty_;
290 };
291 
292 
293 // The array writer. `element(value)` is used to write a new element.
294 // If `element` is not called at all, an empty JSON array is written.
296 {
297 public:
298  ArrayWriter(rapidjson::Writer<rapidjson::StringBuffer>* writer)
299  : writer_(writer)
300  {
301  CHECK(writer_->StartArray());
302  }
303 
304  ArrayWriter(const ArrayWriter&) = delete;
305  ArrayWriter(ArrayWriter&&) = delete;
306 
308  {
309  CHECK(writer_->EndArray());
310  }
311 
312  ArrayWriter& operator=(const ArrayWriter&) = delete;
313  ArrayWriter& operator=(ArrayWriter&&) = delete;
314 
315  template <typename T>
316  void element(const T& value) { jsonify(value).write(writer_); }
317 
318 private:
319  rapidjson::Writer<rapidjson::StringBuffer>* writer_;
320 };
321 
322 
323 // The object writer. `field(key, value)` is used to write a new field.
324 // If `field` is not called at all, an empty JSON object is written.
326 {
327 public:
328  ObjectWriter(rapidjson::Writer<rapidjson::StringBuffer>* writer)
329  : writer_(writer)
330  {
331  CHECK(writer_->StartObject());
332  }
333 
334  ObjectWriter(const ObjectWriter&) = delete;
335  ObjectWriter(ObjectWriter&&) = delete;
336 
338  {
339  CHECK(writer_->EndObject());
340  }
341 
342  ObjectWriter& operator=(const ObjectWriter&) = delete;
343  ObjectWriter& operator=(ObjectWriter&&) = delete;
344 
345  template <typename T>
346  void field(const std::string& key, const T& value)
347  {
348  // This check will fail we enable write validation in rapidjson;
349  // we'll need to figure out a way to surface the error.
350  //
351  // TODO(bmahler): The 1.1.0 release of rapidjson did not
352  // yet have the std::string overload for `Key`, avoid calling
353  // `c_str()` and `size()` when we upgrade beyond 1.1.0.
354  CHECK(writer_->Key(key.c_str(), key.size()));
355  jsonify(value).write(writer_);
356  }
357 
358 private:
359  rapidjson::Writer<rapidjson::StringBuffer>* writer_;
360 };
361 
362 
364 {
365 public:
366  NullWriter(rapidjson::Writer<rapidjson::StringBuffer>* writer)
367  : writer_(writer) {}
368 
369  NullWriter(const NullWriter&) = delete;
370  NullWriter(NullWriter&&) = delete;
371 
372  ~NullWriter() { CHECK(writer_->Null()); }
373 
374  NullWriter& operator=(const NullWriter&) = delete;
375  NullWriter& operator=(NullWriter&&) = delete;
376 
377 private:
378  rapidjson::Writer<rapidjson::StringBuffer>* writer_;
379 };
380 
381 
382 // `json` function for boolean.
383 inline void json(BooleanWriter* writer, bool value) { writer->set(value); }
384 
385 
386 // `json` functions for numbers.
387 inline void json(NumberWriter* writer, short int value) { writer->set(value); }
388 inline void json(NumberWriter* writer, int value) { writer->set(value); }
389 inline void json(NumberWriter* writer, long int value) { writer->set(value); }
390 
391 
392 inline void json(NumberWriter* writer, long long int value)
393 {
394  writer->set(value);
395 }
396 
397 
398 inline void json(NumberWriter* writer, unsigned short int value)
399 {
400  writer->set(value);
401 }
402 
403 
404 inline void json(NumberWriter* writer, unsigned int value)
405 {
406  writer->set(value);
407 }
408 
409 
410 inline void json(NumberWriter* writer, unsigned long int value)
411 {
412  writer->set(value);
413 }
414 
415 
416 inline void json(NumberWriter* writer, unsigned long long int value)
417 {
418  writer->set(value);
419 }
420 
421 
422 inline void json(NumberWriter* writer, float value) { writer->set(value); }
423 inline void json(NumberWriter* writer, double value) { writer->set(value); }
424 
425 
426 // `json` functions for strings.
427 
428 template <std::size_t N>
429 void json(StringWriter* writer, const char (&value)[N])
430 {
431  writer->set(value);
432 }
433 
434 
435 inline void json(StringWriter* writer, const std::string& value)
436 {
437  writer->set(value);
438 }
439 
440 namespace internal {
441 
442 // TODO(mpark): Pull this out to something like <stout/meta.hpp>.
443 // This pattern already exists in `<process/future.hpp>`.
444 struct LessPrefer {};
445 struct Prefer : LessPrefer {};
446 
447 // The member `value` is `true` if `T` is a sequence, and `false` otherwise.
448 template <typename T>
450 {
451 private:
452  // This overload only participates in overload resolution if the following
453  // expressions are valid.
454  // (1) begin(t) != end(t)
455  // (2) auto iter = begin(t); ++iter
456  // (3) *begin(t)
457  //
458  // The expressions are only used for SFINAE purposes, and comma operators are
459  // used to ignore the results of the expressions. That is, the return type of
460  // this function is `decltype(expr0, expr1, expr2, std::true_type{})` which is
461  // `std::true_type`.
462  template <typename U>
463  static auto test(Prefer) -> decltype(
464  // Cast to `void` to suppress `-Wunused-comparison` warnings.
465  void(std::begin(std::declval<U&>()) != std::end(std::declval<U&>())),
466  ++std::declval<decltype(std::begin(std::declval<U&>()))&>(),
467  *std::begin(std::declval<U&>()),
468  std::true_type{});
469 
470  // This overload is chosen if the preferred version is SFINAE'd out.
471  template <typename U>
472  static std::false_type test(LessPrefer);
473 
474 public:
475  static constexpr bool value = decltype(test<T>(Prefer()))::value;
476 };
477 
478 
479 // The member `value` is `true` if `T` has a member typedef `mapped_type`, and
480 // `false` otherwise. We take the existence of `mapped_type` as the indication
481 // of an associative container (e.g., std::map).
482 template <typename T>
484 {
485 private:
486  template <typename U, typename = typename U::mapped_type>
487  static std::true_type test(Prefer);
488 
489  template <typename U>
490  static std::false_type test(LessPrefer);
491 
492 public:
493  static constexpr bool value = decltype(test<T>(Prefer()))::value;
494 };
495 
496 } // namespace internal {
497 
498 // `json` function for iterables (e.g., std::vector).
499 // This function is only enabled if `Iterable` is iterable, is not a
500 // `const char (&)[N]` (in order to avoid ambiguity with the string literal
501 // overload), and does not have a member typedef `mapped_type` (we take the
502 // existence of `mapped_type` as the indication of an associative container).
503 template <
504  typename Iterable,
505  typename std::enable_if<
507  !(std::is_array<Iterable>::value &&
508  std::rank<Iterable>::value == 1 &&
509  std::is_same<
510  char, typename std::remove_extent<Iterable>::type>::value) &&
512 void json(ArrayWriter* writer, const Iterable& iterable)
513 {
514  foreach (const auto& value, iterable) {
515  writer->element(value);
516  }
517 }
518 
519 
520 // `json` function for dictionaries (e.g., std::map).
521 // This function is only enabled if `Dictionary` is iterable, and has a member
522 // typedef `mapped_type` (we take the existence of `mapped_type` as the
523 // indication of an associative container).
524 template <
525  typename Dictionary,
526  typename std::enable_if<
529 void json(ObjectWriter* writer, const Dictionary& dictionary)
530 {
531  foreachpair (const auto& key, const auto& value, dictionary) {
532  // TODO(mpark): Consider passing `stringify(key)`.
533  writer->field(key, value);
534  }
535 }
536 
537 
538 // An object that can be converted to a pointer to any of the JSON writers.
539 // This is used to resolve the following scenario:
540 //
541 // ```
542 // void json(JSON::ObjectWriter*, const Resources&);
543 //
544 // void json(
545 // JSON::ArrayWriter*,
546 // const google::protobuf::RepeatedPtrField<Resource>&);
547 //
548 // Resources resources;
549 // std::cout << jsonify(resources); // We want to use the first overload!
550 // ```
551 //
552 // The goal is to perform overload resolution based on the second parameter.
553 // Since `WriterProxy` is convertible to any of the writers equivalently, we
554 // force overload resolution of `json(WriterProxy(writer), value)` to depend
555 // only on the second parameter.
557 {
558 public:
559  WriterProxy(rapidjson::Writer<rapidjson::StringBuffer>* writer)
560  : writer_(writer) {}
561 
563  {
564  switch (type_) {
565  case BOOLEAN_WRITER: {
566  proxy_.boolean_writer.~BooleanWriter();
567  break;
568  }
569  case NUMBER_WRITER: {
570  proxy_.number_writer.~NumberWriter();
571  break;
572  }
573  case STRING_WRITER: {
574  proxy_.string_writer.~StringWriter();
575  break;
576  }
577  case ARRAY_WRITER: {
578  proxy_.array_writer.~ArrayWriter();
579  break;
580  }
581  case OBJECT_WRITER: {
582  proxy_.object_writer.~ObjectWriter();
583  break;
584  }
585  case NULL_WRITER: {
586  proxy_.null_writer.~NullWriter();
587  break;
588  }
589  }
590  }
591 
592  operator BooleanWriter*() &&
593  {
594  new (&proxy_.boolean_writer) BooleanWriter(writer_);
595  type_ = BOOLEAN_WRITER;
596  return &proxy_.boolean_writer;
597  }
598 
599  operator NumberWriter*() &&
600  {
601  new (&proxy_.number_writer) NumberWriter(writer_);
602  type_ = NUMBER_WRITER;
603  return &proxy_.number_writer;
604  }
605 
606  operator StringWriter*() &&
607  {
608  new (&proxy_.string_writer) StringWriter(writer_);
609  type_ = STRING_WRITER;
610  return &proxy_.string_writer;
611  }
612 
613  operator ArrayWriter*() &&
614  {
615  new (&proxy_.array_writer) ArrayWriter(writer_);
616  type_ = ARRAY_WRITER;
617  return &proxy_.array_writer;
618  }
619 
620  operator ObjectWriter*() &&
621  {
622  new (&proxy_.object_writer) ObjectWriter(writer_);
623  type_ = OBJECT_WRITER;
624  return &proxy_.object_writer;
625  }
626 
627  operator NullWriter*() &&
628  {
629  new (&proxy_.null_writer) NullWriter(writer_);
630  type_ = NULL_WRITER;
631  return &proxy_.null_writer;
632  }
633 
634 private:
635  enum Type
636  {
637  BOOLEAN_WRITER,
638  NUMBER_WRITER,
639  STRING_WRITER,
640  ARRAY_WRITER,
641  OBJECT_WRITER,
642  NULL_WRITER
643  };
644 
645  union Writer
646  {
647  Writer() {}
648  ~Writer() {}
649  BooleanWriter boolean_writer;
650  NumberWriter number_writer;
651  StringWriter string_writer;
652  ArrayWriter array_writer;
653  ObjectWriter object_writer;
654  NullWriter null_writer;
655  };
656 
657  rapidjson::Writer<rapidjson::StringBuffer>* writer_;
658  Type type_;
659  Writer proxy_;
660 };
661 
662 
663 namespace internal {
664 
665 // NOTE: The following overloads of `internal::jsonify` return a `std::function`
666 // rather than a `JSON::Proxy` since `JSON::Proxy`'s copy/move constructors are
667 // declared `private`. We could also declare `internal::jsonify` as friend of
668 // `JSON::Proxy` but chose to minimize friendship and construct a
669 // `std::function` instead.
670 
671 // Given an `F` which is a "write" function, we simply use it directly.
673 std::function<void(rapidjson::Writer<rapidjson::StringBuffer>*)> jsonify(
674  const F& write,
675  Prefer)
676 {
677  return [&write](rapidjson::Writer<rapidjson::StringBuffer>* writer) {
678  write(WriterProxy(writer));
679  };
680 }
681 
682 // Given a `T` which is not a "write" function itself, the default "write"
683 // function is to perform an unqualified function call to `json`, which enables
684 // argument-dependent lookup. This considers the `json` overloads in the `JSON`
685 // namespace as well, since `WriterProxy` is intentionally defined in the
686 // `JSON` namespace.
687 template <typename T>
688 std::function<void(rapidjson::Writer<rapidjson::StringBuffer>*)> jsonify(
689  const T& value,
690  LessPrefer)
691 {
692  return [&value](rapidjson::Writer<rapidjson::StringBuffer>* writer) {
693  json(WriterProxy(writer), value);
694  };
695 }
696 
697 } // namespace internal {
698 } // namespace JSON {
699 
700 template <typename T>
701 JSON::Proxy jsonify(const T& t)
702 {
704 }
705 
706 #endif // __STOUT_JSONIFY__
StringWriter(rapidjson::Writer< rapidjson::StringBuffer > *writer)
Definition: jsonify.hpp:257
void set(short int value)
Definition: jsonify.hpp:196
~BooleanWriter()
Definition: jsonify.hpp:145
double double_
Definition: jsonify.hpp:246
WriterProxy(rapidjson::Writer< rapidjson::StringBuffer > *writer)
Definition: jsonify.hpp:559
Definition: jsonify.hpp:363
Definition: jsonify.hpp:136
Definition: jsonify.hpp:483
BooleanWriter(rapidjson::Writer< rapidjson::StringBuffer > *writer)
Definition: jsonify.hpp:139
friend std::ostream & operator<<(std::ostream &stream, Proxy &&that)
Definition: jsonify.hpp:127
Definition: jsonify.hpp:254
void element(const T &value)
Definition: jsonify.hpp:316
friend Proxy() jsonify(const T &)
Definition: jsonify.hpp:701
void json(BooleanWriter *writer, const Boolean &boolean)
Definition: json.hpp:735
~ArrayWriter()
Definition: jsonify.hpp:307
NullWriter(rapidjson::Writer< rapidjson::StringBuffer > *writer)
Definition: jsonify.hpp:366
void field(const std::string &key, const T &value)
Definition: jsonify.hpp:346
std::function< void(rapidjson::Writer< rapidjson::StringBuffer > *)> write
Definition: jsonify.hpp:123
Definition: jsonify.hpp:85
Definition: jsonify.hpp:445
Definition: jsonify.hpp:449
Definition: jsonify.hpp:325
#define foreachpair(KEY, VALUE, ELEMS)
Definition: foreach.hpp:51
ArrayWriter(rapidjson::Writer< rapidjson::StringBuffer > *writer)
Definition: jsonify.hpp:298
~WriterProxy()
Definition: jsonify.hpp:562
Definition: jsonify.hpp:160
JSON::Proxy jsonify(const T &)
Definition: jsonify.hpp:701
~NumberWriter()
Definition: jsonify.hpp:169
std::function< void(rapidjson::Writer< rapidjson::StringBuffer > *)> jsonify(const F &write, Prefer)
Definition: jsonify.hpp:673
long long int int_
Definition: jsonify.hpp:244
Definition: attributes.hpp:24
~StringWriter()
Definition: jsonify.hpp:263
~ObjectWriter()
Definition: jsonify.hpp:337
Try< uint32_t > type(const std::string &path)
ObjectWriter(rapidjson::Writer< rapidjson::StringBuffer > *writer)
Definition: jsonify.hpp:328
Definition: jsonify.hpp:444
unsigned long long int uint_
Definition: jsonify.hpp:245
NumberWriter(rapidjson::Writer< rapidjson::StringBuffer > *writer)
Definition: jsonify.hpp:163
void set(bool value)
Definition: jsonify.hpp:150
~NullWriter()
Definition: jsonify.hpp:372
Definition: json.hpp:51
Definition: jsonify.hpp:556
Definition: jsonify.hpp:295
void set(const char(&value)[N])
Definition: jsonify.hpp:269