Adjust to PR feedback.
Moves implementation into 'std.h', and tests into 'std-test.cc'. Avoid fold-expression since MSVC was crashing. Hopefully works with this. Add secion for 'fmt/std.h' in API-docs.
This commit is contained in:
parent
093630eb5d
commit
594ec8e1c0
24
doc/api.rst
24
doc/api.rst
@ -12,7 +12,7 @@ The {fmt} library API consists of the following parts:
|
||||
formatting functions and locale support
|
||||
* :ref:`fmt/ranges.h <ranges-api>`: formatting of ranges and tuples
|
||||
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
|
||||
* :ref:`fmt/variant.h <variant-api>`: formatting of variants
|
||||
* :ref:`fmt/std.h <std-api>`: formatters for standard library types
|
||||
* :ref:`fmt/compile.h <compile-api>`: format string compilation
|
||||
* :ref:`fmt/color.h <color-api>`: terminal color and text style
|
||||
* :ref:`fmt/os.h <os-api>`: system APIs
|
||||
@ -183,7 +183,7 @@ Formatting User-defined Types
|
||||
The {fmt} library provides formatters for many standard C++ types.
|
||||
See :ref:`fmt/ranges.h <ranges-api>` for ranges and tuples including standard
|
||||
containers such as ``std::vector``, :ref:`fmt/chrono.h <chrono-api>` for date
|
||||
and time formatting and :ref:`fmt/variant.h <variant-api>` for variant
|
||||
and time formatting and :ref:`fmt/std.h <std-api>` for filesystem and variant
|
||||
formatting.
|
||||
|
||||
To make a user-defined type formattable, specialize the ``formatter<T>`` struct
|
||||
@ -449,16 +449,24 @@ The format syntax is described in :ref:`chrono-specs`.
|
||||
|
||||
.. doxygenfunction:: gmtime(std::time_t time)
|
||||
|
||||
.. _variant-api:
|
||||
.. _std-api:
|
||||
|
||||
Variant Formatting
|
||||
==================
|
||||
Standard Library Types Formatting
|
||||
=================================
|
||||
|
||||
``fmt/variant.h`` provides formatters for
|
||||
``fmt/std.h`` provides formatters for:
|
||||
|
||||
* `std::filesystem::path <std::filesystem::path>`_
|
||||
* `std::thread::id <https://en.cppreference.com/w/cpp/thread/thread/id>`_
|
||||
* `std::monostate <https://en.cppreference.com/w/cpp/utility/variant/monostate>`_
|
||||
* `std::variant <https://en.cppreference.com/w/cpp/utility/variant/variant>`_
|
||||
|
||||
Formatting Variants
|
||||
-------------------
|
||||
|
||||
A ``std::variant`` is only formattable if every variant alternative is formattable, and requires the
|
||||
``__cpp_lib_variant`` `library feature <https://en.cppreference.com/w/cpp/feature_test>`_.
|
||||
|
||||
**Example**::
|
||||
|
||||
#include <fmt/variant.h>
|
||||
@ -470,10 +478,6 @@ Variant Formatting
|
||||
std::variant<std::monostate, char> v1{};
|
||||
// Prints "< >"
|
||||
|
||||
.. note::
|
||||
|
||||
Variant support is only available for C++17 and up.
|
||||
|
||||
.. _compile-api:
|
||||
|
||||
Format string compilation
|
||||
|
||||
@ -9,6 +9,8 @@
|
||||
#define FMT_STD_H_
|
||||
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "ostream.h"
|
||||
|
||||
@ -68,4 +70,129 @@ template <typename Char>
|
||||
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#ifdef __cpp_lib_variant
|
||||
# include <variant>
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <typename Char> struct formatter<std::monostate, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext = format_context>
|
||||
auto format(const std::monostate&, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
*out++ = ' ';
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
using variant_index_sequence =
|
||||
std::make_index_sequence<std::variant_size<T>::value>;
|
||||
|
||||
// variant_size and variant_alternative check.
|
||||
template <typename T, typename U = void>
|
||||
struct is_variant_like_ : std::false_type {};
|
||||
template <typename T>
|
||||
struct is_variant_like_<T, std::void_t<decltype(std::variant_size<T>::value)>>
|
||||
: std::true_type {};
|
||||
|
||||
// formattable element check
|
||||
template <typename T, typename C> class is_variant_formattable_ {
|
||||
template <std::size_t... I>
|
||||
static std::conjunction<
|
||||
is_formattable<std::variant_alternative_t<I, T>, C>...>
|
||||
check(std::index_sequence<I...>);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
decltype(check(variant_index_sequence<T>{}))::value;
|
||||
};
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
auto write_variant_alternative(OutputIt out, basic_string_view<Char> str)
|
||||
-> OutputIt {
|
||||
return write_escaped_string(out, str);
|
||||
}
|
||||
|
||||
// Returns true if T has a std::string-like interface, like std::string_view.
|
||||
template <typename T> class is_std_string_likevv {
|
||||
template <typename U>
|
||||
static auto check(U* p)
|
||||
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
is_string<T>::value ||
|
||||
std::is_convertible<T, std_string_view<char>>::value ||
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_std_string_likevv<fmt::basic_string_view<Char>> : std::true_type {};
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)>
|
||||
inline auto write_variant_alternative(OutputIt out, const T& str) -> OutputIt {
|
||||
auto sv = std_string_view<Char>(str);
|
||||
return write_variant_alternative<Char>(out, basic_string_view<Char>(sv));
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename Arg,
|
||||
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
|
||||
OutputIt write_variant_alternative(OutputIt out, const Arg v) {
|
||||
return write_escaped_char(out, v);
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename Arg,
|
||||
FMT_ENABLE_IF(
|
||||
!is_std_string_likevv<typename std::decay<Arg>::type>::value &&
|
||||
!std::is_same<Arg, Char>::value)>
|
||||
OutputIt write_variant_alternative(OutputIt out, const Arg& v) {
|
||||
return write<Char>(out, v);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T> struct is_variant_like {
|
||||
static constexpr const bool value = detail::is_variant_like_<T>::value;
|
||||
};
|
||||
|
||||
template <typename T, typename C> struct is_variant_formattable {
|
||||
static constexpr const bool value =
|
||||
detail::is_variant_formattable_<T, C>::value;
|
||||
};
|
||||
|
||||
template <typename VariantT, typename Char>
|
||||
struct formatter<
|
||||
VariantT, Char,
|
||||
std::enable_if_t<std::conjunction_v<
|
||||
is_variant_like<VariantT>, is_variant_formattable<VariantT, Char>>>> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext = format_context>
|
||||
auto format(const VariantT& value, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
*out++ = '<';
|
||||
std::visit(
|
||||
[&](const auto& v) {
|
||||
out = detail::write_variant_alternative<Char>(out, v);
|
||||
},
|
||||
value);
|
||||
*out++ = '>';
|
||||
return out;
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
#endif
|
||||
|
||||
#endif // FMT_STD_H_
|
||||
|
||||
@ -1,107 +0,0 @@
|
||||
// Formatting library for C++ - experimental range support
|
||||
//
|
||||
// {fmt} support for variant interface.
|
||||
|
||||
#ifndef FMT_VARIANT_H_
|
||||
#define FMT_VARIANT_H_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "format.h"
|
||||
#include "ranges.h"
|
||||
|
||||
#define FMT_HAS_VARIANT FMT_CPLUSPLUS >= 201703L
|
||||
#if FMT_HAS_VARIANT
|
||||
|
||||
#include <variant>
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
template <typename Char> struct formatter<std::monostate, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext = format_context>
|
||||
auto format(const std::monostate&, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
*out++ = ' ';
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
using variant_index_sequence = make_index_sequence<std::variant_size<T>::value>;
|
||||
|
||||
// variant_size and variant_alternative check.
|
||||
template <typename T> class is_variant_like_ {
|
||||
template <typename U>
|
||||
static auto check(U* p) -> decltype(std::variant_size<U>::value, int());
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
// formattable element check
|
||||
template <typename T, typename C, bool = is_variant_like_<T>::value>
|
||||
class is_variant_formattable_ {
|
||||
public:
|
||||
static constexpr const bool value = false;
|
||||
};
|
||||
template <typename T, typename C> class is_variant_formattable_<T, C, true> {
|
||||
template <std::size_t... I>
|
||||
static std::integral_constant<
|
||||
bool,
|
||||
(is_formattable<std::variant_alternative_t<I, T>, C>::value && ...)>
|
||||
check(index_sequence<I...>);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
decltype(check(variant_index_sequence<T>{}))::value;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T> struct is_variant_like {
|
||||
static constexpr const bool value = detail::is_variant_like_<T>::value;
|
||||
};
|
||||
|
||||
template <typename T, typename C> struct is_variant_formattable {
|
||||
static constexpr const bool value =
|
||||
detail::is_variant_formattable_<T, C>::value;
|
||||
};
|
||||
|
||||
template <typename VariantT, typename Char>
|
||||
struct formatter<
|
||||
VariantT, Char,
|
||||
enable_if_t<fmt::is_variant_like<VariantT>::value &&
|
||||
fmt::is_variant_formattable<VariantT, Char>::value>> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext = format_context>
|
||||
auto format(const VariantT& value, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
*out++ = '<';
|
||||
std::visit(
|
||||
[&](const auto& v) { out = detail::write_range_entry<Char>(out, v); },
|
||||
value);
|
||||
*out++ = '>';
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_VARIANT_H_
|
||||
|
||||
#endif // FMT_HAS_VARIANT
|
||||
@ -76,7 +76,6 @@ if (MSVC)
|
||||
endif()
|
||||
add_fmt_test(printf-test)
|
||||
add_fmt_test(ranges-test ranges-odr-test.cc)
|
||||
add_fmt_test(variant-test)
|
||||
add_fmt_test(scan-test)
|
||||
add_fmt_test(std-test)
|
||||
add_fmt_test(unicode-test HEADER_ONLY)
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include "fmt/std.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
TEST(std_test, path) {
|
||||
@ -32,3 +34,34 @@ TEST(std_test, path) {
|
||||
TEST(std_test, thread_id) {
|
||||
EXPECT_FALSE(fmt::format("{}", std::this_thread::get_id()).empty());
|
||||
}
|
||||
|
||||
TEST(std_test, variant) {
|
||||
#ifdef __cpp_lib_variant
|
||||
EXPECT_EQ(fmt::format("{}", std::monostate{}), " ");
|
||||
using V0 = std::variant<int, float, std::string, char>;
|
||||
V0 v0(42);
|
||||
V0 v1(1.5f);
|
||||
V0 v2("hello");
|
||||
V0 v3('i');
|
||||
EXPECT_EQ(fmt::format("{}", v0), "<42>");
|
||||
EXPECT_EQ(fmt::format("{}", v1), "<1.5>");
|
||||
EXPECT_EQ(fmt::format("{}", v2), "<\"hello\">");
|
||||
EXPECT_EQ(fmt::format("{}", v3), "<'i'>");
|
||||
|
||||
struct unformattable {};
|
||||
EXPECT_FALSE((fmt::is_formattable<unformattable>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::variant<unformattable>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::variant<unformattable, int>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::variant<int, unformattable>>::value));
|
||||
EXPECT_FALSE(
|
||||
(fmt::is_formattable<std::variant<unformattable, unformattable>>::value));
|
||||
EXPECT_TRUE((fmt::is_formattable<std::variant<int, float>>::value));
|
||||
|
||||
using V1 = std::variant<std::monostate, std::string, std::string>;
|
||||
V1 v4{};
|
||||
V1 v5{std::in_place_index<1>, "yes, this is variant"};
|
||||
|
||||
EXPECT_EQ(fmt::format("{}", v4), "< >");
|
||||
EXPECT_EQ(fmt::format("{}", v5), "<\"yes, this is variant\">");
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
// Formatting library for C++ - experimental variant API
|
||||
//
|
||||
// {fmt} support for variant interface.
|
||||
|
||||
#include "fmt/variant.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#if FMT_HAS_VARIANT
|
||||
|
||||
TEST(variant_test, format_monostate) {
|
||||
EXPECT_EQ(fmt::format("{}", std::monostate{}), " ");
|
||||
}
|
||||
TEST(variant_test, format_variant) {
|
||||
using V0 = std::variant<int, float, std::string, char>;
|
||||
V0 v0(42);
|
||||
V0 v1(1.5f);
|
||||
V0 v2("hello");
|
||||
V0 v3('i');
|
||||
EXPECT_EQ(fmt::format("{}", v0), "<42>");
|
||||
EXPECT_EQ(fmt::format("{}", v1), "<1.5>");
|
||||
EXPECT_EQ(fmt::format("{}", v2), "<\"hello\">");
|
||||
EXPECT_EQ(fmt::format("{}", v3), "<'i'>");
|
||||
|
||||
struct unformattable{};
|
||||
EXPECT_FALSE((fmt::is_formattable<unformattable>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::variant<unformattable>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::variant<unformattable,int>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::variant<int,unformattable>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::variant<unformattable,unformattable>>::value));
|
||||
EXPECT_TRUE((fmt::is_formattable<std::variant<int,float>>::value));
|
||||
|
||||
using V1 = std::variant<std::monostate, std::string, std::string>;
|
||||
V1 v4{};
|
||||
V1 v5{std::in_place_index<1>,"yes, this is variant"};
|
||||
|
||||
EXPECT_EQ(fmt::format("{}", v4), "< >");
|
||||
EXPECT_EQ(fmt::format("{}", v5), "<\"yes, this is variant\">");
|
||||
}
|
||||
|
||||
#endif // FMT_HAS_VARIANT
|
||||
Loading…
Reference in New Issue
Block a user