-
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
flexible std::complex formatter #1467
Comments
Looks interesting. I don't think that it needs to be in the core library considering that |
Ugh, of course that has a bug in it -- here's a link instead for future passers-by : https://gitlab.com/tesch1/cppduals/blob/master/duals/dual#L1276 at least they'll land somewhere close to a more tested version of the above. Just a side note, in the above, I did a little hacky re-parsing of the fmt string when inheriting from a standard formatter: it would have been nice if the specs_ member was protected rather than private, to allow reuse of parse() from custom formatters that subclass On the accusation of niche -- I have to defend std::complex's honor a little here :o) a lot of people do use std::complex, probably more than use libfmt, even. A surprising amount of what Feynmann would have called science is done in C++, and I'd wager a limb that a lot of them also dont love the default I apologize for the noise, but this seems like a good place to more thoroughly collect some complex number formatting options. A brief survey:
The variables :
edit: clarify that c++ meant iostreams |
P1636 didn't make it into C++20, so there is an opportunity to improve the default formatting for complex in |
Yeah, would be glad to. I guess if I complain I should be willing to try to improve things too. Another data point: python's discussion on the issue: https://bugs.python.org/issue1588 Although, I edit: an ounce of humility |
Hey @tesch1, are you still interested in writing a proposal? |
hey, yeah, theoretically I am.. practically I dont have time. I started on it a bit here: https://github.com/tesch1/complex_fmt_scn if someone wants to take over that's perfectly ok w/ me. |
I think the paper already contains all the important pieces. We don't really need a wording for the first revision. I can add examples, submit and champion it on your behalf. |
Sounds great to me! |
@tesch1 Thanks for the legwork. I ran into a snag, ASan throws a fit + segtault unless I test the
Repro: #include <complex>
#include <vector>
#include <numeric>
#include <fmt/ranges.h>
template <typename T, typename Char>
struct fmt::formatter<std::complex<T>, Char> : public fmt::formatter<T, Char> {
typedef fmt::formatter<T, Char> base;
enum style { expr, star, pair } style_ = expr;
internal::dynamic_format_specs<Char> specs_;
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
using handler_type =
internal::dynamic_specs_handler<format_parse_context>;
auto type = internal::type_constant<T, Char>::value;
internal::specs_checker<handler_type> handler(handler_type(specs_, ctx),
type);
auto it = ctx.begin();
/*if (it != ctx.end())*/ { // uncomment to remove SEGV
switch (*it) {
case '$': style_ = style::expr; ctx.advance_to(++it); break;
case '*': style_ = style::star; ctx.advance_to(++it); break;
case ',': style_ = style::pair; ctx.advance_to(++it); break;
default: break;
}
}
if (style_ != style::pair)
parse_format_specs(ctx.begin(), ctx.end(), handler);
// todo: fixup alignment
return base::parse(ctx);
}
template <typename FormatCtx>
auto format(const std::complex<T>& x, FormatCtx& ctx)
-> decltype(ctx.out()) {
format_to(ctx.out(), "(");
if (x.real() || !x.imag())
base::format(x.real(), ctx);
if (x.imag()) {
if (style_ == style::pair)
format_to(ctx.out(), ",");
else if (x.real() && x.imag() >= 0 && specs_.sign != sign::plus)
format_to(ctx.out(), "+");
base::format(x.imag(), ctx);
if (style_ != style::pair) {
if (style_ == style::star)
format_to(ctx.out(), "*i");
else
format_to(ctx.out(), "i");
if (std::is_same<typename std::decay<T>::type, float>::value)
format_to(ctx.out(), "f");
if (std::is_same<typename std::decay<T>::type,
long double>::value)
format_to(ctx.out(), "l");
}
}
return format_to(ctx.out(), ")");
}
};
int main() {
using V = std::complex<double>;
std::vector<V> v{ { 1, 1 }, { 2, 2 } };
auto r = std::accumulate(v.begin(), v.end(), V{});
fmt::print("Sum({}) -> {}\n", v, r);
}
|
I made minor corrections, added examples and rudimentary wording: https://fmt.dev/papers/d2197r0.html. If there are no objections I'll submit this to the next mailing. |
lgtm |
@vitaut I was quite interested in the paper you've cited, however the link is dead. Would you be able to provide it for me? I am interested in using fmt in a library for numerical analysis and printing complex numbers with as much precision as possible would be super helpful. |
It's probably this one: |
@mwinterb please correct me should I be wrong, but there is no explicit implementation of
in this paper, is there? |
@orlandini correct - the paper just defines the problem and the proposed interface - not the implementation. There is an implementation (not necessarily coherent with the paper!) here https://gitlab.com/tesch1/cppduals/blob/master/duals/dual#L1379-1452 with some rough tests here https://gitlab.com/tesch1/cppduals/-/blob/master/tests/test_fmt.cpp#L40-151 (line numbers may shift as that library changes). I'm more or less passively maintaining this - it could certainly be improved and revised along with the paper towards something more perfect and beautiful; I can chip in, but I dont currently have Copious Free Time(TM) to push it forward. |
@tesch1 thanks a lot for the attention! I will take a look and see what I can do. |
Hi all, I found the code to implement a formatter for complex numbers very usefull. Unfortunately it does not compile anymore with fmt 10.0.0. (internal::dynamic_specs_handler does not exist anymore, just to start with) I don't understand the code, nor fmt's internals well enough to fix this myself. Is anybody here able to provide an updated code snipped? (Or help me otherwise) :-) Cheers |
You can use existing formatters such as Lines 2662 to 2664 in dd17f89
|
@vitaut thanks. I managed modify the code (https://gitlab.com/tesch1/cppduals/blob/master/duals/dual#L1331) such that it compiles and does what I want (at least it seems like this) In case anybody finds this useful: template<typename T, typename Char>
struct fmt::formatter<std::complex<T>, Char> : public fmt::formatter<T,Char>
{
private:
typedef fmt::formatter<T, Char> base;
enum style
{
expr,
star,
pair
} style_ = expr;
detail::dynamic_format_specs<Char> specs_;
public:
template<typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char*
{
auto it = ctx.begin();
if (it != ctx.end())
{
switch (*it)
{
case '$':
style_ = style::expr;
ctx.advance_to(++it);
break;
case '*':
style_ = style::star;
ctx.advance_to(++it);
break;
case ',':
style_ = style::pair;
ctx.advance_to(++it);
break;
default:
break;
}
}
auto type = detail::type_constant<T, Char>::value;
auto end = detail::parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, type);
if (type == detail::type::char_type)
detail::check_char_specs(specs_);
return end;
}
template<typename FormatContext>
FMT_CONSTEXPR auto format(const std::complex<T>& x, FormatContext& ctx) const
-> decltype(ctx.out())
{
format_to(ctx.out(), "(");
if (style_ == style::pair)
{
base::format(x.real(), ctx);
format_to(ctx.out(), ",");
base::format(x.imag(), ctx);
return format_to(ctx.out(), ")");
}
if (x.real() || !x.imag())
base::format(x.real(), ctx);
if (x.imag())
{
if (x.real() && x.imag() >= 0 && specs_.sign != sign::plus)
format_to(ctx.out(), "+");
base::format(x.imag(), ctx);
if (style_ == style::star)
format_to(ctx.out(), "*i");
else
format_to(ctx.out(), "i");
if (std::is_same<typename std::decay<T>::type, float>::value)
format_to(ctx.out(), "f");
if (std::is_same<typename std::decay<T>::type, long double>::value)
format_to(ctx.out(), "l");
}
return format_to(ctx.out(), ")");
}
}; Note that I don't actually understand this code... Improvements welcome. |
The default format for |
So... c++20 will have a formatter for std::complex (https://www.zverovich.net/2019/07/23/std-format-cpp20.html, https://wg21.link/p1636r0). In the N years between now and when that's widely available, is there something that will vaguely approximate what the standard will be? is there a proposed implementation yet? google doesn't find me anything, so...
I've come up with this, is it ok? Any ideas on how to improve it? It doesnt handle width and align properly yet. Wanted to share it at least, because I couldn't find anything like it, and hopefully someone can improve it....
If this is totally uninteresting feel free to close the issue, at least the next person searching for a std::complex formatter will be able to find a starting place :)
The text was updated successfully, but these errors were encountered: