1#ifndef MSTL_CORE_STRING_FORMAT_HPP__
2#define MSTL_CORE_STRING_FORMAT_HPP__
3#include "to_string.hpp"
6enum class FORMAT_ALIGN {
13enum class FORMAT_TYPE {
24struct MSTL_API format_options {
28 FORMAT_ALIGN alignment = FORMAT_ALIGN::RIGHT;
30 bool show_base =
false;
31 FORMAT_TYPE type = FORMAT_TYPE::DECIMAL;
32 bool zero_pad =
false;
38constexpr FORMAT_ALIGN to_number_alignment(
const char c) {
40 case '<':
return FORMAT_ALIGN::LEFT;
41 case '>':
return FORMAT_ALIGN::RIGHT;
42 case '^':
return FORMAT_ALIGN::CENTER;
43 case '=':
return FORMAT_ALIGN::INTERNAL;
44 default:
return FORMAT_ALIGN::RIGHT;
48constexpr FORMAT_TYPE to_number_type(
const char c) {
50 case 'b':
return FORMAT_TYPE::BINARY;
51 case 'o':
return FORMAT_TYPE::OCTAL;
52 case 'd':
return FORMAT_TYPE::DECIMAL;
53 case 'x':
return FORMAT_TYPE::HEX_LOW;
54 case 'X':
return FORMAT_TYPE::HEX_UP;
55 case 'f':
return FORMAT_TYPE::FLOAT_FIX;
56 case 'e':
return FORMAT_TYPE::FLOAT_EXP;
57 case 'g':
return FORMAT_TYPE::FLOAT_GEN;
58 default:
return FORMAT_TYPE::DECIMAL;
62MSTL_CONSTEXPR20 format_options parse_number_format(
const string_view& fmt_str) {
63 format_options options;
66 if (fmt_str.empty())
return options;
68 bool found_align =
false;
69 if (pos + 1 < fmt_str.size()) {
70 const char second_char = fmt_str[pos + 1];
71 const char first_char = fmt_str[pos];
73 if ((second_char ==
'<' || second_char ==
'>' ||
74 second_char ==
'^' || second_char ==
'=') &&
75 (first_char !=
'+' && first_char !=
'-' && first_char !=
' ')) {
76 options.fill = first_char;
77 options.alignment = to_number_alignment(second_char);
83 if (!found_align && pos < fmt_str.size()) {
84 const char c = fmt_str[pos];
85 if (c ==
'<' || c ==
'>' || c ==
'^' || c ==
'=') {
86 options.alignment = to_number_alignment(c);
92 if (pos < fmt_str.size()) {
93 const char c = fmt_str[pos];
94 if (c ==
'+' || c ==
'-' || c ==
' ') {
95 options.sign_mode = c;
100 if (!found_align && pos < fmt_str.size()) {
101 const char c = fmt_str[pos];
102 if (c ==
'<' || c ==
'>' || c ==
'^' || c ==
'=') {
103 options.alignment = to_number_alignment(c);
108 if (pos < fmt_str.size() && fmt_str[pos] ==
'#') {
109 options.show_base =
true;
113 if (pos < fmt_str.size() && fmt_str[pos] ==
'0' &&
114 options.fill ==
' ' && options.alignment == FORMAT_ALIGN::RIGHT) {
115 options.zero_pad =
true;
117 options.alignment = FORMAT_ALIGN::INTERNAL;
123 while (pos < fmt_str.size() &&
_MSTL is_digit(fmt_str[pos])) {
124 width = width * 10 + (fmt_str[pos] -
'0');
127 options.width = width;
130 if (pos < fmt_str.size() && fmt_str[pos] ==
'.') {
133 while (pos < fmt_str.size() &&
_MSTL is_digit(fmt_str[pos])) {
134 precision = precision * 10 + (fmt_str[pos] -
'0');
137 options.precision = precision;
140 if (pos < fmt_str.size()) {
141 options.type = to_number_type(fmt_str[pos]);
151template <
typename Number,
typename =
void>
156struct formatter<T,
enable_if_t<is_floating_point_v<T>>> {
157 MSTL_CONSTEXPR20
string operator ()(
const T& value,
const format_options& options)
const {
158 double val =
static_cast<double>(value);
165 }
else if (options.sign_mode ==
'+') {
167 }
else if (options.sign_mode ==
' ') {
172 const int precision = options.precision >= 0 ? options.precision : 6;
174 switch (options.type) {
175 case FORMAT_TYPE::FLOAT_FIX: {
176 number_str =
_MSTL to_string_fixed(val, precision);
179 case FORMAT_TYPE::FLOAT_EXP: {
180 number_str =
_MSTL to_string_scientific(val, precision);
183 case FORMAT_TYPE::FLOAT_GEN:
185 number_str =
_MSTL to_string_general(val, precision);
190 if (!number_str.empty() && number_str[0] ==
'-') {
191 number_str = number_str.substr(1);
194 string result = prefix + number_str;
195 if (options.width <= 0 || result.size() >=
static_cast<size_t>(options.width)) {
199 const size_t fill_count = options.width - result.size();
200 const string fill_str(fill_count, options.fill);
202 switch (options.alignment) {
203 case FORMAT_ALIGN::LEFT:
204 return result + fill_str;
205 case FORMAT_ALIGN::RIGHT:
206 return fill_str + result;
207 case FORMAT_ALIGN::INTERNAL:
208 return prefix + fill_str + number_str;
209 case FORMAT_ALIGN::CENTER: {
210 const size_t left_fill = fill_count / 2;
211 const size_t right_fill = fill_count - left_fill;
212 return string(left_fill, options.fill) + result + string(right_fill, options.fill);
215 return fill_str + result;
221struct formatter<T,
enable_if_t<is_integral_v<T> && is_signed_v<T>>> {
222 MSTL_CONSTEXPR20
string operator ()(
const T& value,
const format_options& options)
const {
229 switch (options.type) {
230 case FORMAT_TYPE::BINARY: {
234 }
case FORMAT_TYPE::OCTAL: {
238 }
case FORMAT_TYPE::HEX_LOW: {
239 digits =
"0123456789abcdef";
242 }
case FORMAT_TYPE::HEX_UP: {
243 digits =
"0123456789ABCDEF";
246 }
case FORMAT_TYPE::DECIMAL:
default: {
247 digits =
"0123456789";
254 if (abs_value == 0) {
257 while (abs_value > 0) {
258 number_str = digits[abs_value % base] + number_str;
266 }
else if (options.sign_mode ==
'+') {
268 }
else if (options.sign_mode ==
' ') {
272 if (options.show_base && base != 10) {
273 switch (options.type) {
274 case FORMAT_TYPE::BINARY: prefix +=
"0b";
break;
275 case FORMAT_TYPE::OCTAL: prefix +=
"0o";
break;
276 case FORMAT_TYPE::HEX_LOW: prefix +=
"0x";
break;
277 case FORMAT_TYPE::HEX_UP: prefix +=
"0X";
break;
282 string result = prefix + number_str;
283 if (options.width <= 0 || result.size() >=
static_cast<size_t>(options.width)) {
287 const size_t fill_count = options.width - result.size();
288 const string fill_str(fill_count, options.fill);
290 switch (options.alignment) {
291 case FORMAT_ALIGN::LEFT:
292 return result + fill_str;
293 case FORMAT_ALIGN::RIGHT:
294 return fill_str + result;
295 case FORMAT_ALIGN::INTERNAL:
296 return prefix + fill_str + number_str;
297 case FORMAT_ALIGN::CENTER: {
298 const size_t left_fill = fill_count / 2;
299 const size_t right_fill = fill_count - left_fill;
300 return string(left_fill, options.fill) + result + string(right_fill, options.fill);
303 return fill_str + result;
309struct formatter<T,
enable_if_t<is_integral_v<T> && is_unsigned_v<T>>> {
310 MSTL_CONSTEXPR20
string operator ()(
const T& value,
const format_options& options)
const {
313 bool uppercase =
false;
315 switch (options.type) {
316 case FORMAT_TYPE::HEX_LOW: {
320 }
case FORMAT_TYPE::HEX_UP: {
324 }
case FORMAT_TYPE::OCTAL: {
327 }
case FORMAT_TYPE::BINARY: {
330 }
case FORMAT_TYPE::DECIMAL:
default: {
339 digits =
_INNER __uint_to_string_base(
static_cast<const unpackage_t<T>&
>(value), base, uppercase);
342 string base_prefix =
"";
343 if (options.show_base) {
346 base_prefix = uppercase ?
"0X" :
"0x";
352 base_prefix = uppercase ?
"0B" :
"0b";
358 string number_str = base_prefix + digits;
360 if (options.width <= 0 || number_str.size() >=
static_cast<size_t>(options.width)) {
364 const size_t fill_count = options.width - number_str.size();
366 switch (options.alignment) {
367 case FORMAT_ALIGN::LEFT:
368 return number_str + string(fill_count, options.fill);
369 case FORMAT_ALIGN::RIGHT:
370 return string(fill_count, options.fill) + number_str;
371 case FORMAT_ALIGN::INTERNAL: {
372 if (!base_prefix.empty()) {
373 return base_prefix + string(fill_count, options.fill) + digits;
375 return string(fill_count, options.fill) + number_str;
377 case FORMAT_ALIGN::CENTER: {
378 const size_t left_fill = fill_count / 2;
379 const size_t right_fill = fill_count - left_fill;
380 return string(left_fill, options.fill) + number_str + string(right_fill, options.fill);
383 return string(fill_count, options.fill) + number_str;
389struct formatter<string> {
390 MSTL_CONSTEXPR20
string operator ()(
const string& value,
const format_options& options)
const {
391 if (options.width <= 0 || value.size() >=
static_cast<size_t>(options.width)) {
395 const size_t fill_count = options.width - value.size();
396 const string fill_str(fill_count, options.fill);
398 switch (options.alignment) {
399 case FORMAT_ALIGN::LEFT:
400 return value + fill_str;
401 case FORMAT_ALIGN::RIGHT:
402 return fill_str + value;
403 case FORMAT_ALIGN::CENTER: {
404 const size_t left_fill = fill_count / 2;
405 const size_t right_fill = fill_count - left_fill;
406 return string(left_fill, options.fill) + value + string(right_fill, options.fill);
408 case FORMAT_ALIGN::INTERNAL:
410 return fill_str + value;
416struct formatter<const char*> {
417 MSTL_CONSTEXPR20
string operator ()(
const char* value,
const format_options& options)
const {
418 return formatter<string>()(string(value), options);
423struct formatter<string_view> {
424 MSTL_CONSTEXPR20
string operator ()(
const string_view value,
const format_options& options)
const {
425 return formatter<string>()(string(value), options);
430struct formatter<char*> {
431 MSTL_CONSTEXPR20
string operator ()(
char* value,
const format_options& options)
const {
432 return formatter<string>()(string(value), options);
437struct formatter<char> {
438 MSTL_CONSTEXPR20
string operator ()(
const char value,
const format_options& options)
const {
439 return formatter<string>()(string(1, value), options);
445MSTL_CONSTEXPR20
string format_impl(
const string_view fmt,
size_t& pos) {
447 while (pos < fmt.size()) {
448 if (fmt[pos] ==
'{') {
449 if (pos + 1 < fmt.size() && fmt[pos + 1] ==
'{') {
453 throw_exception(
value_exception(
"Not enough arguments for format string"));
455 }
else if (fmt[pos] ==
'}') {
456 if (pos + 1 < fmt.size() && fmt[pos + 1] ==
'}') {
470template <
typename First,
typename... Rest>
471MSTL_CONSTEXPR20
string format_impl(
const string_view fmt,
size_t& pos, First&& first, Rest&&... rest) {
473 while (pos < fmt.size()) {
474 if (fmt[pos] ==
'{') {
475 if (pos + 1 < fmt.size() && fmt[pos + 1] ==
'{') {
481 size_t end_pos = pos;
483 while (end_pos < fmt.size() && depth > 0) {
484 if (fmt[end_pos] ==
'{') {
486 }
else if (fmt[end_pos] ==
'}') {
489 if (depth > 0) ++end_pos;
495 const string_view spec_str = fmt.substr(pos, end_pos - pos);
498 if (spec_str.empty()) {
499 opts =
_INNER parse_number_format(
"");
500 }
else if (spec_str[0] ==
':') {
501 opts =
_INNER parse_number_format(spec_str.substr(1));
503 opts =
_INNER parse_number_format(spec_str);
508 }
else if (fmt[pos] ==
'}') {
509 if (pos + 1 < fmt.size() && fmt[pos + 1] ==
'}') {
525template <
typename... Args,
enable_if_t<(
sizeof...(Args) > 0),
int> = 0>
526MSTL_NODISCARD MSTL_CONSTEXPR20
string format(
const string_view fmt, Args&&... args) {
MSTL_NODISCARD constexpr T && forward(remove_reference_t< T > &x) noexcept
完美转发左值
typename unpackage< T >::type unpackage_t
unpackage的便捷别名
MSTL_CONST_FUNCTION MSTL_CONSTEXPR14 bool is_digit(const CharT c) noexcept
检查字符是否为数字
long long int64_t
64位有符号整数类型
unsigned long long uint64_t
64位无符号整数类型
#define _MSTL
全局命名空间MSTL前缀
#define MSTL_END_INNER__
结束inner命名空间
#define _INNER
inner命名空间前缀
#define MSTL_END_NAMESPACE__
结束全局命名空间MSTL
#define MSTL_BEGIN_NAMESPACE__
开始全局命名空间MSTL
#define MSTL_BEGIN_INNER__
开始inner命名空间
MSTL_CONST_FUNCTION constexpr bool is_negative(const T x) noexcept
检查浮点数是否为负数
constexpr void fill(Iterator first, Iterator last, const T &value)
填充范围元素
typename enable_if< Test, T >::type enable_if_t
enable_if的便捷别名