mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-31 03:07:36 +02:00
Add support for nonconst formattable types
This commit is contained in:
@ -711,6 +711,22 @@ class appender;
|
|||||||
|
|
||||||
FMT_BEGIN_DETAIL_NAMESPACE
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
|
|
||||||
|
template <typename Context, typename T>
|
||||||
|
constexpr auto is_const_formattable_impl(T*)
|
||||||
|
-> decltype(typename Context::template formatter_type<T>().format(
|
||||||
|
std::declval<const T&>(), std::declval<Context&>()),
|
||||||
|
true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
template <typename Context>
|
||||||
|
constexpr auto is_const_formattable_impl(...) -> bool {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
template <typename T, typename Context>
|
||||||
|
constexpr auto is_const_formattable() -> bool {
|
||||||
|
return is_const_formattable_impl<Context>(static_cast<T*>(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
// Extracts a reference to the container from back_insert_iterator.
|
// Extracts a reference to the container from back_insert_iterator.
|
||||||
template <typename Container>
|
template <typename Container>
|
||||||
inline auto get_container(std::back_insert_iterator<Container> it)
|
inline auto get_container(std::back_insert_iterator<Container> it)
|
||||||
@ -1112,8 +1128,8 @@ template <typename Char> struct named_arg_value {
|
|||||||
|
|
||||||
template <typename Context> struct custom_value {
|
template <typename Context> struct custom_value {
|
||||||
using parse_context = typename Context::parse_context_type;
|
using parse_context = typename Context::parse_context_type;
|
||||||
const void* value;
|
void* value;
|
||||||
void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx);
|
void (*format)(void* arg, parse_context& parse_ctx, Context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
// A formatting argument value.
|
// A formatting argument value.
|
||||||
@ -1164,26 +1180,30 @@ template <typename Context> class value {
|
|||||||
FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
|
FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
|
||||||
: named_args{args, size} {}
|
: named_args{args, size} {}
|
||||||
|
|
||||||
template <typename T> FMT_CONSTEXPR FMT_INLINE value(const T& val) {
|
template <typename T> FMT_CONSTEXPR FMT_INLINE value(T& val) {
|
||||||
custom.value = &val;
|
using value_type = remove_cvref_t<T>;
|
||||||
|
custom.value = const_cast<value_type*>(&val);
|
||||||
// Get the formatter type through the context to allow different contexts
|
// Get the formatter type through the context to allow different contexts
|
||||||
// have different extension points, e.g. `formatter<T>` for `format` and
|
// have different extension points, e.g. `formatter<T>` for `format` and
|
||||||
// `printf_formatter<T>` for `printf`.
|
// `printf_formatter<T>` for `printf`.
|
||||||
custom.format = format_custom_arg<
|
custom.format = format_custom_arg<
|
||||||
T, conditional_t<has_formatter<T, Context>::value,
|
value_type,
|
||||||
typename Context::template formatter_type<T>,
|
conditional_t<has_formatter<value_type, Context>::value,
|
||||||
fallback_formatter<T, char_type>>>;
|
typename Context::template formatter_type<value_type>,
|
||||||
|
fallback_formatter<value_type, char_type>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Formats an argument of a custom type, such as a user-defined class.
|
// Formats an argument of a custom type, such as a user-defined class.
|
||||||
template <typename T, typename Formatter>
|
template <typename T, typename Formatter>
|
||||||
static void format_custom_arg(const void* arg,
|
static void format_custom_arg(void* arg,
|
||||||
typename Context::parse_context_type& parse_ctx,
|
typename Context::parse_context_type& parse_ctx,
|
||||||
Context& ctx) {
|
Context& ctx) {
|
||||||
Formatter f;
|
auto f = Formatter();
|
||||||
parse_ctx.advance_to(f.parse(parse_ctx));
|
parse_ctx.advance_to(f.parse(parse_ctx));
|
||||||
ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx));
|
using qualified_type =
|
||||||
|
conditional_t<is_const_formattable<T, Context>(), const T, T>;
|
||||||
|
ctx.advance_to(f.format(*static_cast<qualified_type*>(arg), ctx));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1323,11 +1343,16 @@ template <typename Context> struct arg_mapper {
|
|||||||
static_cast<typename std::underlying_type<T>::type>(val))) {
|
static_cast<typename std::underlying_type<T>::type>(val))) {
|
||||||
return map(static_cast<typename std::underlying_type<T>::type>(val));
|
return map(static_cast<typename std::underlying_type<T>::type>(val));
|
||||||
}
|
}
|
||||||
template <typename T,
|
template <typename T, typename U = remove_cvref_t<T>,
|
||||||
FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
|
FMT_ENABLE_IF(!is_string<U>::value && !is_char<U>::value &&
|
||||||
(has_formatter<T, Context>::value ||
|
!std::is_array<U>::value &&
|
||||||
has_fallback_formatter<T, char_type>::value))>
|
(has_formatter<U, Context>::value ||
|
||||||
FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> const T& {
|
has_fallback_formatter<U, char_type>::value))>
|
||||||
|
FMT_CONSTEXPR FMT_INLINE auto map(T&& val) -> T& {
|
||||||
|
static_assert(is_const_formattable<U, Context>() ||
|
||||||
|
!std::is_const<remove_reference_t<T>>() ||
|
||||||
|
has_fallback_formatter<U, char_type>(),
|
||||||
|
"cannot format a const argument");
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1562,8 +1587,8 @@ FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context> {
|
|||||||
// another (not recommended).
|
// another (not recommended).
|
||||||
template <bool IS_PACKED, typename Context, type, typename T,
|
template <bool IS_PACKED, typename Context, type, typename T,
|
||||||
FMT_ENABLE_IF(IS_PACKED)>
|
FMT_ENABLE_IF(IS_PACKED)>
|
||||||
FMT_CONSTEXPR FMT_INLINE auto make_arg(const T& val) -> value<Context> {
|
FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
|
||||||
const auto& arg = arg_mapper<Context>().map(val);
|
const auto& arg = arg_mapper<Context>().map(std::forward<T>(val));
|
||||||
static_assert(
|
static_assert(
|
||||||
!std::is_same<decltype(arg), const unformattable&>::value,
|
!std::is_same<decltype(arg), const unformattable&>::value,
|
||||||
"Cannot format an argument. To make type T formattable provide a "
|
"Cannot format an argument. To make type T formattable provide a "
|
||||||
@ -1684,14 +1709,16 @@ class format_arg_store
|
|||||||
: 0);
|
: 0);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FMT_CONSTEXPR FMT_INLINE format_arg_store(const Args&... args)
|
template <typename... T>
|
||||||
|
FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args)
|
||||||
:
|
:
|
||||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||||
basic_format_args<Context>(*this),
|
basic_format_args<Context>(*this),
|
||||||
#endif
|
#endif
|
||||||
data_{detail::make_arg<
|
data_{detail::make_arg<
|
||||||
is_packed, Context,
|
is_packed, Context,
|
||||||
detail::mapped_type_constant<Args, Context>::value>(args)...} {
|
detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>(
|
||||||
|
std::forward<T>(args))...} {
|
||||||
detail::init_named_args(data_.named_args(), 0, 0, args...);
|
detail::init_named_args(data_.named_args(), 0, 0, args...);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1705,9 +1732,9 @@ class format_arg_store
|
|||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename Context = format_context, typename... Args>
|
template <typename Context = format_context, typename... Args>
|
||||||
constexpr auto make_format_args(const Args&... args)
|
constexpr auto make_format_args(Args&&... args)
|
||||||
-> format_arg_store<Context, Args...> {
|
-> format_arg_store<Context, remove_cvref_t<Args>...> {
|
||||||
return {args...};
|
return {std::forward<Args>(args)...};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -847,3 +847,43 @@ TEST(core_test, adl) {
|
|||||||
fmt::print("{}", s);
|
fmt::print("{}", s);
|
||||||
fmt::print(stdout, "{}", s);
|
fmt::print(stdout, "{}", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct const_formattable {};
|
||||||
|
struct nonconst_formattable {};
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
template <> struct formatter<const_formattable> {
|
||||||
|
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto format(const const_formattable&, format_context& ctx)
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
auto test = string_view("test");
|
||||||
|
return std::copy_n(test.data(), test.size(), ctx.out());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct formatter<nonconst_formattable> {
|
||||||
|
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto format(nonconst_formattable&, format_context& ctx)
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
auto test = string_view("test");
|
||||||
|
return std::copy_n(test.data(), test.size(), ctx.out());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
TEST(core_test, is_const_formattable) {
|
||||||
|
EXPECT_TRUE((fmt::detail::is_const_formattable<const_formattable,
|
||||||
|
fmt::format_context>()));
|
||||||
|
EXPECT_FALSE((fmt::detail::is_const_formattable<nonconst_formattable,
|
||||||
|
fmt::format_context>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(core_test, format_nonconst) {
|
||||||
|
EXPECT_EQ(fmt::format("{}", nonconst_formattable()), "test");
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user