Skip to content

Feature/add libfmt #182

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

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft

Feature/add libfmt #182

wants to merge 1 commit into from

Conversation

Ozaq
Copy link
Contributor

@Ozaq Ozaq commented Mar 21, 2025

No description provided.

@Ozaq Ozaq force-pushed the feature/add-libfmt branch from e4a62ce to 5ffccda Compare March 21, 2025 22:38
Copy link

Private downstream CI failed.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14002135899.

@Ozaq Ozaq force-pushed the feature/add-libfmt branch from 5ffccda to 4b6d2f6 Compare March 21, 2025 22:49
@codecov-commenter
Copy link

codecov-commenter commented Mar 21, 2025

Codecov Report

Attention: Patch coverage is 34.11420% with 450 lines in your changes missing coverage. Please review.

Project coverage is 62.99%. Comparing base (76a6f33) to head (8150253).

Files with missing lines Patch % Lines
third-party/fmt-11.1.4/include/fmt/format-inl.h 22.51% 320 Missing ⚠️
third-party/fmt-11.1.4/src/os.cc 0.00% 106 Missing ⚠️
third-party/fmt-11.1.4/include/fmt/os.h 0.00% 14 Missing ⚠️
tests/format/test_format_log.cc 77.27% 5 Missing ⚠️
third-party/fmt-11.1.4/include/fmt/chrono.h 55.55% 4 Missing ⚠️
tests/format/test_format_to.cc 98.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #182      +/-   ##
===========================================
- Coverage    64.69%   62.99%   -1.70%     
===========================================
  Files         1107     1120      +13     
  Lines        56064    58603    +2539     
  Branches      4235     4543     +308     
===========================================
+ Hits         36268    36918     +650     
- Misses       19796    21685    +1889     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Ozaq Ozaq force-pushed the feature/add-libfmt branch 3 times, most recently from d46cff7 to f4a90c7 Compare March 21, 2025 23:11
Copy link

Private downstream CI failed.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14002581491.

Copy link

Private downstream CI failed.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14003492452.

@pmaciel
Copy link
Member

pmaciel commented Mar 22, 2025

(Not a review)

This is an ideal candidate for git submodules. You can pin to a version, a branch typically main/master. Don't discard the testing....

Copy link

Private downstream CI failed.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14013287677.

Copy link

Private downstream CI failed.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14013322210.

@geier1993 geier1993 force-pushed the feature/add-libfmt branch from 83085b6 to d89bddd Compare March 23, 2025 21:45
Copy link

Private downstream CI failed.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14023372025.

Copy link

Private downstream CI failed.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14023633597.

Copy link

Private downstream CI failed.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14024041579.

@geier1993 geier1993 force-pushed the feature/add-libfmt branch from 23e0ccb to d89bddd Compare March 24, 2025 07:44
Copy link

Private downstream CI failed.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14029932450.

Copy link

Private downstream CI failed.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14029984133.

Copy link

Private downstream CI failed.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14031334889.

Copy link

Private downstream CI failed.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14031516324.

Copy link

Private downstream CI failed.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14158617423.

Copy link

Private downstream CI failed.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14158843892.

Copy link

Private downstream CI failed.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14170190234.

@geier1993
Copy link
Contributor

So the find_dependency stuff is working properly now - also from a bundle.

Now we just need to see why the pyfdb runner with gnu8.5 is using the lib64/ instead of lib/ path

Copy link

github-actions bot commented Apr 3, 2025

Private downstream CI failed.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14241788931.

Copy link

github-actions bot commented Apr 3, 2025

Private downstream CI failed.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14242362353.

@pmaciel
Copy link
Member

pmaciel commented Apr 3, 2025

In a separate PR I'm proposing to use git submodules as a way to integrate third-party libraries functionality without copying and with direct access to said libraries versioning. We already have the example of using xxHash, and for libfmt I'm of the opinion of not copying code into eckit, especially if that means stripping functionality (documentation, testing).

if you find you have opinions on this please add yourself to review the PR!

@Ozaq Ozaq force-pushed the feature/add-libfmt branch from 6161a4a to 91e6127 Compare April 8, 2025 16:05
Copy link

github-actions bot commented Apr 8, 2025

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14339273479.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443697582.

7 similar comments
Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443697582.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443697582.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443697582.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443697582.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443697582.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443697582.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci-hpc
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443697582.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

16 similar comments
Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

Copy link

Private downstream CI succeeded.
Workflow name: private-downstream-ci
View the logs at https://github.com/ecmwf/private-downstream-ci/actions/runs/14443701903.

Copy link
Contributor

@simondsmart simondsmart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm very happy with this, modulo the comment that I don't understand why we are creating essentially a second contrib directory. Please include the code in the same way as other contexts!

@@ -31,6 +31,7 @@ jobs:
clang_format: true
clang_format_ignore: |
src/eckit/contrib
third-party
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we creating a second directory here. I don't see libfmt as a dependency being conceptually meaningfully different to xxHash. They are both third party contribs. Please put in the 'original' location.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, it is a third-party library ... same for xxHash. The naming contribution is just not appropriate for these because they are no contributions from externals provided just for this purpose.

Also note that in #187 it is to discussed to move them to third_party directory as well, and also use git submodules properly...

However, we want to keep the discussion with the git submodules separate. That's why this library is still vendored.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semantics... contrib is the directory for external contribution; personally I never heard of the term "vendored" before in this context either.
If instead this is agreed to go to a new directory third-party, then I propose to also move xxHash to this third-party to be consistent at least.

#include <limits>

#include "eckit/exception/Exceptions.h"
#include "eckit/types/FloatCompare.h"

using std::fpclassify;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems unrelated.

