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

Extend tuple features (e.g. join) to tuple-like objects #4226

Closed
correaa opened this issue Nov 2, 2024 · 12 comments
Closed

Extend tuple features (e.g. join) to tuple-like objects #4226

correaa opened this issue Nov 2, 2024 · 12 comments

Comments

@correaa
Copy link

correaa commented Nov 2, 2024

For some technical reasons I had to replicate the functionality of std::tuple in my library. (see here: https://github.com/correaa/boost-multi?tab=readme-ov-file#formatting-fmt-pretty-printing)

I would be nice that the features in fmt would work on any type that has std::get<N>, std::tuple_size, etc. in their interface.
That is, if a type is tuple like then it could be printed as a tuple.

(This will also work on any class for which structured binding is implemented).

int main() {

    std::tuple<int, int> t1(1, 2);
    fmt::print( "{}", fmt::join(t1, "x") );  // prints 1 x 2

    my_tuple<int, int> t2(1, 2);
    fmt::print( "{}", fmt::join(t2, "x") );  // doesn't work now
}

Here it is a godbolt example:

https://godbolt.org/z/Gof6f4oaE

@vitaut
Copy link
Contributor

vitaut commented Nov 4, 2024

Thanks for the suggestion. I think it's reasonable to add tuple-like object support to fmt::join and a PR would be welcome.

@correaa
Copy link
Author

correaa commented Nov 11, 2024

Does it work? I think godbolt has updated, but the library still fails for a tuple-like structure:

https://godbolt.org/z/EW4659Yzv

@phprus
Copy link
Contributor

phprus commented Nov 11, 2024

boost::multi has a standard incompatible std::tuple_size implementation.

All specializations of std::tuple_size satisfy UnaryTypeTrait with base characteristic std::integral_constant<std::size_t, N> for some N.

https://en.cppreference.com/w/cpp/utility/tuple_size

But https://gitlab.com/correaa/boost-multi/-/blob/master/include/boost/multi/detail/tuple_zip.hpp?ref_type=heads#L300 :

template<class... Ts>
struct std::tuple_size<boost::multi::detail::tuple<Ts...>> {  // NOLINT(cert-dcl58-cpp) to have structured bindings
	// cppcheck-suppress unusedStructMember
	static constexpr std::size_t value = sizeof...(Ts);
};

does not inherit std::integral_constant.

@correaa
Copy link
Author

correaa commented Nov 11, 2024

Good point, I didn't know that the type has to specifically inherit from std::integral_type, I thought it was enough to have a value static member. Inherithing from std::integral_type seems like an implementation detail.

@phprus
Copy link
Contributor

phprus commented Nov 11, 2024

https://gitlab.com/correaa/boost-multi/-/blob/master/include/boost/multi/detail/tuple_zip.hpp?ref_type=heads#L328

Adding a get function specification into namespace std is undefined behavior.
Use ADL or typle-like.get<i>() for structured binding.

@correaa
Copy link
Author

correaa commented Nov 11, 2024

Yes, the whole reimplementation of tuple was a hack to make the code work on CUDA.
And I had to throw a lot at it to make it work.

In perspective, maybe this is because of the problem you noticed, about inheriting from integral_constant.

@correaa
Copy link
Author

correaa commented Nov 12, 2024

@phprus I think getting into the namespace std was there to be able to access elements using the full syntax std::get.

  1. I think that is the only way to get elements in generic code std::get. If not what I am supposed to do? this? using std::get; get<N>(tp);?

  2. isn't one able to add specialization of some std functions?, I though std::get was one of them.

EDIT: regarding 1), I now remember the problem, in C++17, when I tried to use the ADL get<N> function, I was getting the error "use of function template name with no prior declaration in function call with explicit template arguments is a C++20 extension [-Wc++20-extensions]".
The only workaround I found was to really declare, in addition to ADL and member, a proper std::get.
Now I think this was an overkill, the other workaround in C++17 is to put a spurious using std::get; before get so the compiler has at least one candidate to work with.
This is still not perfect because it doesn't work on function signatures (e.g. -> decltype(get<0>(arg)); but it might be preferable to adding this ilegal std specializations.

@phprus
Copy link
Contributor

phprus commented Nov 12, 2024

It is undefined behavior to declare a full specialization of any standard library function template. (since C++20)

https://en.cppreference.com/w/cpp/language/extending_std

