-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/complete module #2340
Feature/complete module #2340
Conversation
721829d
to
929f2ba
Compare
using detail::specs_handler; | ||
using detail::to_unsigned; | ||
using detail::type; | ||
using detail::write; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still not sure, if this is really needed by the standard or if this is just an implementation deficit. At least it looks ugly.
From reading "C++ Templates - The Definitive Guide" and the standard, it is clear that the local class is a templated entity. I need further studies of the standard to find an answer to the question: which names within this so-called 'temploid' are bound at which time.
I'd rather prefer to make this 'temploid' a proper class template.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which local class are you referring to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
struct format_handler
within vformat_to
in format.h, currently line 2649.
The problem is explored with this stripped-down code example here https://godbolt.org/z/34Ph9dee9. Fortunately I got this compiled with gcc (modules-branch) for the first time just a few minutes ago, so that I have a second opinion on the matter now: gcc doesn't require this 'symbolic-linking' of names into the body of vformat_to
and all of my studies of the standard seem to corroborate that. Therefore I consider this an implementation bug in latest msvc. This symbolic linking is the only workaround that works at all. Qualified name lookup doesn't. And only local classes within function templates are affected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is acknowledged as bug in msvc by realiable sources familiar with the compiler: name-binding is happening in phase 2 instead of phase 1 in this particular case. In other words: too late to find anything in namespace detail
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a short comment clarifying that this is a workaround for an msvc bug?
929f2ba
to
32af9f9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR.
In general looks good but i don't think we should be introducing a new header for macros because FMT_STRING
and FMT_COMPILE
are effectively obsolete with C++20. The former is redundant and the latter is replaced with a UDL.
include/fmt/compile.h
Outdated
FMT_MODULE_EXPORT_BEGIN | ||
|
||
// compile-time support | ||
namespace ct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why new namespace?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is stuff that was in namespace detail
before. It must be made visible to be used from importers, f.e. by the ""_cf
udl. The compiled_string
tag class can be moved into namespace fmt
just as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can't we make detail::compiled_string
visible? It's an implementation detail and not supposed to be directly used by anyone, so detail
is the correct namespace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we make detail::compiled_string
visible then the namespace detail
becomes implicitly visible, too. This disenfranchises testing from ever checking if anything from that namespace becomes unwittingly visible that was never meant to be visible int the first place. This is the sole reason why I introduced namespace ct
: everything in there is the sole minimum of non-public API that needs to be visible. I'd be sad if we'd call namespace detail
"open season" to visibility. I was bugging Cameron to fix the current issue in msvc of detail
being visible even if nothing in there is currently visible (he checked this with his BMI explorer tool) and he told me that this should be easy to fix. 🤞
I've just finished reworking the PR and peeked here to check for any news. Part of the rework was moving compiled_string
to namespace fmt
because I got the feeling you were objecting to putting it into ct
where other necessarily visible compile-time related stuff is moved to as well. I will push the PR as it is now. Changing it in either direction is a piece of cake and can be done within a few minutes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not making detail
visible makes sense but ct
looks like part of the public API which is undesirable. I suggest renaming ct
to something like detail_exported
to make it clear that this is an internal API but exported for module purposes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
std::wstring w; | ||
fmt::format_to(std::back_inserter(w), L"{}", 42); | ||
EXPECT_EQ(L"42", w); | ||
|
||
wchar_t wbuffer[4] = {0}; | ||
fmt::format_to(wbuffer, L"{}", 42); | ||
EXPECT_EQ(L"42", std::wstring_view(wbuffer)); | ||
|
||
fmt::wmemory_buffer wb; | ||
fmt::format_to(wb, L"{}", 42); | ||
EXPECT_EQ(L"42", std::wstring_view(wbuffer)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As before, I think this does too much functional testing. I suggest having a single wide format_to call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are different overloads in the API. I think this is the minimal set for full coverage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, let's keep them.
test/module-test.cc
Outdated
EXPECT_WRITE(stdout, fmt::print(L"{}µ", 42), as_string(L"42µ")); | ||
EXPECT_WRITE(stderr, fmt::print(stderr, L"{}µ", 4.2), as_string(L"4.2µ")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These can be problematic because IIRC they may fail if the stream is not in wide mode. I suggest only checking that this compiles without actually running, i.e. wrap in if (false)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, will do.
Even better then. I'm not keen on seeing macros in any API.
Both macros are listed in the documentation as official part of the API and this is why I felt like providing them. |
|
32af9f9
to
8f45f23
Compare
BTW, I've removed the block in my local repo that prevents msvc from running the |
include/fmt/compile.h
Outdated
FMT_MODULE_EXPORT_BEGIN | ||
|
||
// A compile-time string which is compiled into fast formatting code. | ||
class compiled_string {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it still need to be public if we don't export the FMT_COMPILE
macro and only provide a UDL?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well spotted - thanks!
All related stuff removed.
669e148
to
60cdb0e
Compare
include/fmt/compile.h
Outdated
@@ -164,7 +164,7 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {}; | |||
#endif | |||
|
|||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS | |||
template <typename Char, size_t N, fixed_string<Char, N> Str> | |||
template <typename Char, size_t N, fmt::detail_exported::str<Char, N> Str> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest keeping the old name (fixed_string) which is more descriptive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right.
My concern is the size of these fmt::v7::detail_exported::fixed_string
linker symbols. This is just the common prefix. If you look at real code, the actual linker symbols with the specialized fixed strings become horribly long. May be I can convince Cameron to use a different mangling, may be derived from the base64-encoded SHA1 of the string value.
Anyway, I will change that.
60cdb0e
to
8e7b4db
Compare
include/fmt/format.h
Outdated
// Converts a compile-time string to basic_string_view. | ||
template <typename Char, size_t N> | ||
constexpr auto string_to_view(const Char (&s)[N]) -> basic_string_view<Char> { | ||
// Remove trailing NUL character if needed. Won't be present if this is used | ||
// with a raw character array (i.e. not defined as a string). | ||
return {s, N - (std::char_traits<Char>::to_int_type(s[N - 1]) == 0 ? 1 : 0)}; | ||
} | ||
template <typename Char> | ||
constexpr auto string_to_view(detail::std_string_view<Char> s) | ||
-> basic_string_view<Char> { | ||
return {s.data(), s.size()}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are only used in macros that are not exported so probably should be in the detail
namespace, not detail_exported
. Also please keep the old names (compile_string_to_view
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not correct. These overloads must be visible or xchar.h
:231 will fail to compile. And there is no workaround for that.
I will change the name. Even though nothing is compiled here but just a view to the stored data created.
…on in module Make just the necessary parts available for lookup from client context.
8e7b4db
to
1a351a2
Compare
Thank you! |
Add all missing pieces to complete {fmt} as module:
char
overloadsThis includes the macro-based API
FMT_STRING
andFMT_COMPILE
by means of a module companion headerfmt.h