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 #ifndef __WINDOWS__
17 #include <locale.h>
18 #endif // __WINDOWS__
19 
20 #include <clocale>
21 #include <cstddef>
22 #include <functional>
23 #include <ostream>
24 #include <sstream>
25 #include <string>
26 #include <type_traits>
27 #include <utility>
28 
29 #include <stout/check.hpp>
30 #include <stout/result_of.hpp>
31 #include <stout/strings.hpp>
32 
33 // `jsonify` takes an instance of a C++ object and returns a light-weight proxy
34 // object that can either be implicitly converted to a `std::string`, or
35 // directly inserted into an output stream.
36 //
37 // `jsonify(const T&)` is implemented by calling the function `json`.
38 // We perform unqualified function call so that it can detect overloads via
39 // argument dependent lookup. That is, we will search for, and use a free
40 // function named `json` in the same namespace as `T`.
41 //
42 // NOTE: This relationship is similar to `boost::hash` and `hash_value`.
43 //
44 // IMPORTANT: The output stream must not be exception-enabled. This is because
45 // the writer definitions below insert into the output stream in their
46 // destructors.
47 //
48 // Refer to https://github.com/apache/mesos/tree/master/3rdparty/stout#jsonify
49 // for more information.
50 
51 // Forward declaration of `JSON::Proxy`.
52 namespace JSON { class Proxy; }
53 
54 // Forward declaration of `jsonify`.
55 template <typename T>
56 JSON::Proxy jsonify(const T&);
57 
58 namespace JSON {
59 
60 namespace internal {
61 
71 // TODO(josephw): Consider pulling this helper into a separate header.
73 {
74 #ifdef __WINDOWS__
75 public:
77  {
78  // We will only change the locale for this thread
79  // and save the previous state of the thread's locale.
80  original_per_thread_ = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
81 
82  // NOTE: We must make a copy of the return value as it points
83  // to global or shared memory. Future calls to `setlocale` will
84  // invalidate the memory location.
85  original_locale_ = setlocale(LC_NUMERIC, "C");
86  }
87 
89  {
90  setlocale(LC_NUMERIC, original_locale_.c_str());
91  _configthreadlocale(original_per_thread_);
92  }
93 
94 private:
95  int original_per_thread_;
96  std::string original_locale_;
97 #else
98 public:
100  {
101  c_locale_ = newlocale(LC_NUMERIC_MASK, "C", nullptr);
102  original_locale_ = uselocale(c_locale_);
103  }
104 
106  {
107  uselocale(original_locale_);
108  CHECK(c_locale_ != 0);
109  freelocale(c_locale_);
110  }
111 
112 private:
113  locale_t original_locale_;
114  locale_t c_locale_;
115 #endif // __WINDOWS__
116 };
117 
118 } // namespace internal {
119 
120 
121 // The result of `jsonify`. This is a light-weight proxy object that can either
122 // be implicitly converted to a `std::string`, or directly inserted into an
123 // output stream.
124 //
125 // In order to make this object light-weight, variables are captured by
126 // reference. This gives rise to similar semantics as `std::forward_as_tuple`.
127 // If the arguments are temporaries, `JSON::Proxy` does not extend their
128 // lifetime; they have to be used before the end of the full expression.
129 class Proxy
130 {
131 public:
132  operator std::string() &&
133  {
134  // Needed to set C locale and therefore creating proper JSON output.
136 
137  std::ostringstream stream;
138  stream << std::move(*this);
139  return stream.str();
140  }
141 
142 private:
143  Proxy(std::function<void(std::ostream*)> write) : write_(std::move(write)) {}
144 
145  // We declare copy/move constructors `private` to prevent statements that try
146  // to "save" an instance of `Proxy` such as:
147  //
148  // ```
149  // std::string F();
150  // Proxy proxy = jsonify(F());
151  // ```
152  //
153  // Since `proxy` in the above example would be holding onto a reference to a
154  // temporary string returned by `F()`.
155  Proxy(const Proxy&) = default;
156  Proxy(Proxy&&) = default;
157 
158  std::function<void(std::ostream*)> write_;
159 
160  template <typename T>
161  friend Proxy (::jsonify)(const T&);
162 
163  friend std::ostream& operator<<(std::ostream& stream, Proxy&& that);
164 };
165 
166 
167 inline std::ostream& operator<<(std::ostream& stream, Proxy&& that)
168 {
169  // Needed to set C locale and therefore creating proper JSON output.
171 
172  that.write_(&stream);
173  return stream;
174 }
175 
176 
177 // The boolean writer. If `set` is not called at all, `false` is printed.
178 // If `set` is called more than once, only the last value is printed out.
180 {
181 public:
182  BooleanWriter(std::ostream* stream) : stream_(stream), value_(false) {}
183 
184  BooleanWriter(const BooleanWriter&) = delete;
185  BooleanWriter(BooleanWriter&&) = delete;
186 
187  ~BooleanWriter() { *stream_ << (value_ ? "true" : "false"); }
188 
189  BooleanWriter& operator=(const BooleanWriter&) = delete;
190  BooleanWriter& operator=(BooleanWriter&&) = delete;
191 
192  void set(bool value) { value_ = value; }
193 
194 private:
195  std::ostream* stream_;
196  bool value_;
197 };
198 
199 
200 // The number writer. If `set` is not called at all, `0` is printed.
201 // If `set` is called more than once, only the last value is printed.
203 {
204 public:
205  NumberWriter(std::ostream* stream)
206  : stream_(stream), type_(INT), int_(0) {}
207 
208  NumberWriter(const NumberWriter&) = delete;
209  NumberWriter(NumberWriter&&) = delete;
210 
212  {
213  switch (type_) {
214  case INT: {
215  *stream_ << int_;
216  break;
217  }
218  case UINT: {
219  *stream_ << uint_;
220  break;
221  }
222  case DOUBLE: {
223  // Prints a floating point value, with the specified precision, see:
224  // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2005.pdf
225  // Additionally ensures that a decimal point is in the output.
226  char buffer[50]; // More than enough for the specified precision.
227  const int size = snprintf(
228  buffer,
229  sizeof(buffer),
230  "%#.*g",
231  std::numeric_limits<double>::digits10,
232  double_);
233 
234  // Get rid of excess trailing zeroes before outputting.
235  // Otherwise, printing 1.0 would result in "1.00000000000000".
236  //
237  // NOTE: We intentionally do not use `strings::trim` here in order to
238  // avoid construction of temporary strings.
239  int back = size - 1;
240  for (; back > 0; --back) {
241  if (buffer[back] != '0') {
242  break;
243  }
244  buffer[back] = '\0';
245  }
246 
247  // NOTE: valid JSON numbers cannot end with a '.'.
248  *stream_ << buffer << (buffer[back] == '.' ? "0" : "");
249  break;
250  }
251  }
252  }
253 
254  NumberWriter& operator=(const NumberWriter&) = delete;
255  NumberWriter& operator=(NumberWriter&&) = delete;
256 
257  // NOTE 1: We enumerate overloads for all of the integral types here to avoid
258  // ambiguities between signed and unsigned conversions. If we were to only
259  // overload for `long long int` and `unsigned long long int`, passing an
260  // argument of `0` would be ambiguous since `0` has type `int`, and cost of
261  // conversion to `long long int` or `unsigned long long int` is equivalent.
262 
263  // NOTE 2: We use the various modifiers on `int` as opposed to fixed size
264  // types such as `int32_t` and `int64_t` because these types do not cover all
265  // of the integral types. For example, `uint32_t` may map to `unsigned int`,
266  // and `uint64_t` to `unsigned long long int`. If `size_t` maps to `unsigned
267  // long int`, it is ambiguous to pass an instance of `size_t`. defining an
268  // overload for `size_t` would solve the problem on a specific platform, but
269  // we can run into issues again on another platform if `size_t` maps to
270  // `unsigned long long int`, since we would get a redefinition error.
271 
272  void set(short int value) { set(static_cast<long long int>(value)); }
273 
274  void set(int value) { set(static_cast<long long int>(value)); }
275 
276  void set(long int value) { set(static_cast<long long int>(value)); }
277 
278  void set(long long int value)
279  {
280  type_ = INT;
281  int_ = value;
282  }
283 
284  void set(unsigned short int value)
285  {
286  set(static_cast<unsigned long long int>(value));
287  }
288 
289  void set(unsigned int value)
290  {
291  set(static_cast<unsigned long long int>(value));
292  }
293 
294  void set(unsigned long int value)
295  {
296  set(static_cast<unsigned long long int>(value));
297  }
298 
299  void set(unsigned long long int value)
300  {
301  type_ = UINT;
302  uint_ = value;
303  }
304 
305  void set(float value) { set(static_cast<double>(value)); }
306 
307  void set(double value)
308  {
309  type_ = DOUBLE;
310  double_ = value;
311  }
312 
313 private:
314  std::ostream* stream_;
315 
316  enum { INT, UINT, DOUBLE } type_;
317 
318  union
319  {
320  long long int int_;
321  unsigned long long int uint_;
322  double double_;
323  };
324 };
325 
326 
327 // The string writer. `append` is used to append a character or a string.
328 // If `append` is not called at all, `""` is printed.
330 {
331 public:
332  StringWriter(std::ostream* stream) : stream_(stream) { *stream_ << '"'; }
333 
334  StringWriter(const StringWriter&) = delete;
335  StringWriter(StringWriter&&) = delete;
336 
337  ~StringWriter() { *stream_ << '"'; }
338 
339  StringWriter& operator=(const StringWriter&) = delete;
340  StringWriter& operator=(StringWriter&&) = delete;
341 
342  void append(char c)
343  {
344  switch (c) {
345  case '"' : *stream_ << "\\\""; break;
346  case '\\': *stream_ << "\\\\"; break;
347  case '/' : *stream_ << "\\/"; break;
348  case '\b': *stream_ << "\\b"; break;
349  case '\f': *stream_ << "\\f"; break;
350  case '\n': *stream_ << "\\n"; break;
351  case '\r': *stream_ << "\\r"; break;
352  case '\t': *stream_ << "\\t"; break;
353  default: {
354  if (static_cast<unsigned char>(c) < 0x20 || c == 0x7f) {
355  char buffer[7];
356  snprintf(buffer, sizeof(buffer), "\\u%04x", c & 0xff);
357  stream_->write(buffer, sizeof(buffer) - 1);
358  } else {
359  *stream_ << c;
360  }
361  break;
362  }
363  }
364  }
365 
366  template <std::size_t N>
367  void append(const char (&value)[N]) { append(value, N - 1); }
368  void append(const std::string& value) { append(value.data(), value.size()); }
369 
370 private:
371  void append(const char* value, std::size_t size)
372  {
373  for (std::size_t i = 0; i < size; ++i) {
374  append(value[i]);
375  }
376  }
377 
378  std::ostream* stream_;
379 };
380 
381 
382 // The array writer. `element(value)` is used to write a new element.
383 // If `element` is not called at all, `[]` is printed.
385 {
386 public:
387  ArrayWriter(std::ostream* stream) : stream_(stream), count_(0)
388  {
389  *stream_ << '[';
390  }
391 
392  ArrayWriter(const ArrayWriter&) = delete;
393  ArrayWriter(ArrayWriter&&) = delete;
394 
395  ~ArrayWriter() { *stream_ << ']'; }
396 
397  ArrayWriter& operator=(const ArrayWriter&) = delete;
398  ArrayWriter& operator=(ArrayWriter&&) = delete;
399 
400  template <typename T>
401  void element(const T& value)
402  {
403  if (count_ > 0) {
404  *stream_ << ',';
405  }
406  *stream_ << jsonify(value);
407  ++count_;
408  }
409 
410 private:
411  std::ostream* stream_;
412  std::size_t count_;
413 };
414 
415 
416 // The object writer. `field(key, value)` is used to write a new field.
417 // If `field` is not called at all, `{}` is printed.
419 {
420 public:
421  ObjectWriter(std::ostream* stream) : stream_(stream), count_(0)
422  {
423  *stream_ << '{';
424  }
425 
426  ObjectWriter(const ObjectWriter&) = delete;
427  ObjectWriter(ObjectWriter&&) = delete;
428 
429  ~ObjectWriter() { *stream_ << '}'; }
430 
431  ObjectWriter& operator=(const ObjectWriter&) = delete;
432  ObjectWriter& operator=(ObjectWriter&&) = delete;
433 
434  template <typename T>
435  void field(const std::string& key, const T& value)
436  {
437  if (count_ > 0) {
438  *stream_ << ',';
439  }
440  *stream_ << jsonify(key) << ':' << jsonify(value);
441  ++count_;
442  }
443 
444 private:
445  std::ostream* stream_;
446  std::size_t count_;
447 };
448 
449 
451 {
452 public:
453  NullWriter(std::ostream* stream) : stream_(stream) {}
454 
455  NullWriter(const NullWriter&) = delete;
456  NullWriter(NullWriter&&) = delete;
457 
458  ~NullWriter() { *stream_ << "null"; }
459 
460  NullWriter& operator=(const NullWriter&) = delete;
461  NullWriter& operator=(NullWriter&&) = delete;
462 
463 private:
464  std::ostream* stream_;
465 };
466 
467 
468 // `json` function for boolean.
469 inline void json(BooleanWriter* writer, bool value) { writer->set(value); }
470 
471 
472 // `json` functions for numbers.
473 inline void json(NumberWriter* writer, short int value) { writer->set(value); }
474 inline void json(NumberWriter* writer, int value) { writer->set(value); }
475 inline void json(NumberWriter* writer, long int value) { writer->set(value); }
476 
477 
478 inline void json(NumberWriter* writer, long long int value)
479 {
480  writer->set(value);
481 }
482 
483 
484 inline void json(NumberWriter* writer, unsigned short int value)
485 {
486  writer->set(value);
487 }
488 
489 
490 inline void json(NumberWriter* writer, unsigned int value)
491 {
492  writer->set(value);
493 }
494 
495 
496 inline void json(NumberWriter* writer, unsigned long int value)
497 {
498  writer->set(value);
499 }
500 
501 
502 inline void json(NumberWriter* writer, unsigned long long int value)
503 {
504  writer->set(value);
505 }
506 
507 
508 inline void json(NumberWriter* writer, float value) { writer->set(value); }
509 inline void json(NumberWriter* writer, double value) { writer->set(value); }
510 
511 
512 // `json` functions for strings.
513 
514 template <std::size_t N>
515 void json(StringWriter* writer, const char (&value)[N])
516 {
517  writer->append(value);
518 }
519 
520 
521 inline void json(StringWriter* writer, const std::string& value)
522 {
523  writer->append(value);
524 }
525 
526 namespace internal {
527 
528 // TODO(mpark): Pull this out to something like <stout/meta.hpp>.
529 // This pattern already exists in `<process/future.hpp>`.
530 struct LessPrefer {};
531 struct Prefer : LessPrefer {};
532 
533 // The member `value` is `true` if `T` is a sequence, and `false` otherwise.
534 template <typename T>
536 {
537 private:
538  // This overload only participates in overload resolution if the following
539  // expressions are valid.
540  // (1) begin(t) != end(t)
541  // (2) auto iter = begin(t); ++iter
542  // (3) *begin(t)
543  //
544  // The expressions are only used for SFINAE purposes, and comma operators are
545  // used to ignore the results of the expressions. That is, the return type of
546  // this function is `decltype(expr0, expr1, expr2, std::true_type{})` which is
547  // `std::true_type`.
548  template <typename U>
549  static auto test(Prefer) -> decltype(
550  // Cast to `void` to suppress `-Wunused-comparison` warnings.
551  void(std::begin(std::declval<U&>()) != std::end(std::declval<U&>())),
552  ++std::declval<decltype(std::begin(std::declval<U&>()))&>(),
553  *std::begin(std::declval<U&>()),
554  std::true_type{});
555 
556  // This overload is chosen if the preferred version is SFINAE'd out.
557  template <typename U>
558  static std::false_type test(LessPrefer);
559 
560 public:
561  static constexpr bool value = decltype(test<T>(Prefer()))::value;
562 };
563 
564 
565 // The member `value` is `true` if `T` has a member typedef `mapped_type`, and
566 // `false` otherwise. We take the existence of `mapped_type` as the indication
567 // of an associative container (e.g., std::map).
568 template <typename T>
570 {
571 private:
572  template <typename U, typename = typename U::mapped_type>
573  static std::true_type test(Prefer);
574 
575  template <typename U>
576  static std::false_type test(LessPrefer);
577 
578 public:
579  static constexpr bool value = decltype(test<T>(Prefer()))::value;
580 };
581 
582 } // namespace internal {
583 
584 // `json` function for iterables (e.g., std::vector).
585 // This function is only enabled if `Iterable` is iterable, is not a
586 // `const char (&)[N]` (in order to avoid ambiguity with the string literal
587 // overload), and does not have a member typedef `mapped_type` (we take the
588 // existence of `mapped_type` as the indication of an associative container).
589 template <
590  typename Iterable,
591  typename std::enable_if<
593  !(std::is_array<Iterable>::value &&
594  std::rank<Iterable>::value == 1 &&
595  std::is_same<
596  char, typename std::remove_extent<Iterable>::type>::value) &&
598 void json(ArrayWriter* writer, const Iterable& iterable)
599 {
600  foreach (const auto& value, iterable) {
601  writer->element(value);
602  }
603 }
604 
605 
606 // `json` function for dictionaries (e.g., std::map).
607 // This function is only enabled if `Dictionary` is iterable, and has a member
608 // typedef `mapped_type` (we take the existence of `mapped_type` as the
609 // indication of an associative container).
610 template <
611  typename Dictionary,
612  typename std::enable_if<
615 void json(ObjectWriter* writer, const Dictionary& dictionary)
616 {
617  foreachpair (const auto& key, const auto& value, dictionary) {
618  // TODO(mpark): Consider passing `stringify(key)`.
619  writer->field(key, value);
620  }
621 }
622 
623 
624 // An object that can be converted to a pointer to any of the JSON writers.
625 // This is used to resolve the following scenario:
626 //
627 // ```
628 // void json(JSON::ObjectWriter*, const Resources&);
629 //
630 // void json(
631 // JSON::ArrayWriter*,
632 // const google::protobuf::RepeatedPtrField<Resource>&);
633 //
634 // Resources resources;
635 // std::cout << jsonify(resources); // We want to use the first overload!
636 // ```
637 //
638 // The goal is to perform overload resolution based on the second parameter.
639 // Since `WriterProxy` is convertible to any of the writers equivalently, we
640 // force overload resolution of `json(WriterProxy(stream), value)` to depend
641 // only on the second parameter.
643 {
644 public:
645  WriterProxy(std::ostream* stream) : stream_(stream) {}
646 
648  {
649  switch (type_) {
650  case BOOLEAN_WRITER: {
651  writer_.boolean_writer.~BooleanWriter();
652  break;
653  }
654  case NUMBER_WRITER: {
655  writer_.number_writer.~NumberWriter();
656  break;
657  }
658  case STRING_WRITER: {
659  writer_.string_writer.~StringWriter();
660  break;
661  }
662  case ARRAY_WRITER: {
663  writer_.array_writer.~ArrayWriter();
664  break;
665  }
666  case OBJECT_WRITER: {
667  writer_.object_writer.~ObjectWriter();
668  break;
669  }
670  case NULL_WRITER: {
671  writer_.null_writer.~NullWriter();
672  break;
673  }
674  }
675  }
676 
677  operator BooleanWriter*() &&
678  {
679  new (&writer_.boolean_writer) BooleanWriter(stream_);
680  type_ = BOOLEAN_WRITER;
681  return &writer_.boolean_writer;
682  }
683 
684  operator NumberWriter*() &&
685  {
686  new (&writer_.number_writer) NumberWriter(stream_);
687  type_ = NUMBER_WRITER;
688  return &writer_.number_writer;
689  }
690 
691  operator StringWriter*() &&
692  {
693  new (&writer_.string_writer) StringWriter(stream_);
694  type_ = STRING_WRITER;
695  return &writer_.string_writer;
696  }
697 
698  operator ArrayWriter*() &&
699  {
700  new (&writer_.array_writer) ArrayWriter(stream_);
701  type_ = ARRAY_WRITER;
702  return &writer_.array_writer;
703  }
704 
705  operator ObjectWriter*() &&
706  {
707  new (&writer_.object_writer) ObjectWriter(stream_);
708  type_ = OBJECT_WRITER;
709  return &writer_.object_writer;
710  }
711 
712  operator NullWriter*() &&
713  {
714  new (&writer_.null_writer) NullWriter(stream_);
715  type_ = NULL_WRITER;
716  return &writer_.null_writer;
717  }
718 
719 private:
720  enum Type
721  {
722  BOOLEAN_WRITER,
723  NUMBER_WRITER,
724  STRING_WRITER,
725  ARRAY_WRITER,
726  OBJECT_WRITER,
727  NULL_WRITER
728  };
729 
730  union Writer
731  {
732  Writer() {}
733  ~Writer() {}
734  BooleanWriter boolean_writer;
735  NumberWriter number_writer;
736  StringWriter string_writer;
737  ArrayWriter array_writer;
738  ObjectWriter object_writer;
739  NullWriter null_writer;
740  };
741 
742  std::ostream* stream_;
743  Type type_;
744  Writer writer_;
745 };
746 
747 namespace internal {
748 
749 // NOTE: The following overloads of `internal::jsonify` return a `std::function`
750 // rather than a `JSON::Proxy` since `JSON::Proxy`'s copy/move constructors are
751 // declared `private`. We could also declare `internal::jsonify` as friend of
752 // `JSON::Proxy` but chose to minimize friendship and construct a
753 // `std::function` instead.
754 
755 // Given an `F` which is a "write" function, we simply use it directly.
757 std::function<void(std::ostream*)> jsonify(const F& write, Prefer)
758 {
759  return [&write](std::ostream* stream) { write(WriterProxy(stream)); };
760 }
761 
762 // Given a `T` which is not a "write" function itself, the default "write"
763 // function is to perform an unqualified function call to `json`, which enables
764 // argument-dependent lookup. This considers the `json` overloads in the `JSON`
765 // namespace as well, since `WriterProxy` is intentionally defined in the
766 // `JSON` namespace.
767 template <typename T>
768 std::function<void(std::ostream*)> jsonify(const T& value, LessPrefer)
769 {
770  return [&value](std::ostream* stream) {
771  json(WriterProxy(stream), value);
772  };
773 }
774 
775 } // namespace internal {
776 } // namespace JSON {
777 
778 template <typename T>
779 JSON::Proxy jsonify(const T& t)
780 {
782 }
783 
784 #endif // __STOUT_JSONIFY__
std::function< void(std::ostream *)> jsonify(const F &write, Prefer)
Definition: jsonify.hpp:757
void set(short int value)
Definition: jsonify.hpp:272
Try< Bytes > size(const std::string &path, const FollowSymlink follow=FollowSymlink::FOLLOW_SYMLINK)
Definition: stat.hpp:121
~ClassicLocale()
Definition: jsonify.hpp:105
BooleanWriter(std::ostream *stream)
Definition: jsonify.hpp:182
~BooleanWriter()
Definition: jsonify.hpp:187
double double_
Definition: jsonify.hpp:322
ArrayWriter(std::ostream *stream)
Definition: jsonify.hpp:387
void append(const std::string &value)
Definition: jsonify.hpp:368
WriterProxy(std::ostream *stream)
Definition: jsonify.hpp:645
Definition: jsonify.hpp:450
Definition: jsonify.hpp:179
Definition: jsonify.hpp:569
Definition: jsonify.hpp:329
void element(const T &value)
Definition: jsonify.hpp:401
StringWriter(std::ostream *stream)
Definition: jsonify.hpp:332
void json(BooleanWriter *writer, const Boolean &boolean)
Definition: json.hpp:694
~ArrayWriter()
Definition: jsonify.hpp:395
void field(const std::string &key, const T &value)
Definition: jsonify.hpp:435
ObjectWriter(std::ostream *stream)
Definition: jsonify.hpp:421
Definition: jsonify.hpp:129
Definition: jsonify.hpp:531
Definition: jsonify.hpp:535
Definition: jsonify.hpp:418
#define foreachpair(KEY, VALUE, ELEMS)
Definition: foreach.hpp:51
~WriterProxy()
Definition: jsonify.hpp:647
Definition: jsonify.hpp:202
This object changes the current thread&#39;s locale to the default "C" locale for number printing purpose...
Definition: jsonify.hpp:72
std::ostream & operator<<(std::ostream &stream, const Boolean &boolean)
Definition: json.hpp:814
void append(char c)
Definition: jsonify.hpp:342
JSON::Proxy jsonify(const T &)
Definition: jsonify.hpp:779
~NumberWriter()
Definition: jsonify.hpp:211
long long int int_
Definition: jsonify.hpp:320
Definition: attributes.hpp:24
~StringWriter()
Definition: jsonify.hpp:337
~ObjectWriter()
Definition: jsonify.hpp:429
Protocol< WriteRequest, WriteResponse > write
Try< uint32_t > type(const std::string &path)
NullWriter(std::ostream *stream)
Definition: jsonify.hpp:453
Definition: jsonify.hpp:530
unsigned long long int uint_
Definition: jsonify.hpp:321
Try< Nothing > append(const std::string &path, const google::protobuf::Message &message)
Definition: protobuf.hpp:158
void set(bool value)
Definition: jsonify.hpp:192
NumberWriter(std::ostream *stream)
Definition: jsonify.hpp:205
~NullWriter()
Definition: jsonify.hpp:458
Definition: json.hpp:47
void append(const char(&value)[N])
Definition: jsonify.hpp:367
Definition: jsonify.hpp:642
ClassicLocale()
Definition: jsonify.hpp:99
Definition: jsonify.hpp:384