-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkformat.hpp
139 lines (120 loc) · 3.77 KB
/
kformat.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// KTL single-header library
// Requirements: C++20
#pragma once
#include <cassert>
#include <cstdio>
#include <cstring>
#include <string>
namespace ktl {
///
/// \brief Obtain formatted string
///
template <typename... Args>
[[nodiscard]] std::string kformat(std::string_view const fmt, Args const&... args);
///
/// \brief Interpolate arguments to output string
///
template <typename... Args>
void kformat_to(std::string& out, std::string_view const fmt, Args const&... args);
///
/// \brief Customization point
///
template <typename T>
struct kformatter {
void operator()(std::string& out, std::string_view fmt, T const& arg) const;
};
// impl
namespace detail {
struct format_arg {
using do_format_t = void (*)(std::string& out, std::string_view fmt, void const* arg);
do_format_t do_format{&null_format};
void const* arg{};
static constexpr void null_format(std::string&, std::string_view, void const*) {}
template <typename T>
static constexpr do_format_t make_do_format() {
return [](std::string& out, std::string_view fmt, void const* arg) { kformatter<T>{}(out, fmt, *static_cast<T const*>(arg)); };
}
template <typename T>
static constexpr format_arg make(T const& t) {
return {make_do_format<T>(), &t};
}
};
template <std::size_t Capacity>
struct format_args {
format_arg args[Capacity]{};
std::size_t size{};
std::size_t index{};
template <typename... Args>
static constexpr format_args make(Args const&... args) {
static_assert(sizeof...(Args) <= Capacity);
return format_args{{format_arg::make(args)...}, sizeof...(Args)};
}
constexpr format_arg next() {
assert(index < Capacity);
return args[index++];
}
};
template <typename T>
std::string kformat_to_string(T const& t) {
if constexpr (std::convertible_to<T, std::string_view>) {
return std::string(t);
} else if constexpr (std::same_as<T, char>) {
return std::string(1, static_cast<char>(t));
} else if constexpr (std::is_pointer_v<T>) {
return std::to_string(reinterpret_cast<std::size_t>(t));
} else {
using std::to_string;
return to_string(t);
}
}
struct kfmt {
template <std::size_t Size>
void operator()(std::string& out, std::string_view fmt, format_args<Size> args) const {
out.reserve(out.size() + fmt.size() + args.size * 8);
auto lbrace = fmt.find('{');
while (lbrace != std::string_view::npos) {
auto const rbrace = fmt.find('}', lbrace);
auto const argfmt = fmt.substr(lbrace + 1, rbrace - lbrace - 1);
auto const arg = args.next();
out += fmt.substr(0, lbrace);
arg.do_format(out, argfmt, arg.arg);
fmt = fmt.substr(rbrace + 1);
lbrace = fmt.find('{');
}
if (!fmt.empty()) { out += std::string(fmt); }
}
static constexpr std::size_t size(std::size_t count) {
constexpr std::size_t min_args_v{16};
return count < min_args_v ? min_args_v : count;
}
};
} // namespace detail
template <typename T>
void kformatter<T>::operator()(std::string& out, std::string_view fmt, T const& arg) const {
if constexpr (std::integral<T> || std::floating_point<T> || std::is_pointer_v<T>) {
if (fmt.empty() || fmt.size() + 1 >= 16) {
out += detail::kformat_to_string(arg);
} else if (fmt[0] == ':') {
fmt = fmt.substr(1);
char szfmt[16]{};
char szbuf[64]{};
szfmt[0] = '%';
std::memcpy(szfmt + 1, fmt.data(), fmt.size());
std::snprintf(szbuf, sizeof(szbuf), szfmt, arg);
out += szbuf;
}
} else {
out += detail::kformat_to_string(arg);
}
}
} // namespace ktl
template <typename... Args>
void ktl::kformat_to(std::string& out, std::string_view const fmt, Args const&... args) {
detail::kfmt{}(out, fmt, detail::format_args<detail::kfmt::size(sizeof...(Args))>::make(args...));
}
template <typename... Args>
std::string ktl::kformat(std::string_view const fmt, Args const&... args) {
auto ret = std::string{};
kformat_to(ret, fmt, args...);
return ret;
}