Skip to content

Commit

Permalink
Add member format_as for std
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed Sep 20, 2024
1 parent 6d43c75 commit c460e73
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 14 deletions.
31 changes: 17 additions & 14 deletions include/fmt/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
# include <stdio.h> // FILE
# include <string.h> // memcmp

// <cstddef> is also included transitively from <type_traits>.
# include <cstddef> // std::byte
# include <type_traits> // std::enable_if
#endif

Expand Down Expand Up @@ -643,15 +641,6 @@ struct formatter {
formatter() = delete;
};

// This is defined in base.h instead of format.h to avoid injecting in std.
// It is a template to avoid undesirable implicit conversions to std::byte.
#ifdef __cpp_lib_byte
template <typename T, FMT_ENABLE_IF(std::is_same<T, std::byte>::value)>
inline auto format_as(T b) -> unsigned char {
return static_cast<unsigned char>(b);
}
#endif

/// Reports a format error at compile time or, via a `format_error` exception,
/// at runtime.
// This function is intentionally not constexpr to give a compile-time error.
Expand Down Expand Up @@ -1071,22 +1060,32 @@ using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
template <typename T>
using format_as_result =
remove_cvref_t<decltype(format_as(std::declval<const T&>()))>;
template <typename T>
using format_as_member_result =
remove_cvref_t<decltype(formatter<T>::format_as(std::declval<const T&>()))>;

template <typename T, typename Enable = std::true_type>
struct use_format_as : std::false_type {};
// format_as member is only used to avoid injection into the std namespace.
template <typename T, typename Enable = std::true_type>
struct use_format_as_member : std::false_type {};

// Only map owning types because mapping views can be unsafe.
template <typename T>
struct use_format_as<
T, bool_constant<std::is_integral<format_as_result<T>>::value>>
T, bool_constant<std::is_arithmetic<format_as_result<T>>::value>>
: std::true_type {};
template <typename T>
struct use_format_as_member<
T, bool_constant<std::is_arithmetic<format_as_member_result<T>>::value>>
: std::true_type {};

template <typename T, typename U = remove_const_t<T>>
using use_formatter =
bool_constant<(std::is_class<T>::value || std::is_enum<T>::value ||
std::is_union<T>::value || std::is_array<T>::value) &&
!has_to_string_view<T>::value && !is_named_arg<T>::value &&
!use_format_as<T>::value>;
!use_format_as<T>::value && !use_format_as_member<T>::value>;

template <typename Char, typename T, typename U = remove_const_t<T>>
auto has_formatter_impl(T* p, buffered_context<Char>* ctx = nullptr)
Expand Down Expand Up @@ -1147,6 +1146,8 @@ template <typename Char> struct type_mapper {

template <typename T, FMT_ENABLE_IF(use_format_as<T>::value)>
static auto map(const T& x) -> decltype(map(format_as(x)));
template <typename T, FMT_ENABLE_IF(use_format_as_member<T>::value)>
static auto map(const T& x) -> decltype(map(formatter<T>::format_as(x)));

template <typename T, FMT_ENABLE_IF(use_formatter<T>::value)>
static auto map(T&) -> conditional_t<has_formatter<T, Char>(), T&, void>;
Expand Down Expand Up @@ -2144,6 +2145,8 @@ template <typename Context> class value {

template <typename T, FMT_ENABLE_IF(use_format_as<T>::value)>
value(const T& x) : value(format_as(x)) {}
template <typename T, FMT_ENABLE_IF(use_format_as_member<T>::value)>
value(const T& x) : value(formatter<T>::format_as(x)) {}

template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
value(const T& named_arg) : value(named_arg.value) {}
Expand Down Expand Up @@ -2376,7 +2379,7 @@ template <typename T> class basic_appender {
public:
using iterator_category = int;
using value_type = T;
using difference_type = ptrdiff_t;
using difference_type = decltype(std::declval<T*>() - std::declval<T*>());
using pointer = T*;
using reference = T&;
using container_type = detail::buffer<T>;
Expand Down
12 changes: 12 additions & 0 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -3919,6 +3919,18 @@ constexpr auto format_as(Enum e) noexcept -> underlying_t<Enum> {
}
} // namespace enums

#ifdef __cpp_lib_byte
template <> struct formatter<std::byte> : formatter<unsigned> {
static auto format_as(std::byte b) -> unsigned char {
return static_cast<unsigned char>(b);
}
template <typename Context>
auto format(std::byte b, Context& ctx) const -> decltype(ctx.out()) {
return formatter<unsigned>::format(format_as(b), ctx);
}
};
#endif

class bytes {
private:
string_view data_;
Expand Down
2 changes: 2 additions & 0 deletions test/format-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2030,6 +2030,8 @@ enum big_enum : unsigned long long { big_enum_value = 5000000000ULL };
auto format_as(big_enum e) -> unsigned long long { return e; }

TEST(format_test, strong_enum) {
auto arg = fmt::basic_format_arg<fmt::context>(big_enum_value);
EXPECT_EQ(arg.type(), fmt::detail::type::ulong_long_type);
EXPECT_EQ(fmt::format("{}", big_enum_value), "5000000000");
}
#endif
Expand Down

0 comments on commit c460e73

Please sign in to comment.