@康桓瑋 mentioned in a comment that this may be a bug of fmt
. The following analysis is purely about why it doesn't work currently, regardless of whether it is a bug or not.
If we use clang 17 to compile the code we can see the following error message
/opt/compiler-explorer/libs/fmt/10.1.1/include/fmt/format.h:4061:18: error: no matching member function for call to 'format'
4061 | return base::format(format_as(value), ctx);
| ~~~~~~^~~~~~
/opt/compiler-explorer/libs/fmt/10.1.1/include/fmt/core.h:1308:22: note: in instantiation of function template specialization 'fmt::formatter<CodecMask>::format<fmt::basic_format_context<fmt::appender, char>>' requested here
1308 | ctx.advance_to(f.format(*static_cast<qualified_type*>(arg), ctx));
| ^
/opt/compiler-explorer/libs/fmt/10.1.1/include/fmt/core.h:1291:21: note: in instantiation of function template specialization 'fmt::detail::value<fmt::basic_format_context<fmt::appender, char>>::format_custom_arg<CodecMask, fmt::formatter<CodecMask>>' requested here
1291 | custom.format = format_custom_arg<
| ^
/opt/compiler-explorer/libs/fmt/10.1.1/include/fmt/core.h:2903:37: note: in instantiation of function template specialization 'fmt::format<CodecMask &>' requested here
2903 | return fmt::print(f, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
| ^
/opt/compiler-explorer/libs/fmt/10.1.1/include/fmt/core.h:2912:15: note: in instantiation of function template specialization 'fmt::println<CodecMask &>' requested here
2912 | return fmt::println(stdout, fmt, std::forward<T>(args)...);
| ^
<source>:68:10: note: in instantiation of function template specialization 'fmt::println<CodecMask &>' requested here
68 | fmt::println("mask: {}", mask);
| ^
/opt/compiler-explorer/libs/fmt/10.1.1/include/fmt/ranges.h:546:8: note: candidate function template not viable: expects an lvalue for 1st argument
546 | auto format(range_type& range, FormatContext& ctx) const
| ^ ~~~~~~~~~~~~~~~~~
That means the issue is that base::format
is expecting a range_type&
as its first argument, but we have provided a rvalue. Indeed, the function format_as
is actually returning a rvalue. Usually, it should just work fine for rvalue if range_type
is const T
for some T
. This is handled by the following lines around line 412 in ranges.h
:
template <typename R>
using maybe_const_range =
conditional_t<has_const_begin_end<R>::value, const R, R>;
Where has_const_begin_end
is defined as
template <typename T>
struct has_const_begin_end<
T,
void_t<
decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
: std::true_type {};
We may roughly consider the detail::range_begin
and detail::range_end
as equivalent to std::ranges::begin
and std::ranges::end
. This is at least true for views. So the real issue is that the filter_view
is not const-iterable (Thanks to @cpplearner for correcting in the comments), and the reason for that is explained in this answer (also by @cpplearner).
Now, none of this matters if we pass the view directly to fmt::format
or fmt::println
, because the correct constructor for fmt::format_arg
will be called for our arguments, and that will handle both lvalues and rvalues correctly.
The solution is already pointed out in a comment: collect them in a vector before you return.
auto format_as(CodecMask m) {
return std::ranges::to<std::vector>(CodecMask::k_codecs | std::views::filter([&](auto c) { return m.has(c); }));
}
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4