1#ifndef LIBFILEZILLA_FORMAT_HEADER
2#define LIBFILEZILLA_FORMAT_HEADER
10#ifdef LFZ_FORMAT_DEBUG
12#define format_assert(pred) assert((pred))
14#define format_assert(pred)
41 explicit operator bool()
const {
return type != 0; }
45bool is_negative([[maybe_unused]] Arg && v)
47 if constexpr (std::is_signed_v<std::decay_t<Arg>>) {
57template<
typename String,
bool Un
signed,
typename Arg>
58typename std::enable_if_t<std::is_integral_v<std::decay_t<Arg>> && !std::is_enum_v<std::decay_t<Arg>>, String> integral_to_string(field
const& f, Arg && arg)
60 std::decay_t<Arg> v = arg;
64 format_assert(!Unsigned || !std::is_signed_v<std::decay_t<Arg>> || arg >= 0);
66 if (is_negative(arg)) {
69 else if (f.flags & always_sign) {
72 else if (f.flags & pad_blank) {
77 typename String::value_type buf[
sizeof(v) * 4 + 1];
78 auto *
const end = buf +
sizeof(v) * 4 + 1;
82 int const mod = std::abs(
static_cast<int>(v % 10));
88 if (f.flags & with_width) {
89 if (lead && width > 0) {
95 if (f.flags & pad_0) {
99 if (
static_cast<size_t>(end - p) < width) {
100 ret.append(width - (end - p),
'0');
105 if (
static_cast<size_t>(end - p) < width && !(f.flags & left_align)) {
106 ret.append(width - (end - p),
' ');
112 if (
static_cast<size_t>(end - p) < width && f.flags & left_align) {
113 ret.append(width - (end - p),
' ');
123 return String(p, end);
128template<
typename String,
bool Un
signed,
typename Arg>
129typename std::enable_if_t<std::is_enum_v<std::decay_t<Arg>>, String> integral_to_string(field
const& f, Arg && arg)
131 return integral_to_string<String, Unsigned>(f,
static_cast<std::underlying_type_t<std::decay_t<Arg>
>>(arg));
135template<
typename String,
bool Un
signed,
typename Arg>
136typename std::enable_if_t<!std::is_integral_v<std::decay_t<Arg>> && !std::is_enum_v<std::decay_t<Arg>>, String> integral_to_string(field
const&, Arg &&)
142template<
typename String,
class Arg,
typename =
void>
143struct has_toString : std::false_type {};
145template<
typename String,
class Arg>
146struct has_toString<String, Arg, std::void_t<decltype(toString<String>(std::declval<Arg>()))>> : std::true_type {};
154 template <
typename String>
155 static constexpr bool is_formattable_as = std::disjunction<
156 std::is_enum<std::decay_t<Arg>>,
157 std::is_arithmetic<std::decay_t<Arg>>,
158 std::is_pointer<std::decay_t<Arg>>,
159 std::is_same<String, std::decay_t<Arg>>,
160 has_toString<String, Arg>
166template<
typename String,
bool Lowercase,
typename Arg>
167String integral_to_hex_string(Arg && arg)
noexcept
169 if constexpr (std::is_enum_v<std::decay_t<Arg>>) {
171 return integral_to_hex_string<String, Lowercase>(
static_cast<std::underlying_type_t<std::decay_t<Arg>
>>(arg));
173 else if constexpr (std::is_signed_v<std::decay_t<Arg>>) {
174 return integral_to_hex_string<String, Lowercase>(
static_cast<std::make_unsigned_t<std::decay_t<Arg>
>>(arg));
176 else if constexpr (std::is_integral_v<std::decay_t<Arg>>) {
177 std::decay_t<Arg> v = arg;
178 typename String::value_type buf[
sizeof(v) * 2];
179 auto*
const end = buf +
sizeof(v) * 2;
187 return String(p, end);
196template<
typename String,
typename Arg>
197String integral_to_octal_string(Arg && arg)
noexcept
199 if constexpr (std::is_enum_v<std::decay_t<Arg>>) {
201 return integral_to_octal_string<String>(
static_cast<std::underlying_type_t<std::decay_t<Arg>
>>(arg));
203 else if constexpr (std::is_signed_v<std::decay_t<Arg>>) {
204 return integral_to_octal_string<String>(
static_cast<std::make_unsigned_t<std::decay_t<Arg>
>>(arg));
206 else if constexpr (std::is_integral_v<std::decay_t<Arg>>) {
207 std::decay_t<Arg> v = arg;
208 typename String::value_type buf[
sizeof(v) * 3];
209 auto*
const end = buf +
sizeof(v) * 3;
213 *(--p) = (v & 07) +
'0';
217 return String(p, end);
226template<
typename String,
typename Arg>
227String pointer_to_string(Arg&& arg)
noexcept
229 if constexpr (std::is_pointer_v<std::decay_t<Arg>>) {
230 return String({
'0',
'x'}) + integral_to_hex_string<String, true>(
reinterpret_cast<uintptr_t
>(arg));
238template<
typename String,
typename Arg>
239String char_to_string(Arg&& arg)
241 if constexpr (std::is_integral_v<std::decay_t<Arg>>) {
242 return String({
static_cast<typename String::value_type
>(
static_cast<unsigned char>(arg))});
251template<
typename String>
252void pad_arg(String& s, field
const& f)
254 if (f.flags & with_width && s.size() < f.width) {
255 if (f.flags & left_align) {
256 s += String(f.width - s.size(),
' ');
259 s = String(f.width - s.size(), (f.flags & pad_0) ?
'0' :
' ') + s;
264template<
typename String,
typename Arg>
265String format_arg(field
const& f, Arg&& arg)
269 if constexpr (std::is_same_v<String, std::decay_t<Arg>>) {
272 else if constexpr (has_toString<String, Arg>::value) {
283 else if (f.type ==
'd' || f.type ==
'i') {
284 ret = integral_to_string<String, false>(f, std::forward<Arg>(arg));
286 else if (f.type ==
'u') {
287 ret = integral_to_string<String, true>(f, std::forward<Arg>(arg));
289 else if (f.type ==
'x') {
290 ret = integral_to_hex_string<String, true>(std::forward<Arg>(arg));
293 else if (f.type ==
'X') {
294 ret = integral_to_hex_string<String, false>(std::forward<Arg>(arg));
297 else if (f.type ==
'o') {
298 ret = integral_to_octal_string<String>(std::forward<Arg>(arg));
301 else if (f.type ==
'p') {
302 ret = pointer_to_string<String>(std::forward<Arg>(arg));
305 else if (f.type ==
'c') {
306 ret = char_to_string<String>(std::forward<Arg>(arg));
314template<
typename String,
typename... Args>
315String extract_arg(field
const&,
size_t)
321template<
typename String,
typename Arg,
typename... Args>
322String extract_arg(field
const& f,
size_t arg_n, Arg&& arg, Args&&...args)
327 ret = format_arg<String>(f, std::forward<Arg>(arg));
330 ret = extract_arg<String>(f, arg_n - 1, std::forward<Args>(args)...);
336template<
typename InString,
typename OutString>
337field get_field(InString
const& fmt,
typename InString::size_type & pos,
size_t& arg_n, OutString & ret)
340 if (++pos >= fmt.size()) {
346 if (fmt[pos] ==
'%') {
354 if (fmt[pos] ==
'0') {
357 else if (fmt[pos] ==
' ') {
358 f.flags |= pad_blank;
360 else if (fmt[pos] ==
'-') {
362 f.flags |= left_align;
364 else if (fmt[pos] ==
'+') {
365 f.flags &= ~pad_blank;
366 f.flags |= always_sign;
368 else if (fmt[pos] ==
'\'') {
369 f.flags |= thousands;
374 if (++pos >= fmt.size()) {
381 while (fmt[pos] >=
'0' && fmt[pos] <=
'9') {
382 f.flags |= with_width;
384 f.width += fmt[pos] -
'0';
385 if (++pos >= fmt.size()) {
390 if (f.width > 10000) {
395 if (fmt[pos] ==
'$') {
398 if (++pos >= fmt.size()) {
408 if (c ==
'h' || c ==
'l' || c ==
'L' || c ==
'j' || c ==
'z' || c ==
't') {
409 if (++pos >= fmt.size()) {
419 f.type =
static_cast<char>(fmt[pos++]);
423template<
typename String,
typename Arg,
int N>
424constexpr bool check_argument()
427 argument<N>::template of_type<Arg>::template is_formattable_as<String>,
428 "Argument cannot be formatted by fz::sprintf()"
431 return argument<N>::template of_type<Arg>::template is_formattable_as<String>;
434template<
typename String,
typename... Args, std::size_t... Is>
435constexpr bool check_arguments(std::index_sequence<Is...>)
437 return (check_argument<String, Args, Is>() && ...);
440template<
typename InString,
typename CharType =
typename InString::value_type,
typename OutString = std::basic_
string<CharType>,
typename... Args>
441OutString do_sprintf(InString
const& fmt, Args&&... args)
446 typename InString::size_type start = 0, pos;
449 while ((pos = fmt.find(
'%', start)) != InString::npos) {
452 ret += fmt.substr(start, pos - start);
454 field f = detail::get_field(fmt, pos, arg_n, ret);
456 format_assert(arg_n <
sizeof...(args));
457 ret += detail::extract_arg<OutString>(f, arg_n++, std::forward<Args>(args)...);
464 ret += fmt.substr(start);
493template<
typename... Args>
494std::string
sprintf(std::string_view
const& fmt, Args&&... args)
496 detail::check_arguments<std::string, Args...>(std::index_sequence_for<Args...>());
498 return detail::do_sprintf(fmt, std::forward<Args>(args)...);
501template<
typename... Args>
502std::wstring
sprintf(std::wstring_view
const& fmt, Args&&... args)
504 detail::check_arguments<std::wstring, Args...>(std::index_sequence_for<Args...>());
506 return detail::do_sprintf(fmt, std::forward<Args>(args)...);
Functions to encode/decode strings.
type
Definition logger.hpp:16
The namespace used by libfilezilla.
Definition apply.hpp:17
auto toString(Arg &&arg) -> typename std::enable_if< std::is_same_v< String, std::string >, decltype(to_string(std::forward< Arg >(arg)))>::type
Calls either fz::to_string or fz::to_wstring depending on the passed template argument.
Definition string.hpp:307
Char int_to_hex_char(int d)
Converts an integer to the corresponding lowercase hex digit.
Definition encode.hpp:78
std::string sprintf(std::string_view const &fmt, Args &&... args)
A simple type-safe sprintf replacement.
Definition format.hpp:494
String types and assorted functions.