Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions include/fmt/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -1030,7 +1030,7 @@ enum {
pointer_set = set(type::pointer_type)
};

struct view {};
template <typename T> struct is_view : std::false_type {};

template <typename Char, typename T> struct named_arg;
template <typename T> struct is_named_arg : std::false_type {};
Expand All @@ -1039,14 +1039,17 @@ template <typename T> struct is_static_named_arg : std::false_type {};
template <typename Char, typename T>
struct is_named_arg<named_arg<Char, T>> : std::true_type {};

template <typename Char, typename T> struct named_arg : view {
template <typename Char, typename T> struct named_arg {
const Char* name;
const T& value;

named_arg(const Char* n, const T& v) : name(n), value(v) {}
static_assert(!is_named_arg<T>::value, "nested named arguments");
};

template <typename Char, typename T>
struct is_view<named_arg<Char, T>> : std::true_type {};

template <bool B = false> constexpr auto count() -> int { return B ? 1 : 0; }
template <bool B1, bool B2, bool... Tail> constexpr auto count() -> int {
return (B1 ? 1 : 0) + count<B2, Tail...>();
Expand Down Expand Up @@ -2715,7 +2718,7 @@ template <typename... T> struct fstring {
template <size_t N>
FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) {
using namespace detail;
static_assert(count<(std::is_base_of<view, remove_reference_t<T>>::value &&
static_assert(count<(is_view<remove_cvref_t<T>>::value &&
std::is_reference<T>::value)...>() == 0,
"passing views as lvalues is disallowed");
if (FMT_USE_CONSTEVAL) parse_format_string<char>(s, checker(s, arg_pack()));
Expand Down
4 changes: 3 additions & 1 deletion include/fmt/color.h
Original file line number Diff line number Diff line change
Expand Up @@ -468,12 +468,14 @@ template <typename Char> inline void reset_color(buffer<Char>& buffer) {
buffer.append(reset_color.begin(), reset_color.end());
}

template <typename T> struct styled_arg : view {
template <typename T> struct styled_arg {
const T& value;
text_style style;
styled_arg(const T& v, text_style s) : value(v), style(s) {}
};

template <typename T> struct is_view<styled_arg<T>> : std::true_type {};

template <typename Char>
void vformat_to(buffer<Char>& buf, text_style ts, basic_string_view<Char> fmt,
basic_format_args<buffered_context<Char>> args) {
Expand Down
6 changes: 5 additions & 1 deletion include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -3554,13 +3554,17 @@ FMT_CONSTEXPR void handle_dynamic_spec(
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <typename T, typename Char, size_t N,
fmt::detail::fixed_string<Char, N> Str>
struct static_named_arg : view {
struct static_named_arg {
static constexpr auto name = Str.data;

const T& value;
static_named_arg(const T& v) : value(v) {}
};

template <typename T, typename Char, size_t N,
fmt::detail::fixed_string<Char, N> Str>
struct is_view<static_named_arg<T, Char, N, Str>> : std::true_type {};

template <typename T, typename Char, size_t N,
fmt::detail::fixed_string<Char, N> Str>
struct is_named_arg<static_named_arg<T, Char, N, Str>> : std::true_type {};
Expand Down
18 changes: 16 additions & 2 deletions include/fmt/ranges.h
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,7 @@ struct formatter<
};

template <typename It, typename Sentinel, typename Char = char>
struct join_view : detail::view {
struct join_view {
It begin;
Sentinel end;
basic_string_view<Char> sep;
Expand All @@ -629,6 +629,13 @@ struct join_view : detail::view {
: begin(std::move(b)), end(e), sep(s) {}
};

namespace detail {

template <typename It, typename Sentinel, typename Char>
struct is_view<join_view<It, Sentinel, Char>> : std::true_type {};

} // namespace detail

template <typename It, typename Sentinel, typename Char>
struct formatter<join_view<It, Sentinel, Char>, Char> {
private:
Expand Down Expand Up @@ -670,14 +677,21 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
}
};

template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
template <typename Char, typename Tuple> struct tuple_join_view {
const Tuple& tuple;
basic_string_view<Char> sep;

tuple_join_view(const Tuple& t, basic_string_view<Char> s)
: tuple(t), sep{s} {}
};

namespace detail {

template <typename Char, typename Tuple>
struct is_view<tuple_join_view<Char, Tuple>> : std::true_type {};

} // namespace detail

// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
// support in tuple_join. It is disabled by default because of issues with
// the dynamic width and precision.
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ add_fmt_test(assert-test)
add_fmt_test(chrono-test)
add_fmt_test(color-test)
add_fmt_test(gtest-extra-test)
add_fmt_test(incomplete-type-test)
add_fmt_test(format-test mock-allocator.h)
if (MSVC)
target_compile_options(format-test PRIVATE /bigobj)
Expand Down
41 changes: 41 additions & 0 deletions test/incomplete-type-test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Formatting library for C++ - formatting library tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.

#include "fmt/format.h"
#include "fmt/color.h"
#include "gtest/gtest.h"

// Only defined after all the tests.
struct incomplete_type;
extern const incomplete_type& external_instance;

FMT_BEGIN_NAMESPACE

template <> struct formatter<incomplete_type> : formatter<int> {
auto format(const incomplete_type& x, context& ctx) const
-> decltype(ctx.out());
};

FMT_END_NAMESPACE

TEST(incomplete_type_test, format) {
EXPECT_EQ(fmt::format("{}", external_instance), fmt::format("{}", 42));
EXPECT_EQ(fmt::format("{:4}", external_instance), fmt::format("{:4}", 42));
EXPECT_EQ(fmt::format("{:4}", fmt::styled(external_instance, fg(fmt::color::red))), fmt::format("{:4}", fmt::styled(42, fg(fmt::color::red))));
}

struct incomplete_type {
int i;
};

const incomplete_type& external_instance{42};

auto fmt::formatter<incomplete_type>::format(const incomplete_type& x,
fmt::context& ctx) const
-> decltype(ctx.out()) {
return fmt::formatter<int>::format(x.i, ctx);
}