Add a simple format string compilation API

This commit is contained in:
Victor Zverovich 2020-06-12 07:05:49 -07:00
parent d259fcfb05
commit 2d71d7e030
3 changed files with 45 additions and 37 deletions

View File

@ -15,6 +15,17 @@
FMT_BEGIN_NAMESPACE
namespace detail {
// A compile-time string which is compiled into fast formatting code.
class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string)
template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) { return value; }
// Part of a compiled format string. It can be either literal text or a
// replacement field.
template <typename Char> struct format_part {
@ -381,31 +392,6 @@ constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
return {{&s[pos], size}};
}
template <typename Char, typename OutputIt, typename T,
std::enable_if_t<std::is_integral_v<T>, int> = 0>
OutputIt format_default(OutputIt out, T value) {
// TODO: reserve
format_int fi(value);
return std::copy(fi.data(), fi.data() + fi.size(), out);
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, double value) {
return detail::write(out, value);
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, Char value) {
*out++ = value;
return out;
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, const Char* value) {
auto length = std::char_traits<Char>::length(value);
return copy_str<Char>(value, value + length, out);
}
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
using char_type = Char;
@ -414,7 +400,7 @@ template <typename Char, typename T, int N> struct field {
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
return format_default<Char>(out, arg);
return write<Char>(out, arg);
}
};
@ -456,7 +442,8 @@ constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS != to_string_view(format_str).size()) {
if constexpr (POS !=
basic_string_view<typename S::char_type>(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
@ -506,7 +493,8 @@ constexpr auto compile_format_string(S format_str) {
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
FMT_ENABLE_IF(is_compile_string<S>::value ||
detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = format_str;
if constexpr (str.size() == 0) {
@ -529,7 +517,8 @@ template <typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
cf.format(std::back_inserter(buffer), args...);
detail::buffer<Char>& base = buffer;
cf.format(std::back_inserter(base), args...);
return to_string(buffer);
}
@ -569,6 +558,16 @@ std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
return to_string(buffer);
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(S, Args&&... args) {
constexpr basic_string_view<typename S::char_type> str = S();
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
return fmt::to_string(detail::first(args...));
constexpr auto compiled = compile<Args...>(S());
return format(compiled, std::forward<Args...>(args...));
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>

View File

@ -481,7 +481,7 @@ inline basic_string_view<Char> to_string_view(detail::std_string_view<Char> s) {
}
// A base class for compile-time strings. It is defined in the fmt namespace to
// make formatting functions visible via ADL, e.g. format(fmt("{}"), 42).
// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42).
struct compile_string {};
template <typename S>
@ -1745,8 +1745,8 @@ template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
FMT_INLINE void check_format_string(const S&) {
#ifdef FMT_ENFORCE_COMPILE_STRING
static_assert(is_compile_string<S>::value,
"FMT_ENFORCE_COMPILE_STRING requires all format strings to "
"utilize FMT_STRING() or fmt().");
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
"FMT_STRING.");
#endif
}
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>

View File

@ -700,6 +700,10 @@ void basic_memory_buffer<T, SIZE, Allocator>::grow(size_t size) {
using memory_buffer = basic_memory_buffer<char>;
using wmemory_buffer = basic_memory_buffer<wchar_t>;
template <typename T, size_t SIZE, typename Allocator>
struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {
};
/** A formatting error such as invalid format string. */
FMT_CLASS_API
class FMT_API format_error : public std::runtime_error {
@ -2746,12 +2750,12 @@ FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
return {s.data(), s.size()};
}
#define FMT_STRING_IMPL(s, ...) \
#define FMT_STRING_IMPL(s, base) \
[] { \
/* Use a macro-like name to avoid shadowing warnings. */ \
struct FMT_COMPILE_STRING : fmt::compile_string { \
struct FMT_COMPILE_STRING : base { \
using char_type = fmt::remove_cvref_t<decltype(s[0])>; \
FMT_MAYBE_UNUSED __VA_ARGS__ FMT_CONSTEXPR \
FMT_MAYBE_UNUSED FMT_CONSTEXPR \
operator fmt::basic_string_view<char_type>() const { \
return fmt::detail::compile_string_to_view<char_type>(s); \
} \
@ -2769,7 +2773,7 @@ FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
std::string s = format(FMT_STRING("{:d}"), "foo");
\endrst
*/
#define FMT_STRING(s) FMT_STRING_IMPL(s, )
#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string)
template <typename... Args, typename S,
enable_if_t<(is_compile_string<S>::value), int>>
@ -3344,9 +3348,14 @@ join(const Range& range, wstring_view sep) {
std::string answer = fmt::to_string(42);
\endrst
*/
template <typename T> inline std::string to_string(const T& value) {
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
inline std::string to_string(const T& value) {
return format("{}", value);
}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline std::string to_string(T value) {
return format_int(value).str();
}
/**
Converts *value* to ``std::wstring`` using the default format for type *T*.