VLink  2.0.0
A high-performance communication middleware
format.h
浏览该文件的文档.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file format.h
26  * @brief Minimal heap-free @c {} placeholder formatter for the logger hot path.
27  *
28  * @details
29  * VLink targets C++17 first; @c std::format is unavailable on that baseline and far heavier on
30  * older standards. This header provides a focused subset of std::format-style formatting that
31  * writes through a stack-allocated buffer or a user-supplied output iterator, never allocates,
32  * and dispatches via a compile-time type tag rather than a virtual call chain.
33  *
34  * @par Supported argument types
35  *
36  * | C++ type | Format token | Example output |
37  * | ------------------------------------- | ------------ | ------------------ |
38  * | @c signed @c char / @c short / @c int | @c {} | @c 42 |
39  * | @c unsigned @c char / ... | @c {} | @c 42 |
40  * | @c long / @c long @c long | @c {} | @c 123456789 |
41  * | @c unsigned @c long / @c long @c long | @c {} | @c 123456789 |
42  * | @c bool | @c {} | @c true / @c false |
43  * | @c char | @c {} | @c A |
44  * | @c float / @c double | @c {} | @c 3.14 |
45  * | @c const @c char* / @c char* | @c {} | @c hello |
46  * | @c std::string / @c std::string_view | @c {} | @c hello |
47  * | @c T* (any pointer) | @c {} | @c 0x7ffe1234 |
48  * | @c enum | @c {} | underlying integer |
49  *
50  * @par Placeholder syntax
51  *
52  * | Token | Meaning |
53  * | -------------------- | ------------------------------------------- |
54  * | @c {} | Consume the next argument in order |
55  * | @c {0}, @c {1}, ... | Explicit positional index |
56  * | @c {{ / @c }} | Literal opening / closing brace |
57  *
58  * @par Public API
59  *
60  * | Function | Output target | Truncation flag |
61  * | --------------------------------- | ------------------------------ | --------------- |
62  * | @c format_to_n(out, n, fmt, ...) | @c char* buffer with cap @p n | yes |
63  * | @c format_to(out[N], fmt, ...) | Fixed-size array | yes |
64  * | @c format_to(it, fmt, ...) | Output iterator | no |
65  *
66  * @par Example
67  * @code
68  * char buf[128];
69  * auto result = vlink::format::format_to_n(buf, sizeof(buf) - 1, "x={} y={}", 3, 4.5);
70  * buf[result.size] = '\0';
71  * // buf == "x=3 y=4.5"
72  * @endcode
73  *
74  * @note Floats and doubles use @c "%g" via @c snprintf; there is no precision modifier in the
75  * placeholder syntax. Unsupported argument types trigger a compile-time @c static_assert.
76  */
77 
78 #pragma once
79 
80 #include <cstddef>
81 #include <cstdint>
82 #include <cstring>
83 #include <string>
84 #include <string_view>
85 #include <type_traits>
86 #include <utility>
87 
88 #include "./macros.h"
89 
90 namespace vlink {
91 
92 /**
93  * @namespace vlink::format
94  * @brief Minimal allocation-free @c {} placeholder formatter.
95  */
96 namespace format {
97 
98 namespace detail {
99 
100 template <typename TypeT>
101 using RemoveCvref = typename std::remove_cv_t<std::remove_reference_t<TypeT>>;
102 
103 template <typename TypeT, typename = void>
104 struct IsOutputIteratorImpl : std::false_type {};
105 
106 template <typename TypeT>
107 struct IsOutputIteratorImpl<TypeT, std::enable_if_t<std::is_assignable_v<decltype(*std::declval<TypeT&>()++), char>>>
108  : std::true_type {};
109 
110 template <>
111 struct IsOutputIteratorImpl<char*> : std::true_type {};
112 
113 template <typename TypeT, size_t NumT>
114 struct IsOutputIteratorImpl<TypeT[NumT]> : std::false_type {};
115 
116 template <typename TypeT>
118 
119 enum class Type : uint8_t {
120  kNone,
121  kInt,
122  kUint,
123  kLongLong,
124  kUlongLong,
125  kBool,
126  kChar,
127  kFloat,
128  kDouble,
129  kString,
130  kCstring,
131  kPointer
132 };
133 
134 // NOLINTBEGIN
135 template <typename T>
136 struct TypeConstant : std::integral_constant<Type, Type::kNone> {};
137 
138 template <>
139 struct TypeConstant<signed char> : std::integral_constant<Type, Type::kInt> {};
140 
141 template <>
142 struct TypeConstant<unsigned char> : std::integral_constant<Type, Type::kUint> {};
143 
144 template <>
145 struct TypeConstant<short> : std::integral_constant<Type, Type::kInt> {};
146 
147 template <>
148 struct TypeConstant<unsigned short> : std::integral_constant<Type, Type::kUint> {};
149 
150 template <>
151 struct TypeConstant<int> : std::integral_constant<Type, Type::kInt> {};
152 
153 template <>
154 struct TypeConstant<unsigned> : std::integral_constant<Type, Type::kUint> {};
155 
156 template <>
157 struct TypeConstant<long> : std::integral_constant<Type, Type::kLongLong> {};
158 
159 template <>
160 struct TypeConstant<unsigned long> : std::integral_constant<Type, Type::kUlongLong> {};
161 
162 template <>
163 struct TypeConstant<long long> : std::integral_constant<Type, Type::kLongLong> {};
164 
165 template <>
166 struct TypeConstant<unsigned long long> : std::integral_constant<Type, Type::kUlongLong> {};
167 
168 template <>
169 struct TypeConstant<bool> : std::integral_constant<Type, Type::kBool> {};
170 
171 template <>
172 struct TypeConstant<char> : std::integral_constant<Type, Type::kChar> {};
173 
174 template <>
175 struct TypeConstant<float> : std::integral_constant<Type, Type::kFloat> {};
176 
177 template <>
178 struct TypeConstant<double> : std::integral_constant<Type, Type::kDouble> {};
179 
180 template <>
181 struct TypeConstant<const char*> : std::integral_constant<Type, Type::kCstring> {};
182 
183 template <>
184 struct TypeConstant<char*> : std::integral_constant<Type, Type::kCstring> {};
185 
186 template <>
187 struct TypeConstant<std::string_view> : std::integral_constant<Type, Type::kString> {};
188 
189 template <>
190 struct TypeConstant<std::string> : std::integral_constant<Type, Type::kString> {};
191 
192 template <size_t NumT>
193 struct TypeConstant<char[NumT]> : std::integral_constant<Type, Type::kCstring> {};
194 
195 template <size_t NumT>
196 struct TypeConstant<const char[NumT]> : std::integral_constant<Type, Type::kCstring> {};
197 
198 template <typename T>
199 struct TypeConstant<T*> : std::integral_constant<Type, Type::kPointer> {};
200 // NOLINTEND
201 
202 VLINK_EXPORT size_t format_uint_to(char* buf, unsigned value) noexcept;
203 
204 VLINK_EXPORT size_t format_int_to(char* buf, int value) noexcept;
205 
207  unsigned long long value) noexcept; // NOLINT(runtime/int,google-runtime-int)
208 
210  long long value) noexcept; // NOLINT(runtime/int,google-runtime-int)
211 
212 VLINK_EXPORT size_t format_pointer_to(char* buf, const void* ptr) noexcept;
213 
214 VLINK_EXPORT size_t format_float_to(char* buf, size_t buflen, float value) noexcept;
215 
216 VLINK_EXPORT size_t format_double_to(char* buf, size_t buflen, double value) noexcept;
217 
219  public:
220  StringWriter(char* buf, size_t size) noexcept;
221 
222  char* out() const noexcept;
223 
224  size_t written() const noexcept;
225 
226  size_t total_size() const noexcept;
227 
228  void write(char c);
229 
230  void write(const char* s, size_t count);
231 
232  void write(std::string_view sv);
233 
234  private:
235  char* begin_{nullptr};
236  char* ptr_{nullptr};
237  char* end_{nullptr};
238  size_t total_size_{0};
239 };
240 
241 template <typename OutputItT>
243  public:
244  inline explicit IteratorWriter(OutputItT out) : out_(out) {}
245 
246  inline OutputItT out() const noexcept { return out_; }
247 
248  inline size_t size() const noexcept { return count_; }
249 
250  inline void write(char c) {
251  *out_++ = c;
252  ++count_;
253  }
254 
255  inline void write(const char* s, size_t count) {
256  for (size_t i = 0; i < count; ++i) {
257  *out_++ = s[i];
258  }
259 
260  count_ += count;
261  }
262 
263  inline void write(std::string_view sv) { write(sv.data(), sv.size()); }
264 
265  private:
266  OutputItT out_;
267  size_t count_{0};
268 };
269 
270 template <typename CharT>
271 class Value {
272  public:
273  // NOLINTBEGIN
274  union {
276  unsigned uint_value;
277  long long long_long_value;
278  unsigned long long ulong_long_value;
280  CharT char_value;
281  float float_value;
282  double double_value;
283  const CharT* string_value;
284  std::string_view string_view_value;
285  const void* pointer_value;
286  };
287 
288  constexpr Value() : int_value(0) {}
289 
290  constexpr explicit Value(signed char val) : int_value(static_cast<int>(val)) {}
291 
292  constexpr explicit Value(unsigned char val) : uint_value(static_cast<unsigned>(val)) {}
293 
294  constexpr explicit Value(short val) : int_value(static_cast<int>(val)) {}
295 
296  constexpr explicit Value(unsigned short val) : uint_value(static_cast<unsigned>(val)) {}
297 
298  constexpr explicit Value(int val) : int_value(val) {}
299 
300  constexpr explicit Value(unsigned val) : uint_value(val) {}
301 
302  constexpr explicit Value(long val) : long_long_value(val) {}
303 
304  constexpr explicit Value(unsigned long val) : ulong_long_value(val) {}
305 
306  constexpr explicit Value(long long val) : long_long_value(val) {}
307 
308  constexpr explicit Value(unsigned long long val) : ulong_long_value(val) {}
309 
310  constexpr explicit Value(bool val) : bool_value(val) {}
311 
312  constexpr explicit Value(CharT val) : char_value(val) {}
313 
314  constexpr explicit Value(float val) : float_value(val) {}
315 
316  constexpr explicit Value(double val) : double_value(val) {}
317 
318  constexpr explicit Value(const CharT* val) : string_value(val) {}
319 
320  constexpr explicit Value(CharT* val) : string_value(val) {}
321 
322  constexpr explicit Value(std::string_view val) : string_view_value(val) {}
323 
324  constexpr explicit Value(const std::string& val) : string_view_value(val) {}
325 
326  template <size_t NumT>
327  constexpr explicit Value(const CharT (&val)[NumT]) : string_value(val) {}
328 
329  template <size_t NumT>
330  constexpr explicit Value(CharT (&val)[NumT]) : string_value(val) {}
331 
332  template <typename TypeT>
333  constexpr explicit Value(TypeT* val) : pointer_value(static_cast<const void*>(val)) {}
334  // NOLINTEND
335 };
336 
337 template <typename CharT>
338 class FormatArg {
339  public:
340  constexpr FormatArg() = default; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
341 
342  template <typename TypeT>
343  constexpr explicit FormatArg(const TypeT& val) {
344  if constexpr (std::is_enum_v<TypeT>) {
345  using UnderlyingT = std::underlying_type_t<TypeT>;
346  value_ = Value<CharT>(static_cast<UnderlyingT>(val));
348  } else if constexpr (TypeConstant<RemoveCvref<TypeT>>::value != Type::kNone) {
349  value_ = Value<CharT>(val);
351  } else {
352  static_assert(!sizeof(TypeT),
353  "[vlink::format] unsupported type for format_to/MLOG, "
354  "convert to string first");
355  }
356  }
357 
358  constexpr Type type() const { return type_; }
359  constexpr const Value<CharT>& value() const { return value_; }
360 
361  private:
362  Value<CharT> value_;
363  Type type_{Type::kNone};
364 };
365 
366 template <typename CharT, typename... ArgsT>
368  static constexpr size_t kNumArgs = sizeof...(ArgsT);
370 
371  template <typename... ValuesT>
372  constexpr explicit FormatArgStore(const ValuesT&... values) : args{FormatArg<CharT>(values)...} {}
373 };
374 
375 template <typename CharT>
377  public:
378  constexpr BasicFormatArgs() : args_(nullptr), size_(0) {}
379 
380  template <typename... ArgsT>
381  constexpr explicit BasicFormatArgs(const FormatArgStore<CharT, ArgsT...>& store)
382  : args_(store.args), size_(sizeof...(ArgsT)) {}
383 
384  constexpr FormatArg<CharT> get(size_t id) const {
385  return id < size_ ? args_[id] : FormatArg<CharT>();
386  } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
387 
388  constexpr size_t size() const { return size_; }
389 
390  private:
391  const FormatArg<CharT>* args_;
392  size_t size_;
393 };
394 
396 
397 template <typename CharT, typename WriterT>
399  public:
400  inline explicit FormatWriter(WriterT writer) : writer_(writer) {}
401 
402  void format(std::string_view fmt, BasicFormatArgs<CharT> args) {
403  size_t arg_id = 0;
404  const char* p = fmt.data();
405  const char* end = p + fmt.size();
406 
407  while (p != end) {
408  char c = *p++;
409 
410  if (c == '}') {
411  if (p != end && *p == '}') {
412  writer_.write('}');
413  ++p;
414  } else {
415  writer_.write('}');
416  }
417 
418  continue;
419  }
420 
421  if VLIKELY (c != '{') {
422  writer_.write(c);
423  continue;
424  }
425 
426  if VUNLIKELY (p == end) {
427  writer_.write('{');
428  break;
429  }
430 
431  if (*p == '{') {
432  writer_.write('{');
433  ++p;
434  continue;
435  }
436 
437  if VLIKELY (*p == '}') {
438  if VLIKELY (arg_id < args.size()) {
439  write_arg(args.get(arg_id++));
440  }
441 
442  ++p;
443 
444  continue;
445  }
446 
447  size_t index = arg_id;
448  bool has_explicit_index = false;
449 
450  if (*p >= '0' && *p <= '9') {
451  index = 0;
452  has_explicit_index = true;
453 
454  while (p != end && *p >= '0' && *p <= '9') {
455  index = index * 10 + static_cast<size_t>(*p++ - '0');
456  }
457  }
458 
459  while (p != end && *p != '}') {
460  ++p;
461  }
462 
463  if VLIKELY (p != end) {
464  if VLIKELY (index < args.size()) {
465  write_arg(args.get(index));
466  }
467 
468  if VLIKELY (!has_explicit_index) {
469  ++arg_id;
470  }
471 
472  ++p;
473  }
474  }
475  }
476 
477  inline auto out() const { return writer_.out(); }
478 
479  template <typename WriterImplT = WriterT>
480  inline auto total_size() const -> decltype(std::declval<WriterImplT>().total_size()) {
481  return writer_.total_size();
482  }
483 
484  inline size_t size() const { return writer_.size(); }
485 
486  private:
487  // NOLINTBEGIN
488  void write_int(int value) {
489  char buf[11];
490  size_t n = format_int_to(buf, value);
491  writer_.write(buf, n);
492  }
493 
494  void write_uint(unsigned value) {
495  char buf[10];
496  size_t n = format_uint_to(buf, value);
497  writer_.write(buf, n);
498  }
499 
500  void write_long_long(long long value) {
501  char buf[20];
502  size_t n = format_long_long_to(buf, value);
503  writer_.write(buf, n);
504  }
505 
506  void write_ulong_long(unsigned long long value) {
507  char buf[20];
508  size_t n = format_ulong_long_to(buf, value);
509  writer_.write(buf, n);
510  }
511 
512  void write_bool(bool value) {
513  if (value) {
514  writer_.write("true", 4);
515  } else {
516  writer_.write("false", 5);
517  }
518  }
519 
520  void write_char(char value) { writer_.write(value); }
521 
522  void write_string(const char* str) {
523  if VLIKELY (str) {
524  writer_.write(str, std::strlen(str));
525  } else {
526  writer_.write("(null)", 6);
527  }
528  }
529 
530  void write_string_view(std::string_view sv) { writer_.write(sv); }
531 
532  void write_pointer(const void* ptr) {
533  char buf[18];
534  size_t n = format_pointer_to(buf, ptr);
535  writer_.write(buf, n);
536  }
537 
538  void write_float(float value) {
539  char buf[32];
540  size_t n = format_float_to(buf, sizeof(buf), value);
541  writer_.write(buf, n);
542  }
543 
544  void write_double(double value) {
545  char buf[32];
546  size_t n = format_double_to(buf, sizeof(buf), value);
547  writer_.write(buf, n);
548  }
549 
550  void write_arg(const FormatArg<CharT>& arg) {
551  switch (arg.type()) {
552  case Type::kInt:
553  write_int(arg.value().int_value);
554  break;
555  case Type::kUint:
556  write_uint(arg.value().uint_value);
557  break;
558  case Type::kLongLong:
559  write_long_long(arg.value().long_long_value);
560  break;
561  case Type::kUlongLong:
562  write_ulong_long(arg.value().ulong_long_value);
563  break;
564  case Type::kBool:
565  write_bool(arg.value().bool_value);
566  break;
567  case Type::kChar:
568  write_char(arg.value().char_value);
569  break;
570  case Type::kFloat:
571  write_float(arg.value().float_value);
572  break;
573  case Type::kDouble:
574  write_double(arg.value().double_value);
575  break;
576  case Type::kCstring:
577  write_string(arg.value().string_value);
578  break;
579  case Type::kString:
580  write_string_view(arg.value().string_view_value);
581  break;
582  case Type::kPointer:
583  write_pointer(arg.value().pointer_value);
584  break;
585  default: // LCOV_EXCL_LINE GCOVR_EXCL_LINE
586  break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
587  }
588  }
589  // NOLINTEND
590 
591  WriterT writer_;
592 };
593 
594 } // namespace detail
595 
596 /**
597  * @struct FString
598  * @brief Compile-time format-string wrapper carrying the expected argument list.
599  *
600  * @details
601  * Wraps a @c std::string_view tagged with the argument types so a call site is type-checked
602  * implicitly without runtime dispatch. Constructible directly from string literals so format
603  * arguments accept @c "fmt" without a cast.
604  *
605  * @tparam ArgsT Argument types expected by the format string (compile-time only).
606  */
607 template <typename... ArgsT>
608 struct FString {
609  std::string_view str;
610  using t = FString;
611 
612  // Implicit ctors are intentional: lets format-string parameters accept string literals directly,
613  // e.g. @c format_to_n(buf, n, "x={}", val).
614  template <size_t NumT>
615  // NOLINTNEXTLINE(runtime/explicit,google-explicit-constructor,hicpp-explicit-conversions)
616  constexpr FString(const char (&s)[NumT]) : str(s, NumT - 1) {}
617 
618  // NOLINTNEXTLINE(modernize-use-constraints)
619  template <typename StrT, std::enable_if_t<std::is_convertible_v<const StrT&, std::string_view>, int> = 0>
620  // NOLINTNEXTLINE(runtime/explicit,google-explicit-constructor,hicpp-explicit-conversions)
621  constexpr FString(const StrT& s) : str(s) {}
622 
623  // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
624  inline operator std::string_view() const { return str; }
625 
626  std::string_view get() const { return str; }
627 };
628 
629 /**
630  * @brief Convenience alias used as the formal type of format-string parameters.
631  *
632  * @tparam ArgsT Expected argument types.
633  */
634 template <typename... ArgsT>
635 using format_string = typename FString<ArgsT...>::t;
636 
637 /**
638  * @struct FormatToNResult
639  * @brief Return type of @c format_to_n; carries the end iterator, total size and truncation flag.
640  *
641  * @tparam OutputItT Iterator or pointer type used for output.
642  */
643 template <typename OutputItT>
645  OutputItT out; ///< Iterator one past the last written character.
646  size_t size{0}; ///< Total characters that the format would have written.
647  bool truncated{false}; ///< @c true when the output was truncated because @c size > @c n.
648 };
649 
650 /**
651  * @struct FormatToResult
652  * @brief Return type of the fixed-array @c format_to overload.
653  */
655  char* out; ///< Pointer one past the last written character.
656  size_t size; ///< Total characters that the format would have written.
657  bool truncated; ///< @c true when the output was truncated.
658 };
659 
660 ////////////////////////////////////////////////////////////////
661 /// Public API
662 ////////////////////////////////////////////////////////////////
663 
664 /**
665  * @brief Builds a type-erased argument store from a parameter pack.
666  *
667  * @tparam ArgsT Argument types.
668  * @param args Argument values.
669  * @return Argument store usable by the format engine.
670  */
671 template <typename... ArgsT>
673 
674 /**
675  * @brief Formats @p args into @p out, writing at most @p n characters.
676  *
677  * @details
678  * Placeholders in @p fmt are substituted with @p args in order. When the produced text would
679  * exceed @p n the output is truncated and @c truncated is set to @c true. The caller is
680  * responsible for null termination if needed.
681  *
682  * @tparam ArgsT Argument types (deduced).
683  * @param out Destination buffer with capacity of at least @p n bytes.
684  * @param n Maximum characters to write, not counting a null terminator.
685  * @param fmt Format string with @c {} placeholders.
686  * @param args Arguments to substitute.
687  * @return @c FormatToNResult describing the end iterator, total size and truncation flag.
688  */
689 template <typename... ArgsT>
690 inline FormatToNResult<char*> format_to_n(char* out, size_t n, format_string<ArgsT...> fmt, const ArgsT&... args);
691 
692 /**
693  * @brief Formats @p args into a fixed-size character array; equivalent to @c format_to_n.
694  *
695  * @tparam NumT Array size (deduced).
696  * @tparam ArgsT Argument types.
697  * @param out Destination array.
698  * @param fmt Format string.
699  * @param args Arguments to substitute.
700  * @return @c FormatToResult describing the end pointer, total size and truncation flag.
701  */
702 template <size_t NumT, typename... ArgsT>
703 inline FormatToResult format_to(char (&out)[NumT], format_string<ArgsT...> fmt, const ArgsT&... args);
704 
705 /**
706  * @brief Formats @p args through an output iterator.
707  *
708  * @details
709  * Writes each character via @c *out++ = @c c. The iterator must satisfy the OutputIterator
710  * concept; arrays are explicitly excluded so the fixed-array overload wins overload resolution.
711  *
712  * @tparam OutputItT Output iterator type.
713  * @tparam ArgsT Argument types.
714  * @param out Destination iterator.
715  * @param fmt Format string.
716  * @param args Arguments to substitute.
717  * @return Iterator one past the last written character.
718  */
719 template <typename OutputItT, typename... ArgsT,
720  // NOLINTNEXTLINE(modernize-use-constraints)
721  std::enable_if_t<detail::kIsOutputIterator<detail::RemoveCvref<OutputItT>> &&
722  !std::is_array_v<std::remove_reference_t<OutputItT>>,
723  int> = 0>
724 inline detail::RemoveCvref<OutputItT> format_to(OutputItT&& out, format_string<ArgsT...> fmt, const ArgsT&... args);
725 
726 } // namespace format
727 
728 ////////////////////////////////////////////////////////////////
729 /// Details
730 ////////////////////////////////////////////////////////////////
731 
732 /// @cond INTERNAL
733 
734 template <typename... ArgsT>
736  const ArgsT&... args) {
738 }
739 
740 template <typename... ArgsT>
741 inline format::FormatToNResult<char*> format::format_to_n(char* out, size_t n, format_string<ArgsT...> fmt,
742  const ArgsT&... args) {
743  format::detail::FormatArgStore<char, format::detail::RemoveCvref<ArgsT>...> arg_store{args...};
744 
745  format::detail::FormatArgs fargs(arg_store);
746  format::detail::StringWriter sw(out, n);
747  format::detail::FormatWriter<char, format::detail::StringWriter> writer(sw);
748  writer.format(fmt.get(), fargs);
749 
750  size_t total = writer.total_size();
751 
752  return {writer.out(), total, total > n};
753 }
754 
755 template <size_t NumT, typename... ArgsT>
756 inline format::FormatToResult format::format_to(char (&out)[NumT], format_string<ArgsT...> fmt, const ArgsT&... args) {
757  auto result = ::vlink::format::format_to_n(out, NumT, fmt, args...);
758 
759  return {result.out, result.size, result.truncated};
760 }
761 
762 template <typename OutputItT, typename... ArgsT,
763  std::enable_if_t<format::detail::kIsOutputIterator<format::detail::RemoveCvref<OutputItT>> &&
764  !std::is_array_v<std::remove_reference_t<OutputItT>>,
765  int>>
766 inline format::detail::RemoveCvref<OutputItT> format::format_to(OutputItT&& out, format_string<ArgsT...> fmt,
767  const ArgsT&... args) {
768  using ItT = format::detail::RemoveCvref<OutputItT>;
769 
770  auto arg_store = ::vlink::format::make_format_args(args...);
771 
772  format::detail::FormatArgs fargs(arg_store);
773  format::detail::IteratorWriter<ItT> iter_writer(out);
774  format::detail::FormatWriter<char, format::detail::IteratorWriter<ItT>> writer(iter_writer);
775 
776  writer.format(fmt.get(), fargs);
777 
778  return writer.out();
779 }
780 
781 /// @endcond
782 
783 } // namespace vlink
Cross-platform macros for visibility, branch hints, copy prevention, singletons and string helpers.
#define VUNLIKELY(...)
Short alias for VLINK_UNLIKELY.
Definition: macros.h:289
#define VLINK_EXPORT
Definition: macros.h:81
#define VLIKELY(...)
Short alias for VLINK_LIKELY.
Definition: macros.h:284