Better microfmt implementation

This commit is contained in:
Jeremy Rifkin 2024-12-03 23:53:28 -06:00
parent ddec65195d
commit 4227fc4abe
No known key found for this signature in database
GPG Key ID: 19AA8270105E8EB4

View File

@ -50,19 +50,18 @@ namespace microfmt {
char base = 'd'; char base = 'd';
}; };
template<typename It> void do_write(std::string& out, It begin, It end, const format_options& options) { template<typename OutputIt, typename InputIt>
void do_write(OutputIt out, InputIt begin, InputIt end, const format_options& options) {
auto size = end - begin; auto size = end - begin;
if(static_cast<std::size_t>(size) >= options.width) { if(static_cast<std::size_t>(size) >= options.width) {
out.append(begin, end); std::copy(begin, end, out);
} else { } else {
auto out_size = out.size();
out.resize(out_size + options.width);
if(options.align == alignment::left) { if(options.align == alignment::left) {
std::copy(begin, end, out.begin() + out_size); std::copy(begin, end, out);
std::fill(out.begin() + out_size + size, out.end(), options.fill); std::fill_n(out, options.width - size, options.fill);
} else { } else {
std::fill(out.begin() + out_size, out.begin() + out_size + (options.width - size), options.fill); std::fill_n(out, options.width - size, options.fill);
std::copy(begin, end, out.begin() + out_size + (options.width - size)); std::copy(begin, end, out);
} }
} }
} }
@ -99,15 +98,18 @@ namespace microfmt {
} }
} }
struct string_view {
const char* data;
std::size_t size;
};
class format_value { class format_value {
enum class value_type { enum class value_type {
char_value, char_value,
int64_value, int64_value,
uint64_value, uint64_value,
string_value, string_value,
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
string_view_value, string_view_value,
#endif
c_string_value, c_string_value,
}; };
union { union {
@ -115,9 +117,7 @@ namespace microfmt {
std::int64_t int64_value; std::int64_t int64_value;
std::uint64_t uint64_value; std::uint64_t uint64_value;
const std::string* string_value; const std::string* string_value;
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L) string_view string_view_value;
std::string_view string_view_value;
#endif
const char* c_string_value; const char* c_string_value;
}; };
value_type value; value_type value;
@ -135,7 +135,8 @@ namespace microfmt {
format_value(unsigned long long int_val) : uint64_value(int_val), value(value_type::uint64_value) {} format_value(unsigned long long int_val) : uint64_value(int_val), value(value_type::uint64_value) {}
format_value(const std::string& string) : string_value(&string), value(value_type::string_value) {} format_value(const std::string& string) : string_value(&string), value(value_type::string_value) {}
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L) #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
format_value(std::string_view sv) : string_view_value(sv), value(value_type::string_view_value) {} format_value(std::string_view sv)
: string_view_value{sv.data(), sv.size()}, value(value_type::string_view_value) {}
#endif #endif
format_value(const char* c_string) : c_string_value(c_string), value(value_type::c_string_value) {} format_value(const char* c_string) : c_string_value(c_string), value(value_type::c_string_value) {}
@ -148,7 +149,8 @@ namespace microfmt {
} }
public: public:
void write(std::string& out, const format_options& options) const { template<typename OutputIt>
void write(OutputIt out, const format_options& options) const {
switch(value) { switch(value) {
case value_type::char_value: case value_type::char_value:
do_write(out, &char_value, &char_value + 1, options); do_write(out, &char_value, &char_value + 1, options);
@ -174,11 +176,9 @@ namespace microfmt {
case value_type::string_value: case value_type::string_value:
do_write(out, string_value->begin(), string_value->end(), options); do_write(out, string_value->begin(), string_value->end(), options);
break; break;
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
case value_type::string_view_value: case value_type::string_view_value:
do_write(out, string_view_value.begin(), string_view_value.end(), options); do_write(out, string_view_value.data, string_view_value.data + string_view_value.size, options);
break; break;
#endif
case value_type::c_string_value: case value_type::c_string_value:
do_write(out, c_string_value, c_string_value + std::strlen(c_string_value), options); do_write(out, c_string_value, c_string_value + std::strlen(c_string_value), options);
break; break;
@ -186,9 +186,10 @@ namespace microfmt {
} }
}; };
template<std::size_t N, typename It> // note: previously used std::array and there was a bug with std::array<T, 0> affecting old msvc
std::string format(It fmt_begin, It fmt_end, std::array<format_value, N> args) { // https://godbolt.org/z/88T8hrzzq mre: https://godbolt.org/z/drd8echbP
std::string str; template<typename OutputIt, typename InputIt>
void format(OutputIt out, InputIt fmt_begin, InputIt fmt_end, const std::initializer_list<format_value>& args) {
std::size_t arg_i = 0; std::size_t arg_i = 0;
auto it = fmt_begin; auto it = fmt_begin;
auto peek = [&] (std::size_t dist) -> char { // 0 on failure auto peek = [&] (std::size_t dist) -> char { // 0 on failure
@ -230,7 +231,7 @@ namespace microfmt {
return false; return false;
} }
it += 2; it += 2;
options.width = arg_i < args.size() ? args[arg_i++].unwrap_int() : 0; options.width = arg_i < args.size() ? args.begin()[arg_i++].unwrap_int() : 0;
} }
// try to parse fill/base // try to parse fill/base
if(it != fmt_end && *it == ':') { if(it != fmt_end && *it == ':') {
@ -250,7 +251,7 @@ namespace microfmt {
return false; return false;
} }
if(arg_i < args.size()) { if(arg_i < args.size()) {
args[arg_i++].write(str, options); args.begin()[arg_i++].write(out, options);
} }
return true; return true;
}; };
@ -259,43 +260,40 @@ namespace microfmt {
} }
it = saved_it; // go back it = saved_it; // go back
} }
str += *it; *out++ = *it;
} }
return str; }
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
template<typename OutputIt>
void format(OutputIt out, std::string_view fmt, const std::initializer_list<format_value>& args) {
return format(out, fmt.begin(), fmt.end(), args);
}
#endif
template<typename OutputIt>
void format(OutputIt out, const char* fmt, const std::initializer_list<format_value>& args) {
return format(out, fmt, fmt + std::strlen(fmt), args);
} }
std::ostream& get_cout(); std::ostream& get_cout();
} }
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L) template<typename S, typename... Args>
template<typename... Args> std::string format(const S& fmt, Args&&... args) {
std::string format(std::string_view fmt, Args&&... args) { std::string str;
return detail::format<sizeof...(args)>(fmt.begin(), fmt.end(), {detail::format_value(args)...}); detail::format(std::back_inserter(str), fmt, {detail::format_value(args)...});
} return str;
// working around an old msvc bug https://godbolt.org/z/88T8hrzzq mre: https://godbolt.org/z/drd8echbP
inline std::string format(std::string_view fmt) {
return detail::format<1>(fmt.begin(), fmt.end(), {detail::format_value(1)});
}
#endif
template<typename... Args>
std::string format(const char* fmt, Args&&... args) {
return detail::format<sizeof...(args)>(fmt, fmt + std::strlen(fmt), {detail::format_value(args)...});
}
inline std::string format(const char* fmt) {
return detail::format<1>(fmt, fmt + std::strlen(fmt), {detail::format_value(1)});
} }
template<typename S, typename... Args> template<typename S, typename... Args>
void print(const S& fmt, Args&&... args) { void print(const S& fmt, Args&&... args) {
detail::get_cout()<<format(fmt, args...); detail::format(std::ostream_iterator<char>(detail::get_cout()), fmt, {args...});
} }
template<typename S, typename... Args> template<typename S, typename... Args>
void print(std::ostream& ostream, const S& fmt, Args&&... args) { void print(std::ostream& ostream, const S& fmt, Args&&... args) {
ostream<<format(fmt, args...); detail::format(std::ostream_iterator<char>(ostream), fmt, {args...});
} }
template<typename S, typename... Args> template<typename S, typename... Args>