NexusForce 1.0.0
A Modern C++ Library with extended functionality, web components, and utility libraries
载入中...
搜索中...
未找到
format.hpp
浏览该文件的文档.
1#ifndef NEFORCE_CORE_STRING_FORMAT_HPP__
2#define NEFORCE_CORE_STRING_FORMAT_HPP__
3
11
13NEFORCE_BEGIN_NAMESPACE__
14
20
32
48
56 char fill = ' ';
59 int width = 0;
60 int precision = -1;
61 bool uppercase = false;
62 bool alternate = false;
63 bool zero_pad = false;
64 bool show_sign = false;
65 bool space_sign = false;
66};
67
69NEFORCE_BEGIN_INNER__
70
71NEFORCE_CONSTEXPR20 string uint_to_string_base(uint64_t value, const int base, const bool uppercase) {
72 if (value == 0) {
73 return "0";
74 }
75
76 string result;
77 result.reserve(20);
78
79 constexpr auto digits_lower = "0123456789abcdef";
80 constexpr auto digits_upper = "0123456789ABCDEF";
81 const auto* digits = uppercase ? digits_upper : digits_lower;
82
83 while (value > 0) {
84 const uint64_t remainder = value % base;
85 value /= base;
86 result.push_back(digits[remainder]);
87 }
88
89 result.reverse();
90 return result;
91}
92
93constexpr format_align to_number_alignment(const char c) {
94 switch (c) {
95 case '<':
96 return format_align::LEFT;
97 case '>':
99 case '^':
101 case '=':
103 default:
105 }
106}
107
108constexpr format_type to_number_type(const char c) {
109 switch (c) {
110 case 'd':
112 case 'b':
113 case 'B':
114 return format_type::BINARY;
115 case 'o':
116 return format_type::OCTAL;
117 case 'x':
118 case 'X':
119 return format_type::HEX;
120 case 'e':
121 case 'E':
123 case 'f':
124 case 'F':
125 return format_type::FIXED;
126 case 'g':
127 case 'G':
129 case 'c':
130 return format_type::CHAR;
131 default:
133 }
134}
135
136constexpr format_options parse_number_format(const string_view& fmt_str) {
137 format_options options;
138 size_t pos = 0;
139
140 if (fmt_str.empty()) {
141 return options;
142 }
143
144 bool found_align = false;
145 if (pos + 1 < fmt_str.size()) {
146 const char first_char = fmt_str[pos];
147 const char second_char = fmt_str[pos + 1];
148
149 if (second_char == '<' || second_char == '>' || second_char == '^' || second_char == '=') {
150 if (first_char != '+' && first_char != '-' && first_char != ' ') {
151 options.fill = first_char;
152 options.align = to_number_alignment(second_char);
153 pos += 2;
154 found_align = true;
155 }
156 }
157 }
158
159 if (!found_align && pos < fmt_str.size()) {
160 const char c = fmt_str[pos];
161 if (c == '<' || c == '>' || c == '^' || c == '=') {
162 options.align = to_number_alignment(c);
163 ++pos;
164 }
165 }
166
167 if (pos < fmt_str.size()) {
168 const char c = fmt_str[pos];
169 if (c == '+') {
170 options.show_sign = true;
171 ++pos;
172 } else if (c == ' ') {
173 options.space_sign = true;
174 ++pos;
175 } else if (c == '-') {
176 ++pos;
177 if (pos < fmt_str.size()) {
178 const char next = fmt_str[pos];
179 if (next == '<' || next == '>' || next == '^' || next == '=') {
180 options.align = to_number_alignment(next);
181 ++pos;
182 }
183 }
184 }
185 }
186
187 if (pos < fmt_str.size() && fmt_str[pos] == '#') {
188 options.alternate = true;
189 ++pos;
190 }
191
192 if (pos < fmt_str.size() && fmt_str[pos] == '0' && options.fill == ' ' && options.align == format_align::DEFAULT) {
193 options.zero_pad = true;
194 options.fill = '0';
195 ++pos;
196 }
197
198 if (pos < fmt_str.size() && is_digit(fmt_str[pos])) {
199 int width = 0;
200 while (pos < fmt_str.size() && is_digit(fmt_str[pos])) {
201 width = width * 10 + (fmt_str[pos] - '0');
202 ++pos;
203 }
204 options.width = width;
205 }
206
207 if (pos < fmt_str.size() && fmt_str[pos] == '.') {
208 ++pos;
209 int precision = 0;
210 while (pos < fmt_str.size() && is_digit(fmt_str[pos])) {
211 precision = precision * 10 + (fmt_str[pos] - '0');
212 ++pos;
213 }
214 options.precision = precision;
215 }
216
217 if (pos < fmt_str.size()) {
218 const char c = fmt_str[pos];
219 options.type = to_number_type(c);
220 if (c == 'X' || c == 'E' || c == 'G' || c == 'B') {
221 options.uppercase = true;
222 }
223 ++pos;
224 }
225
226 return options;
227}
228
229
230NEFORCE_CONSTEXPR20 string apply_format_options(string raw, const format_options& options,
231 const bool is_numeric = false) {
232 char existing_sign = '\0';
233 if (!raw.empty() && (raw[0] == '-' || raw[0] == '+' || raw[0] == ' ')) {
234 char sign = raw[0];
235 raw = raw.tail(1);
236 existing_sign = sign;
237 }
238
239 string prefix;
240 if (options.alternate && is_numeric) {
241 switch (options.type) {
242 case format_type::HEX: {
243 prefix = options.uppercase ? "0X" : "0x";
244 break;
245 }
246 case format_type::BINARY: {
247 prefix = options.uppercase ? "0B" : "0b";
248 break;
249 }
250 case format_type::OCTAL: {
251 if (raw.empty() || raw[0] != '0') {
252 prefix = "0";
253 }
254 break;
255 }
256 default: {
257 break;
258 }
259 }
260 }
261
262 string sign_str;
263 if (existing_sign == '-') {
264 sign_str = "-";
265 } else if (options.show_sign) {
266 sign_str = "+";
267 } else if (options.space_sign) {
268 sign_str = " ";
269 }
270
271 const size_t content_len = sign_str.size() + prefix.size() + raw.size();
272 const size_t target_width = (options.width > 0) ? static_cast<size_t>(options.width) : 0;
273
274 const size_t pad_total = (content_len < target_width) ? target_width - content_len : 0;
275
276 format_align align = options.align;
277 if (align == format_align::DEFAULT) {
278 align = is_numeric ? format_align::RIGHT : format_align::LEFT;
279 }
280
281 if (options.zero_pad && is_numeric && align == format_align::RIGHT) {
282 align = format_align::NUMERIC;
283 }
284
285 if (align == format_align::NUMERIC && is_numeric) {
286 const char fill_char = options.fill;
287 string result;
288 result.reserve(target_width > 0 ? target_width : content_len);
289 result += sign_str;
290 result += prefix;
291 for (size_t i = 0; i < pad_total; ++i) {
292 result += fill_char;
293 }
294 result += raw;
295 return result;
296 }
297
298 const char fill_char = options.fill;
299 string left_pad;
300 string right_pad;
301
302 switch (align) {
303 case format_align::LEFT: {
304 right_pad = string(pad_total, fill_char);
305 break;
306 }
308 const size_t left_count = pad_total / 2;
309 const size_t right_count = pad_total - left_count;
310 left_pad = string(left_count, fill_char);
311 right_pad = string(right_count, fill_char);
312 break;
313 }
315 default: {
316 left_pad = string(pad_total, fill_char);
317 break;
318 }
319 }
320
321 string result;
322 result.reserve(target_width > 0 ? target_width : content_len);
323 result += left_pad;
324 result += sign_str;
325 result += prefix;
326 result += raw;
327 result += right_pad;
328 return result;
329}
330
331template <typename T, bool Signed>
332struct integer_formatter_impl {
333 NEFORCE_CONSTEXPR20 string operator()(const T value, const format_options& options) const {
335
336 const bool is_negative = Signed && (value < 0);
337 const UT abs_value = is_negative ? static_cast<UT>(0 - static_cast<UT>(value)) : static_cast<UT>(value);
338 const auto compatible = static_cast<uint64_t>(abs_value);
339
340 string raw;
341
342 switch (options.type) {
343 case format_type::BINARY: {
344 raw = inner::uint_to_string_base(compatible, 2, options.uppercase);
345 break;
346 }
347 case format_type::OCTAL: {
348 raw = inner::uint_to_string_base(compatible, 8, options.uppercase);
349 break;
350 }
351 case format_type::HEX: {
352 raw = inner::uint_to_string_base(compatible, 16, options.uppercase);
353 break;
354 }
355 case format_type::CHAR: {
356 return inner::apply_format_options(string(1, static_cast<char>(value)), options, false);
357 }
360 default: {
361 raw = inner::__int_to_string_dispatch(value);
362 return inner::apply_format_options(_NEFORCE move(raw), options, true);
363 }
364 }
365
366 if (is_negative) {
367 raw = "-" + raw;
368 }
369 return inner::apply_format_options(_NEFORCE move(raw), options, true);
370 }
371};
372
373NEFORCE_END_INNER__
375
384template <typename Number, typename Dummy = void>
386
391template <typename T>
399 NEFORCE_CONSTEXPR20 string operator()(const T& value, const format_options& options) const {
400 const int prec = (options.precision >= 0) ? options.precision : 6;
401 string raw;
402
403 switch (options.type) {
405 raw = _NEFORCE to_string_scientific(value, prec);
406 break;
407 }
408 case format_type::FIXED: {
409 raw = _NEFORCE to_string_fixed(value, prec);
410 break;
411 }
415 default: {
416 raw = _NEFORCE to_string_general(value, prec);
417 break;
418 }
419 }
420
421 if (options.uppercase) {
422 for (auto& c: raw) {
423 if (c == 'e') {
424 c = 'E';
425 break;
426 }
427 }
428 }
429
430 return inner::apply_format_options(_NEFORCE move(raw), options, true);
431 }
432};
433
438template <typename T>
446 NEFORCE_CONSTEXPR20 string operator()(const T value, const format_options& options) const {
447 return inner::integer_formatter_impl<T, true>{}(value, options);
448 }
449};
450
455template <typename T>
463 NEFORCE_CONSTEXPR20 string operator()(const T value, const format_options& options) const {
464 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
465 return inner::integer_formatter_impl<T, false>{}(value, options);
466 }
467};
468
472template <>
473struct formatter<char> {
474 NEFORCE_CONSTEXPR20 string operator()(const char value, const format_options& options) const {
475 switch (options.type) {
478 case format_type::HEX:
480 return inner::integer_formatter_impl<int, true>{}(static_cast<int>(value), options);
481 }
482 default: {
483 break;
484 }
485 }
486 return inner::apply_format_options(string(1, value), options, false);
487 }
488};
489
490template <typename T>
492 NEFORCE_CONSTEXPR20 string operator()(const T value, const format_options& options) const {
493 return formatter<unpackage_t<T>>()(value.value(), options);
494 }
495};
496
500template <>
501struct formatter<bool> {
502 NEFORCE_CONSTEXPR20 string operator()(const bool value, const format_options& options) const {
503 switch (options.type) {
506 case format_type::HEX:
508 return inner::integer_formatter_impl<int, false>{}(static_cast<int>(value), options);
509 }
510 default: {
511 break;
512 }
513 }
514 return inner::apply_format_options(value ? "true" : "false", options, false);
515 }
516};
517
521template <>
523 NEFORCE_CONSTEXPR20 string operator()(const string& value, const format_options& options) const {
524 string raw = value;
525 if (options.precision >= 0 && raw.size() > static_cast<size_t>(options.precision)) {
526 raw = raw.head(static_cast<size_t>(options.precision));
527 }
528 return inner::apply_format_options(_NEFORCE move(raw), options, false);
529 }
530};
531
535template <>
537 NEFORCE_CONSTEXPR20 string operator()(const string_view value, const format_options& options) const {
538 return formatter<string>()(string(value), options);
539 }
540};
541
545template <>
546struct formatter<const char*> {
547 NEFORCE_CONSTEXPR20 string operator()(const char* value, const format_options& options) const {
548 if (value == nullptr) {
549 return inner::apply_format_options("nullptr", options, false);
550 }
551 return formatter<string>{}(string(value), options);
552 }
553};
554
558template <>
560 NEFORCE_CONSTEXPR20 string operator()(nullptr_t, const format_options& options) const {
561 return inner::apply_format_options("nullptr", options, false);
562 }
563};
564
568template <typename T>
570 NEFORCE_CONSTEXPR20 string operator()(const T* ptr, const format_options& options) const {
571 return inner::apply_format_options(_NEFORCE address_string(ptr), options, false);
572 }
573};
574
578template <>
579struct formatter<char*> {
580 NEFORCE_CONSTEXPR20 string operator()(char* value, const format_options& options) const {
581 return formatter<string>()(string(value), options);
582 }
583};
584
586NEFORCE_BEGIN_INNER__
587
596NEFORCE_CONSTEXPR20 void format_impl(const string_view fmt, size_t& pos, string& out) {
597 while (pos < fmt.size()) {
598 if (fmt[pos] == '{') {
599 if (pos + 1 < fmt.size() && fmt[pos + 1] == '{') {
600 out += '{';
601 pos += 2;
602 } else {
603 NEFORCE_THROW_EXCEPTION(value_exception("Not enough arguments"));
604 }
605 } else if (fmt[pos] == '}') {
606 if (pos + 1 < fmt.size() && fmt[pos + 1] == '}') {
607 out += '}';
608 pos += 2;
609 } else {
610 NEFORCE_THROW_EXCEPTION(value_exception("Unmatched '}'"));
611 }
612 } else {
613 out += fmt[pos++];
614 }
615 }
616}
617
630template <typename First, typename... Rest>
631NEFORCE_CONSTEXPR20 void format_impl(const string_view fmt, size_t& pos, string& out, First&& first, Rest&&... rest) {
632 while (pos < fmt.size()) {
633 if (fmt[pos] == '{') {
634 if (pos + 1 < fmt.size() && fmt[pos + 1] == '{') {
635 out += '{';
636 pos += 2;
637 continue;
638 }
639 ++pos;
640 size_t end_pos = pos;
641 int depth = 1;
642 while (end_pos < fmt.size() && depth > 0) {
643 if (fmt[end_pos] == '{') {
644 ++depth;
645 } else if (fmt[end_pos] == '}') {
646 --depth;
647 }
648 if (depth > 0) {
649 ++end_pos;
650 }
651 }
652 if (depth != 0) {
653 NEFORCE_THROW_EXCEPTION(value_exception("Unmatched '{' in format string"));
654 }
655
656 const string_view spec_str = fmt.substr(pos, end_pos - pos);
657 pos = end_pos + 1;
658
659 format_options opts;
660 if (spec_str.empty()) {
661 opts = inner::parse_number_format("");
662 } else if (spec_str[0] == ':') {
663 opts = inner::parse_number_format(spec_str.tail(1));
664 } else {
665 opts = format_options{};
666 }
667 out += formatter<decay_t<First>>()(_NEFORCE forward<First>(first), opts);
668 inner::format_impl(fmt, pos, out, _NEFORCE forward<Rest>(rest)...);
669 return;
670 } else if (fmt[pos] == '}') {
671 if (pos + 1 < fmt.size() && fmt[pos + 1] == '}') {
672 out += '}';
673 pos += 2;
674 } else {
675 NEFORCE_THROW_EXCEPTION(value_exception("Unmatched '}' in format string"));
676 }
677 } else {
678 out += fmt[pos++];
679 }
680 }
681}
682
683NEFORCE_END_INNER__
685
694template <typename... Args, enable_if_t<(sizeof...(Args) > 0), int> = 0>
695NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string format(const string_view fmt, Args&&... args) {
696 string result;
697 result.reserve(fmt.size() + sizeof...(Args) * 8);
698 size_t pos = 0;
699 inner::format_impl(fmt, pos, result, _NEFORCE forward<Args>(args)...);
700 return result;
701}
702 // Format
704
705NEFORCE_END_NAMESPACE__
706#endif // NEFORCE_CORE_STRING_FORMAT_HPP__
constexpr size_type size() const noexcept
获取字符串长度
constexpr basic_string_view tail(const size_type off=0) const
获取尾部子串
constexpr bool empty() const noexcept
检查是否为空
constexpr basic_string_view substr(const size_type off=0, const size_type count=npos) const
获取子视图
constexpr bool empty() const noexcept
检查是否为空
constexpr size_type size() const noexcept
获取字符数
constexpr basic_string head(const size_type count=npos) const
获取头部子串
constexpr void push_back(value_type value)
在末尾插入字符
constexpr void reverse() noexcept
反转字符串
constexpr basic_string tail(const size_type off=0) const
获取尾部子串
constexpr void reserve(const size_type n)
预留容量
constexpr T && forward(remove_reference_t< T > &x) noexcept
完美转发左值
constexpr bool is_signed_v
is_signed的便捷变量模板
constexpr bool is_floating_point_v
is_floating_point的便捷变量模板
constexpr bool is_unsigned_v
is_unsigned的便捷变量模板
constexpr bool is_standard_integral_v
is_standard_integral的便捷变量模板
constexpr bool is_unpackaged_v
is_unpackaged的便捷变量模板
constexpr bool is_base_of_v
is_base_of的便捷变量模板
constexpr bool is_digit(const CharT c) noexcept
检查字符是否为数字
unsigned char uint8_t
8位无符号整数类型
unsigned long long uint64_t
64位无符号整数类型
decltype(nullptr) nullptr_t
空指针类型
format_align
对齐方式枚举
constexpr string format(const string_view fmt, Args &&... args)
格式化字符串
format_type
数值类型格式枚举
@ RIGHT
右对齐 '>'
@ DEFAULT
默认(数字右对齐,其他左对齐)
@ LEFT
左对齐 '<'
@ NUMERIC
符号感知填充 '='
@ CENTER
居中 '^'
@ CHAR
字符 'c'
@ DECIMAL
十进制 'd' / 'f' / 'g'
@ HEX
十六进制 'x' / 'X'
@ DEFAULT
默认(由类型决定)
@ OCTAL
八进制 'o'
@ BINARY
二进制 'b'
@ GENERAL
通用浮点 'g' / 'G'
@ SCIENTIFIC
科学计数法 'e' / 'E'
@ FIXED
固定小数 'f'
constexpr Iterator next(Iterator iter, iter_difference_t< Iterator > n=1)
获取迭代器的后一个位置
constexpr int sign(const T &value) noexcept
获取数值的符号
constexpr decimal_t remainder(const decimal_t x, const decimal_t y) noexcept
计算余数
constexpr bool is_negative(const T x) noexcept
检查浮点数是否为负数
constexpr Iterator2 move(Iterator1 first, Iterator1 last, Iterator2 result) noexcept(noexcept(inner::__move_aux(first, last, result)))
移动范围元素
constexpr string to_string_general(T x, int precision=6)
将浮点数转换为字符串(通用格式)
constexpr string to_string_scientific(T x, int precision=6)
将浮点数转换为字符串(科学计数法格式)
constexpr string to_string_fixed(T x, int precision=6)
将浮点数转换为字符串(固定小数格式)
basic_string< char > string
字符字符串
constexpr string address_string(const void *p)
将指针转换为十六进制地址字符串
basic_string_view< char > string_view
字符字符串视图
typename conditional< Test, T1, T2 >::type conditional_t
conditional的便捷别名
typename enable_if< Test, T >::type enable_if_t
enable_if的便捷别名
格式选项
bool space_sign
是否空格占位符号 ' '
format_align align
对齐方式
int precision
精度(-1表示默认)
bool show_sign
是否强制显示符号 '+'
bool uppercase
是否大写
char fill
填充字符
bool zero_pad
是否零填充
format_type type
类型
bool alternate
是否备用格式(# 前缀)
int width
最小宽度(0表示不限制)
constexpr string operator()(const T &value, const format_options &options) const
格式化浮点数
constexpr string operator()(const T value, const format_options &options) const
格式化有符号整数
constexpr string operator()(const T value, const format_options &options) const
格式化无符号整数
格式化器主模板
变量处理异常
类型到字符串的转换函数