1#ifndef NEFORCE_CORE_STRING_FORMAT_HPP__
2#define NEFORCE_CORE_STRING_FORMAT_HPP__
13NEFORCE_BEGIN_NAMESPACE__
71NEFORCE_CONSTEXPR20
string uint_to_string_base(
uint64_t value,
const int base,
const bool uppercase) {
79 constexpr auto digits_lower =
"0123456789abcdef";
80 constexpr auto digits_upper =
"0123456789ABCDEF";
81 const auto* digits = uppercase ? digits_upper : digits_lower;
93constexpr format_align to_number_alignment(
const char c) {
108constexpr format_type to_number_type(
const char c) {
140 if (fmt_str.
empty()) {
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];
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);
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);
167 if (pos < fmt_str.
size()) {
168 const char c = fmt_str[pos];
172 }
else if (c ==
' ') {
175 }
else if (c ==
'-') {
177 if (pos < fmt_str.
size()) {
178 const char next = fmt_str[pos];
180 options.
align = to_number_alignment(
next);
187 if (pos < fmt_str.
size() && fmt_str[pos] ==
'#') {
200 while (pos < fmt_str.
size() &&
is_digit(fmt_str[pos])) {
201 width = width * 10 + (fmt_str[pos] -
'0');
204 options.
width = width;
207 if (pos < fmt_str.
size() && fmt_str[pos] ==
'.') {
210 while (pos < fmt_str.
size() &&
is_digit(fmt_str[pos])) {
211 precision = precision * 10 + (fmt_str[pos] -
'0');
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') {
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] ==
' ')) {
236 existing_sign =
sign;
241 switch (options.
type) {
243 prefix = options.
uppercase ?
"0X" :
"0x";
247 prefix = options.
uppercase ?
"0B" :
"0b";
251 if (raw.
empty() || raw[0] !=
'0') {
263 if (existing_sign ==
'-') {
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;
274 const size_t pad_total = (content_len < target_width) ? target_width - content_len : 0;
286 const char fill_char = options.
fill;
288 result.
reserve(target_width > 0 ? target_width : content_len);
291 for (
size_t i = 0; i < pad_total; ++i) {
298 const char fill_char = options.
fill;
304 right_pad =
string(pad_total, fill_char);
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);
316 left_pad =
string(pad_total, fill_char);
322 result.
reserve(target_width > 0 ? target_width : content_len);
331template <
typename T,
bool Signed>
332struct integer_formatter_impl {
333 NEFORCE_CONSTEXPR20
string operator()(
const T value,
const format_options& options)
const {
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);
342 switch (options.
type) {
343 case format_type::BINARY: {
344 raw = inner::uint_to_string_base(compatible, 2, options.uppercase);
348 raw = inner::uint_to_string_base(compatible, 8, options.uppercase);
352 raw = inner::uint_to_string_base(compatible, 16, options.uppercase);
356 return inner::apply_format_options(string(1, static_cast<char>(value)), options, false);
361 raw = inner::__int_to_string_dispatch(value);
362 return inner::apply_format_options(_NEFORCE move(raw), options, true);
369 return inner::apply_format_options(_NEFORCE
move(raw), options,
true);
384template <
typename Number,
typename Dummy =
void>
403 switch (options.
type) {
430 return inner::apply_format_options(_NEFORCE
move(raw), options,
true);
447 return inner::integer_formatter_impl<T, true>{}(value, options);
465 return inner::integer_formatter_impl<T, false>{}(value, options);
474 NEFORCE_CONSTEXPR20
string operator()(
const char value,
const format_options& options)
const {
475 switch (options.
type) {
480 return inner::integer_formatter_impl<int, true>{}(
static_cast<int>(value), options);
486 return inner::apply_format_options(
string(1, value), options,
false);
492 NEFORCE_CONSTEXPR20
string operator()(
const T value,
const format_options& options)
const {
502 NEFORCE_CONSTEXPR20
string operator()(
const bool value,
const format_options& options)
const {
503 switch (options.
type) {
508 return inner::integer_formatter_impl<int, false>{}(
static_cast<int>(value), options);
514 return inner::apply_format_options(value ?
"true" :
"false", options,
false);
523 NEFORCE_CONSTEXPR20
string operator()(
const string& value,
const format_options& options)
const {
528 return inner::apply_format_options(_NEFORCE
move(raw), options,
false);
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);
561 return inner::apply_format_options(
"nullptr", options,
false);
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);
580 NEFORCE_CONSTEXPR20
string operator()(
char* value,
const format_options& options)
const {
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] ==
'{') {
605 }
else if (fmt[pos] ==
'}') {
606 if (pos + 1 < fmt.
size() && fmt[pos + 1] ==
'}') {
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] ==
'{') {
640 size_t end_pos = pos;
642 while (end_pos < fmt.
size() && depth > 0) {
643 if (fmt[end_pos] ==
'{') {
645 }
else if (fmt[end_pos] ==
'}') {
653 NEFORCE_THROW_EXCEPTION(
value_exception(
"Unmatched '{' in format string"));
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));
668 inner::format_impl(fmt, pos, out, _NEFORCE
forward<Rest>(rest)...);
670 }
else if (fmt[pos] ==
'}') {
671 if (pos + 1 < fmt.
size() && fmt[pos + 1] ==
'}') {
675 NEFORCE_THROW_EXCEPTION(
value_exception(
"Unmatched '}' in format string"));
694template <
typename... Args,
enable_if_t<(
sizeof...(Args) > 0),
int> = 0>
699 inner::format_impl(fmt, pos, result, _NEFORCE
forward<Args>(args)...);
705NEFORCE_END_NAMESPACE__
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
空指针类型
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的便捷别名