@correaa
Copy link
Author

correaa commented Nov 12, 2024

Yes, I agree.
Multi is a C++17 library and this C++20 extension mentioned in the warning comes just in time to not need the std::get specialization.

I now modified the code to not need the specialization std::get (the ADL and members were in place already).

Tomorrow I will try again with fmt to see if it works.

@correaa
Copy link
Author

correaa commented Nov 15, 2024

It works now! https://godbolt.org/z/Mdo8YYqnf

    fmt::print("{} ", fmt::join(arr.sizes(), "×"));  // prints 2×2

    fmt::print("{}\n", fmt::join(arr, "\n"));
    \\ prints
    \\ [3, 4]
    \\ [6, 7]

@correaa
Copy link
Author

correaa commented Nov 15, 2024

A 2D array can be seen as a container of container (range of ranges), so I wonder if one can control the formatting of the nested ranges.

Since this is close to supporting formatting "tables" (but not exactly), I wonder if controlling the format of nested ranges is within the scope of the library: https://stackoverflow.com/questions/79193937/control-fmt-formatting-of-nested-containers-ranges

@vitaut
Copy link
Contributor

vitaut commented Nov 19, 2024

I wonder if controlling the format of nested ranges is within the scope of the library

Discussed in #4240

mtremer pushed a commit to ipfire/ipfire-2.x that referenced this issue Feb 22, 2025

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
- Update from version 11.0.2 to 11.1.3
- Update of rootfile
- Changelog
    11.1.3
	- Fixed compilation on GCC 9.4 (fmtlib/fmt#4313).
	- Worked around an internal compiler error when using C++20 modules with GCC
	  14.2 and earlier (fmtlib/fmt#4295).
	- Worked around a bug in GCC 6 (fmtlib/fmt#4318).
	- Fixed an issue caused by instantiating `formatter<const T>`
	  (fmtlib/fmt#4303,
	  fmtlib/fmt#4325). Thanks @timsong-cpp.
	- Fixed formatting into `std::ostreambuf_iterator` when using format string
	  compilation (fmtlib/fmt#4309,
	  fmtlib/fmt#4312). Thanks @phprus.
	- Restored a constraint on the map formatter so that it correctly reports as
	  unformattable when the element is (fmtlib/fmt#4326).
	  Thanks @timsong-cpp.
	- Reduced the size of format specs (fmtlib/fmt#4298).
	- Readded `args()` to `fmt::format_context`
	  (fmtlib/fmt#4307,
	  fmtlib/fmt#4310). Thanks @Erroneous1.
	- Fixed a bogus MSVC warning (fmtlib/fmt#4314,
	  fmtlib/fmt#4322). Thanks @ZehMatt.
	- Fixed a pedantic mode error in the CMake config
	  (fmtlib/fmt#4327). Thanks @rlalik.
    11.1.2
	- Fixed ABI compatibility with earlier 11.x versions
	  (fmtlib/fmt#4292).
	- Added `wchar_t` support to the `std::bitset` formatter
	  (fmtlib/fmt#4285,
	  fmtlib/fmt#4286,
	  fmtlib/fmt#4289,
	  fmtlib/fmt#4290). Thanks @phprus.
	- Prefixed CMake components with `fmt-` to simplify usage of {fmt} via
	  `add_subdirectory` (fmtlib/fmt#4283).
	- Updated docs for meson (fmtlib/fmt#4291).
	  Thanks @trim21.
	- Fixed a compilation error in chrono on nvcc
	  (fmtlib/fmt#4297,
	  fmtlib/fmt#4301). Thanks @breyerml.
	- Fixed various warnings
	  (fmtlib/fmt#4288,
	  fmtlib/fmt#4299). Thanks @GamesTrap and @edo9300.
    11.1.1
	- Fixed ABI compatibility with earlier 11.x versions
	  (fmtlib/fmt#4278).
	- Defined CMake components (`core` and `doc`) to allow docs to be installed
	  separately (fmtlib/fmt#4276).
	  Thanks @carlsmedstad.
    11.1.0
	- Improved C++20 module support
	  (fmtlib/fmt#4081,
	  fmtlib/fmt#4083,
	  fmtlib/fmt#4084,
	  fmtlib/fmt#4152,
	  fmtlib/fmt#4153,
	  fmtlib/fmt#4169,
	  fmtlib/fmt#4190,
	  fmtlib/fmt#4234,
	  fmtlib/fmt#4239).
	  Thanks @kamrann and @Arghnews.
	- Reduced debug (unoptimized) binary code size and the number of template
	  instantiations when passing formatting arguments. For example, unoptimized
	  binary code size for `fmt::print("{}", 42)` was reduced by ~40% on GCC and
	  ~60% on clang (x86-64).
	  GCC:
	  - Before: 161 instructions of which 105 are in reusable functions
	    ([godbolt](https://www.godbolt.org/z/s9bGoo4ze)).
	  - After: 116 instructions of which 60 are in reusable functions
	    ([godbolt](https://www.godbolt.org/z/r7GGGxMs6)).
	  Clang:
	  - Before: 310 instructions of which 251 are in reusable functions
	    ([godbolt](https://www.godbolt.org/z/Ts88b7M9o)).
	  - After: 194 instructions of which 135 are in reusable functions
	    ([godbolt](https://www.godbolt.org/z/vcrjP8ceW)).
	- Added an experimental `fmt::writer` API that can be used for writing to
	  different destinations such as files or strings
	  (fmtlib/fmt#2354).
	  For example ([godbolt](https://www.godbolt.org/z/rWoKfbP7e)):
		  ```c++
		  #include <fmt/os.h>
		  void write_text(fmt::writer w) {
		    w.print("The answer is {}.", 42);
		  }
		  int main() {
		    // Write to FILE.
		    write_text(stdout);
		    // Write to fmt::ostream.
		    auto f = fmt::output_file("myfile");
		    write_text(f);
		    // Write to std::string.
		    auto sb = fmt::string_buffer();
		    write_text(sb);
		    std::string s = sb.str();
		  }
		  ```
	- Added width and alignment support to the formatter of `std::error_code`.
	- Made `std::expected<void, E>` formattable
	  (fmtlib/fmt#4145,
	  fmtlib/fmt#4148).
	  For example ([godbolt](https://www.godbolt.org/z/hrj5c6G86)):
		  ```c++
		  fmt::print("{}", std::expected<void, int>());
		  ```
		  prints
		  ```
		  expected()
		  ```
	  Thanks @phprus.
	- Made `fmt::is_formattable<void>` SFINAE-friendly
	  (fmtlib/fmt#4147).
	- Added support for `_BitInt` formatting when using clang
	  (fmtlib/fmt#4007,
	  fmtlib/fmt#4072,
	  fmtlib/fmt#4140,
	  fmtlib/fmt#4173,
	  fmtlib/fmt#4176).
	  For example ([godbolt](https://www.godbolt.org/z/KWjbWec5z)):
		  ```c++
		  using int42 = _BitInt(42);
		  fmt::print("{}", int42(100));
		  ```
	  Thanks @Arghnews.
	- Added the `n` specifier for tuples and pairs
	  (fmtlib/fmt#4107). Thanks @someonewithpc.
	- Added support for tuple-like types to `fmt::join`
	  (fmtlib/fmt#4226,
	  fmtlib/fmt#4230). Thanks @phprus.
	- Made more types formattable at compile time
	  (fmtlib/fmt#4127). Thanks @AnthonyVH.
	- Implemented a more efficient compile-time `fmt::formatted_size`
	  (fmtlib/fmt#4102,
	  fmtlib/fmt#4103). Thanks @phprus.
	- Fixed compile-time formatting of some string types
	  (fmtlib/fmt#4065). Thanks @torshepherd.
	- Made compiled version of `fmt::format_to` work with
	  `std::back_insert_iterator<std::vector<char>>`
	  (fmtlib/fmt#4206,
	  fmtlib/fmt#4211). Thanks @phprus.
	- Added a formatter for `std::reference_wrapper`
	  (fmtlib/fmt#4163,
	  fmtlib/fmt#4164). Thanks @yfeldblum and @phprus.
	- Added experimental padding support (glibc `strftime` extension) to `%m`, `%j`
	  and `%Y` (fmtlib/fmt#4161). Thanks @KKhanhH.
	- Made microseconds formatted as `us` instead of `µs` if the Unicode support is
	  disabled (fmtlib/fmt#4088).
	- Fixed an unreleased regression in transcoding of surrogate pairs
	  (fmtlib/fmt#4094,
	  fmtlib/fmt#4095). Thanks @phprus.
	- Made `fmt::appender` satisfy `std::output_iterator` concept
	  (fmtlib/fmt#4092,
	  fmtlib/fmt#4093). Thanks @phprus.
	- Made `std::iterator_traits<fmt::appender>` standard-conforming
	  (fmtlib/fmt#4185). Thanks @CaseyCarter.
	- Made it easier to reuse `fmt::formatter<std::string_view>` for types with
	  an implicit conversion to `std::string_view`
	  (fmtlib/fmt#4036,
	  fmtlib/fmt#4055). Thanks @Arghnews.
	- Made it possible to disable `<filesystem>` use via `FMT_CPP_LIB_FILESYSTEM`
	  for compatibility with some video game console SDKs, e.g. Nintendo Switch SDK
	  (fmtlib/fmt#4257,
	  fmtlib/fmt#4258,
	  fmtlib/fmt#4259). Thanks @W4RH4WK and @phprus.
	- Fixed compatibility with platforms that use 80-bit `long double`
	  (fmtlib/fmt#4245,
	  fmtlib/fmt#4246). Thanks @jsirpoma.
	- Added support for UTF-32 code units greater than `0xFFFF` in fill
	  (fmtlib/fmt#4201).
	- Fixed handling of legacy encodings on Windows with GCC
	  (fmtlib/fmt#4162).
	- Made `fmt::to_string` take `fmt::basic_memory_buffer` by const reference
	  (fmtlib/fmt#4261,
	  fmtlib/fmt#4262). Thanks @sascha-devel.
	- Added `fmt::dynamic_format_arg_store::size`
	  (fmtlib/fmt#4270). Thanks @hannes-harnisch.
	- Removed the ability to control locale usage via an undocumented
	  `FMT_STATIC_THOUSANDS_SEPARATOR` in favor of `FMT_USE_LOCALE`.
	- Renamed `FMT_EXCEPTIONS` to `FMT_USE_EXCEPTIONS` for consistency with other
	  similar macros.
	- Improved include directory ordering to reduce the chance of including
	  incorrect headers when using multiple versions of {fmt}
	  (fmtlib/fmt#4116). Thanks @cdzhan.
	- Made it possible to compile a subset of {fmt} without the C++ runtime.
	- Improved documentation and README
	  (fmtlib/fmt#4066,
	  fmtlib/fmt#4117,
	  fmtlib/fmt#4203,
	  fmtlib/fmt#4235). Thanks @zyctree and @nikola-sh.
	- Improved the documentation generator (fmtlib/fmt#4110,
	  fmtlib/fmt#4115). Thanks @rturrado.
	- Improved CI (fmtlib/fmt#4155,
	  fmtlib/fmt#4151). Thanks @phprus.
	- Fixed various warnings and compilation issues
	  (fmtlib/fmt#2708,
	  fmtlib/fmt#4091,
	  fmtlib/fmt#4109,
	  fmtlib/fmt#4113,
	  fmtlib/fmt#4125,
	  fmtlib/fmt#4129,
	  fmtlib/fmt#4130,
	  fmtlib/fmt#4131,
	  fmtlib/fmt#4132,
	  fmtlib/fmt#4133,
	  fmtlib/fmt#4144,
	  fmtlib/fmt#4150,
	  fmtlib/fmt#4158,
	  fmtlib/fmt#4159,
	  fmtlib/fmt#4160,
	  fmtlib/fmt#4170,
	  fmtlib/fmt#4177,
	  fmtlib/fmt#4187,
	  fmtlib/fmt#4188,
	  fmtlib/fmt#4194,
	  fmtlib/fmt#4200,
	  fmtlib/fmt#4205,
	  fmtlib/fmt#4207,
	  fmtlib/fmt#4208,
	  fmtlib/fmt#4210,
	  fmtlib/fmt#4220,
	  fmtlib/fmt#4231,
	  fmtlib/fmt#4232,
	  fmtlib/fmt#4233,
	  fmtlib/fmt#4236,
	  fmtlib/fmt#4267,
	  fmtlib/fmt#4271).
	  Thanks @torsten48, @Arghnews, @tinfoilboy, @aminya, @Ottani, @zeroomega,
	  @c4v4, @kongy, @vinayyadav3016, @sergio-nsk, @phprus and @YexuanXiao.

Signed-off-by: Adolf Belka <adolf.belka@ipfire.org>
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants