Skip to content
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

adding a default format for std::chrono::time_point<std::chrono::syst… #2345

Merged
merged 12 commits into from
Jun 11, 2021
Merged

adding a default format for std::chrono::time_point<std::chrono::syst… #2345

merged 12 commits into from
Jun 11, 2021

Conversation

sunmy2019
Copy link
Contributor

@sunmy2019 sunmy2019 commented Jun 7, 2021

For #2319, I add a defalut spec when the format is {}.

Here I use function overload to avoid if constexpr or different template specializations. If you want to support more char types, you can provide with more overload resolutions, or use some template metaprogramming techniques (to restrict sizeof(Char) ).

Copy link
Contributor Author

@sunmy2019 sunmy2019 left a comment

Choose a reason for hiding this comment

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

I was presuming basic_string_view has a constructor that takes in const char (&)[N].

I look up the source today. It turns out there isn't one.

@sunmy2019
Copy link
Contributor Author

I accidently merged the upstream commits. Now I reset to 12aacc2

@sunmy2019
Copy link
Contributor Author

do you mean default?

fixed, thanks

Copy link
Contributor

@vitaut vitaut left a 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. Minor comments inline, otherwise LGTM.

include/fmt/chrono.h Outdated Show resolved Hide resolved
include/fmt/chrono.h Outdated Show resolved Hide resolved
include/fmt/chrono.h Outdated Show resolved Hide resolved
@foonathan
Copy link
Contributor

Here I use function overload to avoid if constexpr or different template specializations. If you want to support more char types, you can provide with more overload resolutions, or use some template metaprogramming techniques (to restrict sizeof(Char) ).

Note that in principle, one can use something like constexpr const CharT format[] = {'%', 'Y', '-', ... } and automatically supports every character type. I'm not sure if it's worth it, however.

@vitaut
Copy link
Contributor

vitaut commented Jun 9, 2021

Here I use function overload to avoid if constexpr or different template specializations. If you want to support more char types, you can provide with more overload resolutions, or use some template metaprogramming techniques (to restrict sizeof(Char) ).

Note that in principle, one can use something like constexpr const CharT format[] = {'%', 'Y', '-', ... } and automatically supports every character type. I'm not sure if it's worth it, however.

I don't think it'll work in this particular case because we need a static string and static constexpr is not supported in constexpr functions (sigh).

@foonathan
Copy link
Contributor

It could be made a static constexpr at class scope.

@vitaut
Copy link
Contributor

vitaut commented Jun 9, 2021

It could be made a static constexpr at class scope.

Good idea, let's do that instead of my earlier suggestion.

@sunmy2019
Copy link
Contributor Author

But using static constexpr in aab4a1b would cause failure on C++11/14, and succeed on C++17/20.
The linker complained about

undefined reference to `fmt::v7::formatter<std::chrono::time_point<std::chrono::_V2::system_clock,
std::chrono::duration<long, std::ratio<1l, 1000000000l> > >, wchar_t, void>::default_spec'

include/fmt/chrono.h Outdated Show resolved Hide resolved
@sunmy2019
Copy link
Contributor Author

sunmy2019 commented Jun 9, 2021

But using static constexpr in aab4a1b would cause failure on C++11/14, and succeed on C++17/20.
The linker complained about

undefined reference to `fmt::v7::formatter<std::chrono::time_point<std::chrono::_V2::system_clock,
std::chrono::duration<long, std::ratio<1l, 1000000000l> > >, wchar_t, void>::default_spec'

Inline variables are introduced in C++17, before C++17 we seem to have to generate the symbol somewhere.

GCC and Clang add supports for inline variables under C++17 as an extension. It seems a failure on MSVC and on older versions of GCC/Clang.

@alexezeder
Copy link
Contributor

I need to use static constexpr inline, since it's implicit inline since C++17.

static inline class variable is a C++17 feature. constexpr implies inline since C++11.
There is an example in {fmt} how to use static constexpr class variables and conform to C++11/14/17+.

FMT_API static constexpr const char digits[][2] = {

#if __cplusplus < 201703L
template <typename T> constexpr const char basic_data<T>::digits[][2];

@sunmy2019
Copy link
Contributor Author

I need to use static constexpr inline, since it's implicit inline since C++17.

static inline class variable is a C++17 feature. constexpr implies inline since C++11.
There is an example in {fmt} how to use static constexpr class variables and conform to C++11/14/17+.

FMT_API static constexpr const char digits[][2] = {

#if __cplusplus < 201703L
template <typename T> constexpr const char basic_data<T>::digits[][2];

Does this mean I need to create a chrono-inl.h to provide with both header-only library and (non header-only) library? I also need to add a translation unit like src/format.cc, and also a change to the building script.

This seems to be a huge change beyond my scope.

@alexezeder
Copy link
Contributor

This seems to be a huge change beyond my scope.

Can't we use here something like:

template<typename A, typename B>
class formatter<A, B> {
  static constexpr auto var = { /* initializer here */ };
};
template <typename A, typename B> constexpr B formatter<A, B>::var[]; 

For me, it's a big question why this code:

fmt/include/fmt/core.h

Lines 1686 to 1691 in 36c2948

static constexpr unsigned long long desc =
(is_packed ? detail::encode_types<Context, Args...>()
: detail::is_unpacked_bit | num_args) |
(num_named_args != 0
? static_cast<unsigned long long>(detail::has_named_args_bit)
: 0);

does not require any out-of-class definitions.

@sunmy2019
Copy link
Contributor Author

Can't we use here something like:

template<typename A, typename B>
class formatter<A, B> {
  static constexpr auto var = { /* initializer here */ };
};
template <typename A, typename B> constexpr B formatter<A, B>::var[]; 

Let's have a try.

@foonathan
Copy link
Contributor

does not require any out-of-class definitions.

You only need a definition if something is "ODR used". Reading a constant integer is not an ODR use, but some part of the code that deals with string literals seems to ODR use the format spec.

Can't we use here something like:

This should definitely work.

@sunmy2019
Copy link
Contributor Author

sunmy2019 commented Jun 10, 2021

Oops, I find my implementation cannot handle compile time string correctly for C++17/20.

The format will be parsed empty in

fmt::print(FMT_COMPILE("{}"), std::chrono::system_clock::now());

But it works correctly in

fmt::print(FMT_COMPILE("{:}"), std::chrono::system_clock::now());

I don't know what's behind the scene, but I guess the parse function was never entered when using {}.

So I move the assignment this->specs to the default constructor in 0f8dc8c .

I tested it on my g++-11 with C++11/14/17/20 standard.

include/fmt/chrono.h Outdated Show resolved Hide resolved
include/fmt/chrono.h Outdated Show resolved Hide resolved
include/fmt/chrono.h Outdated Show resolved Hide resolved
@vitaut vitaut merged commit f28cf33 into fmtlib:master Jun 11, 2021
@vitaut
Copy link
Contributor

vitaut commented Jun 11, 2021

Thank you!

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