-
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
Module linkage fixes for shared build #4169
Conversation
…texpr, to avoid missing external symbols when using fmt module with shared build due to modules not defaulting to implicit inline.
NOTE: Looks as if basic_format_args::get(string_view) could probably be made constexpr instead, but sticking with minimal change approach.
…+ std::string constexpr limitations.
Linter seems to be complaining about formatting that I didn't change. |
Before we go into detail, could you link to where this assumption comes from?
This seems like a major incompatibility and I would be surprised if it was the case. |
Sure. The relevant revision to the standard was P1779. There's also some discussion regarding it in this SO post. |
include/fmt/base.h
Outdated
FMT_CONSTEXPR explicit buffer_traits(size_t) {} | ||
FMT_CONSTEXPR auto count() const -> size_t { return 0; } | ||
FMT_CONSTEXPR auto limit(size_t size) -> size_t { return 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 can be constexpr
. FMT_CONSTEXPR
is only for cases when C++14 relaxed constexpr
is needed.
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.
Okay my bad, hadn't realized the distinction. I think all the changes are now constexpr
where they can be.
include/fmt/base.h
Outdated
FMT_CONSTEXPR explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} | ||
FMT_CONSTEXPR auto count() const -> size_t { return count_; } | ||
FMT_CONSTEXPR auto limit(size_t size) -> size_t { |
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.
Same here and in a few more places below.
include/fmt/base.h
Outdated
@@ -2223,7 +2223,7 @@ struct locale_ref { | |||
public: | |||
constexpr locale_ref() : locale_(nullptr) {} | |||
template <typename Locale> explicit locale_ref(const Locale& loc); | |||
explicit operator bool() const noexcept { return locale_ != nullptr; } | |||
FMT_INLINE explicit operator bool() const noexcept { return locale_ != nullptr; } |
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.
Here and elsewhere, please use inline
instead of FMT_INLINE
since the latter expands to always inline in optimized mode.
include/fmt/chrono.h
Outdated
FMT_INLINE dispatcher(std::time_t t) : time_(t) {} | ||
|
||
auto run() -> bool { | ||
FMT_INLINE auto run() -> bool { | ||
using namespace fmt::detail; | ||
return handle(localtime_r(&time_, &tm_)); | ||
} | ||
|
||
auto handle(std::tm* tm) -> bool { return tm != nullptr; } | ||
FMT_INLINE auto handle(std::tm* tm) -> bool { return tm != nullptr; } | ||
|
||
auto handle(detail::null<>) -> bool { | ||
FMT_INLINE auto handle(detail::null<>) -> bool { | ||
using namespace fmt::detail; | ||
return fallback(localtime_s(&tm_, &time_)); | ||
} | ||
|
||
auto fallback(int res) -> bool { return res == 0; } | ||
FMT_INLINE auto fallback(int res) -> bool { return res == 0; } | ||
|
||
#if !FMT_MSC_VERSION | ||
auto fallback(detail::null<>) -> bool { | ||
FMT_INLINE auto fallback(detail::null<>) -> bool { |
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 needed since the struct where these functions are defined is inaccessible to module consumers.
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.
Although I may have branched out a little when making the changes, the vast majority of them were made on functions that were generating linker errors. I've verified again, these are necessary for the linker to succeed.
Essentially, the issue being addressed here is one of dll symbol export, not module name export, and so the visibility of names (which is what module export/detail namespaces relate to) I think has no bearing. Since this struct is in an inline
function which is exposed, a consumer TU that calls this function (directly or transitively) will either need to inline the member functions of the struct, or emit an external symbol reference for them. So the same choice still applies.
include/fmt/chrono.h
Outdated
@@ -912,7 +912,7 @@ template <typename Derived> struct null_chrono_spec_handler { | |||
}; | |||
|
|||
struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> { | |||
FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); } | |||
FMT_NORETURN FMT_INLINE void unsupported() { FMT_THROW(format_error("no format")); } |
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.
Here and in other places in the detail
namespace - inline
is not needed since it is not exported from the module.
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 above, detail
namespace functions will still generate linker errors if neither inline
nor dll-exported.
e30fadd
to
7226c0e
Compare
OK, considering that the number of changes is manageable, we can merge it in but please apply clang-format first. |
Merged, thanks! |
Following on from #4153:
constexpr
, this was indeed a Clang bug, and anything already markedconstexpr
should be a non-issue; the problem has been fixed on Clang trunk with potential for it to be backported to the 19.x release.#include
withimport
were also being caused by a related Clang bug, also now fixed on trunk.fmt
module via a shared library build, due to the last bullet point in the linked issue.This PR addresses those remaining issues by adding
FMT_INLINE
/FMT_CONSTEXPR
to some member functions defined within the class definition. However, due to the nature of linker issues, problematic cases are only being picked up where a symbol is actually being used from an external dll/exe. Currently, the coverage for that is limited -fmt
's own CI is not yet running modules builds I think (or anyway not withFMT_SHARED
); thebuild2
fmt
package CI also cannot stress test this as the very latest Clang is not available. Furthermore, I've so far only enabled a subset of the tests in thebuild2
package.Fundamentally, I think this addition of
inline
orconstexpr
is essentially needed on every single in-class defined member function which is intended for public use (or alternativelyFMT_API
on the member/class), which is probably a lot more than what I've addressed in these commits and would be quite intrusive. So of course feel free to merge if you like, but it's possible some more thought is warranted in regards toexport
ed from the module, but forexport
, doing things in bulk is possible and less intrusive in terms of code changes)inline
vsFMT_API