We weren't using the std:: ones as we had issues on some compiler combinations. Not sure I want to couple this change in this context?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we included the eckit/format/Format.h in eckit/log/Log.h - this was then included in FloatCompare

Here some macros to math functions were used to avoid having to prefix std:: ... however as they were declared as macros, they caused a lot of conflicts with the included libtfmt headers.

In that sense using the using std::function; approach is much cleaner. I'm not sure if this approach was properly tested before? Moreover we are now using C++17 - I'd be really supprised if there are still compilers complaining about these things. Also the CI passes.

Copy link
Member

@wdeconinck wdeconinck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a nice addition, thanks @Ozaq !

I wonder the usefulness of adding these macros currently:

  • eckit_format
  • eckit_format_cc
  • eckit_format_to
  • eckit_format_to_cc

Adding macros should be avoided, and I understand these can improve performance from the comments, but I don't think we really need to worry about this. So far we have managed without this. Logging should not be in a critical loop anyways.
If this appears to be a requirement we could add this later?

Then, I don't know why it was chosen to call the new functions eckit::str_format (instead of eckit::format) and eckit::str_format_to (instead of eckit::format_to) rather than the similar names in libfmt and C++20 std::format and std::format_to.
Naming the functions similar will be helpful to port later to C++20.

Is the ENABLE_FORMAT procedure similar for libfmt and C++20's format?
Looking at this blogpost it looks a bit different, but I have not investigated further.

* For most cases it is encouraged to use the macro `eckit_format`. It will perform compile time checks.
*
* For a very specific timecritical cases `eckit_format_cc` can be used to produce very optimized formatting code.
* Disadvantag: more binary code
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Disadvantag: more binary code
* Disadvantage: more binary code

Comment on lines +34 to +36
#define ENABLE_FORMAT(typ) \
template <> \
struct fmt::formatter<typ> : fmt::ostream_formatter {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This macro should not be so generic but be prefixed with ECKIT_

@geier1993
Copy link
Contributor

I think this is a nice addition, thanks @Ozaq !

I wonder the usefulness of adding these macros currently:

  • eckit_format
  • eckit_format_cc
  • eckit_format_to
  • eckit_format_to_cc

Adding macros should be avoided, and I understand these can improve performance from the comments, but I don't think we really need to worry about this. So far we have managed without this. Logging should not be in a critical loop anyways. If this appears to be a requirement we could add this later?

Then, I don't know why it was chosen to call the new functions eckit::str_format (instead of eckit::format) and eckit::str_format_to (instead of eckit::format_to) rather than the similar names in libfmt and C++20 std::format and std::format_to. Naming the functions similar will be helpful to port later to C++20.

Is the ENABLE_FORMAT procedure similar for libfmt and C++20's format? Looking at this blogpost it looks a bit different, but I have not investigated further.

The function eckit::format already exists to specify manipulators on output streams... It is used at various places... That's why the prefix str_ was added

@wdeconinck
Copy link
Member

The function eckit::format already exists to specify manipulators on output streams... It is used at various places... That's why the prefix str_ was added

ugh... bummer.
Could you be more specific which function or places this is used?

@Ozaq
Copy link
Contributor Author

Ozaq commented Apr 29, 2025

I think this is a nice addition, thanks @Ozaq !

I wonder the usefulness of adding these macros currently:

  • eckit_format
  • eckit_format_cc
  • eckit_format_to
  • eckit_format_to_cc

Adding macros should be avoided, and I understand these can improve performance from the comments, but I don't think we really need to worry about this. So far we have managed without this. Logging should not be in a critical loop anyways. If this appears to be a requirement we could add this later?

About *_format:
The primary concern of these macros is the compiletime checking of the format expression, which can only be done on constexpr strings, hence if this was a function the format string argument would no loner be constexpr.

The compiletime checks guard against type mismatches and providing additional arguments to the format call. E.g. format("just a single value: {}", 1 ,2) would not compile, if this is done at runtime an exception would be thrown.

I believe once we have format available this will be used a lot when building messages for exceptions, e.g. "Oh noes x[{}] != y[{}]". Unless all these error code paths are executed they might fail at runtime and instead of throwing the exception we want, we end up with a fmt exception about faulty formatting instead.

I am personally against providing eckit wrappers for fmt but this was what I understood as a requirement. I would consider the effort to manually update from fmt to std once we are on c++23 to be minor as there are no structural differences and the vast majority of call-sites can be regexreplaced at some point. Further I believe continued use of fmt over std::format beyond c++23 might bring the benefit of continued API evolution over time.

About *_cc:

These two macro are primarily concerned with performance and offer a compiletime/codesize vs. runtime allocation behavior tradeoff.

They are primarily added to highlight that they exist and enable to log from some somewhat performance critical code (apply your own judgment, Its sill not free even if cheaper)

And the PR is still at this point a basis for discussion.

Then, I don't know why it was chosen to call the new functions eckit::str_format (instead of eckit::format) and eckit::str_format_to (instead of eckit::format_to) rather than the similar names in libfmt and C++20 std::format and std::format_to. Naming the functions similar will be helpful to port later to C++20.

Because it clashes with Log.h:118 eckit::format(...) which seems to be used in a lot of our testing.

Is the ENABLE_FORMAT procedure similar for libfmt and C++20's format? Looking at this blogpost it looks a bit different, but I have not investigated further.

What is happening here is that we are using the fmt ostream_formatter (https://fmt.dev/latest/api/#stdostream-support), a formatter that is designed to make types that support the stream oper already printable with format. This fallback does not exist in C++20/23. On migration to std::format (independently of fmt) we would need to add formatters for all types (that we want to print, so a good chance is that this means for all types that already have a ostream oper) as described in the link you added.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants