strong_type
strong_type copied to clipboard
format regression
181c9f4ff120b2e0e3b7b1e793e5833b9b931c3c causes this compile error with both gcc and clang:
/usr/include/c++/12/type_traits: In substitution of ‘template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = false; _Tp = fmt::v8::formatter<long unsigned int, char, void>]’:
/path/to/strong_type/include/strong_type/strong_type.hpp:1707:8: required from ‘struct fmt::v8::formatter<strong::type<long unsigned int, my_tag, strong::equality, strong::ostreamable, strong::ordered, strong::default_constructible>, char, void>’
/usr/include/c++/12/type_traits:971:30: required from ‘struct std::__is_constructible_impl<fmt::v8::formatter<strong::type<long unsigned int, my_tag, strong::equality, strong::ostreamable, strong::ordered, strong::default_constructible>, char, void> >’
/usr/include/c++/12/type_traits:977:12: required from ‘struct std::is_constructible<fmt::v8::formatter<strong::type<long unsigned int, my_tag, strong::equality, strong::ostreamable, strong::ordered, strong::default_constructible>, char, void> >’
/path/to/fmt-8.1.0/include/fmt/core.h:1472:13: required by substitution of ‘template<class T, class U, typename std::enable_if<((((! fmt::v8::detail::is_string<U>::value) && (! fmt::v8::is_char<U>::value)) && (! std::is_array<_To>::value)) && (std::is_constructible<fmt::v8::formatter<U, char, void> >::value || std::is_constructible<fmt::v8::detail::fallback_formatter<U, char, void> >::value)), int>::type <anonymous> > constexpr decltype (((fmt::v8::detail::arg_mapper<fmt::v8::basic_format_context<fmt::v8::appender, char> >*)this)->fmt::v8::detail::arg_mapper<fmt::v8::basic_format_context<fmt::v8::appender, char> >::do_map(forward<T>(val))) fmt::v8::detail::arg_mapper<fmt::v8::basic_format_context<fmt::v8::appender, char> >::map(T&&) [with T = const strong::type<long unsigned int, my_tag, strong::equality, strong::ostreamable, strong::ordered, strong::default_constructible>&; U = strong::type<long unsigned int, my_tag, strong::equality, strong::ostreamable, strong::ordered, strong::default_constructible>; typename std::enable_if<((((! fmt::v8::detail::is_string<U>::value) && (! fmt::v8::is_char<U>::value)) && (! std::is_array<_To>::value)) && (std::is_constructible<fmt::v8::formatter<U, char, void> >::value || std::is_constructible<fmt::v8::detail::fallback_formatter<U, char, void> >::value)), int>::type <anonymous> = <missing>]’
/path/to/fmt-8.1.0-R1/include/fmt/core.h:2670:41: required from ‘constexpr decltype (ctx.begin()) fmt::v8::detail::parse_format_specs(ParseContext&) [with T = strong::type<long unsigned int, my_tag, strong::equality, strong::ostreamable, strong::ordered, strong::default_constructible>; ParseContext = compile_parse_context<char, error_handler>; decltype (ctx.begin()) = const char*]’
/path/to/fmt-8.1.0-R1/include/fmt/core.h:2906:22: required from ‘constexpr fmt::v8::detail::format_string_checker<Char, ErrorHandler, Args>::format_string_checker(fmt::v8::basic_string_view<Char>, ErrorHandler) [with Char = char; ErrorHandler = fmt::v8::detail::error_handler; Args = {strong::type<long unsigned int, my_tag, strong::equality, strong::ostreamable, strong::ordered, strong::default_constructible>, long unsigned int}]’
/path/to/source.h:80:21: required from here
/usr/include/c++/12/type_traits:2614:11: error: no type named ‘type’ in ‘struct std::enable_if<false, fmt::v8::formatter<long unsigned int, char, void> >’
2614 | using enable_if_t = typename enable_if<_Cond, _Tp>::type;
Can you provide an example program? I fail to reproduce this.
it seems to be related to fmt's ostream
support and strong::ostreamable
:
#include <strong_type/strong_type.hpp>
#include <fmt/format.h>
#include <fmt/ostream.h>
using type = strong::
type< uint64_t, struct tag, strong::ostreamable, strong::default_constructible >;
void foo()
{
fmt::print("{}", type{});
}
Right. Confirmed. Thanks. It does work if you use strong::formattable
, but it should work with ostreamable
too.
After some digging, I now understand what causes the problem, but I'm not yet sure how to solve it. The type ::fmt::formatter<type...>
must conditionally exist only if strong::formattable
is used, which was the case with the earlier SFINAE solution.
Can you give branch fix_fmt a try? I believe it works.
~~the STONG_CONSTEXPR
caused problems for me:~~
namespace fmt
{
template<typename T, typename Tag, typename... M, typename Char>
struct formatter<::strong::type<T, Tag, M...>, Char> : formatter<T, Char>
{
template<typename FormatContext, typename Type>
// STRONG_CONSTEXPR
decltype(auto)
format(const Type& t, FormatContext& fc) const
noexcept(noexcept(std::declval<formatter<T, Char>>().format(std::declval<const T&>(), fc)))
{
return formatter<T, Char>::format(value_of(t), fc);
}
};
}
~~i believe this is because the formatter::format
function of my formatters is not constexpr~~
my formatter::format
functions were not const
, causing build failures, making them const, it works fine
They originally were non-const, but I noticed that they were const in the {fmt}
API examples. I can revert them to non-const.
Unfortunately it's too forgiving now and allows formatting even without either formattable
or ostreamable
, so I still have some work to do. :disappointed:
They originally were non-const, but I noticed that they were const in the
{fmt}
API examples. I can revert them to non-const.
no strong opinion from my side. in my cases the formatters are definitely const, though it may not be impossible to have non-const formatters for esoteric use cases
OK, now it works. I reverted back to non-const format functions. {fmt}
is OK with it, and the implementation can call a const version for the underlying type. The reverse is not true, as you noticed.
There's one somewhat esoteric caveat, though. Since version 9,{fmt}
requires you to define a formatter<T>
for your type even if you have <<
for stream insertion. In that case you let your formatter inherit from fmt::ostream_formatter
and have an empty body. The library creates it for you, but this means that if you use both strong::formattable
and strong::ostreamable
for your type, you'll get two conflicting definitions of fmt::formatter<>
for your type. I guess I could make one of them dominate in that case.
Do you have an opinion on this?
Fixed it so that if a type is both formattable
and ostreamable
, any use of fmt
will use the formatter<>
for the underlying type.
This was very tricky to get to work.
I'm claiming this one done now. Wow, what a ride. Leaving it open until a release has been tagged. If you disagree that it is indeed fixed, please let me know here and I'll remove the "fixed" label.
many thanks! i've tested it with my codebase and it works nicely!
Included in release v5