Reputation: 5138
I am trying to write a custom formatter to help me print vectors. I am attempting to maintain the format specifiers so that each item in the vector is formatted the same way.
I have taken most of my inspiration from this tutorial, and the docs
#include <fmt/core.h>
#include <fmt/format.h>
template<typename ValueType>
struct fmt::formatter<std::vector<ValueType>> : fmt::formatter<ValueType>
{
std::string formatString;
// Hack, copy the original format text into a std::string
constexpr auto parse(format_parse_context& ctx)
{
formatString= "{:";
for (auto iter = std::begin(ctx); iter != std::end(ctx); ++iter) {
char c = *iter;{
formatString += c;
}
if (c == '}') {
return iter;
}
}
return std::end(ctx);
}
template <typename FormatContext>
auto format(const std::vector<ValueType>& container, FormatContext& context)
{
auto&& out = context.out();
format_to(out, "{{");
typename std::vector<ValueType>::size_type count = 0;
const typename std::vector<ValueType>::size_type size = container.size();
for (const auto& item : container) {
// Use the copied format string, but really want to delegate the formatting to superclass...
format_to(out, formatString, item);
if (++count < size) {
format_to(out, ", ");
}
}
return format_to(out, "}}");
}
};
int main()
{
fmt::print("{:.3f}\n", std::vector{ 0.0, 321.123, 654398.4328, -0.0000000000012345, 2374651273.7236457862345});
fmt::print("{:.1e}\n", std::vector{ 0.0, 321.123, 654398.4328, -0.0000000000012345, 2374651273.7236457862345});
return 0;
}
Which outputs:
{0.000, 321.123, 654398.433, -0.000, 2374651273.724}
{0.0e+00, 3.2e+02, 6.5e+05, -1.2e-12, 2.4e+09}
It seems overly clunky and inefficient to copy the format string just so I can feed it back into another fmt::format
call, especially when the extended class : fmt::formatter<ValueType>
is already providing us with a perfectly valid parse
function internally, (which I have re-implemented in this example just to get the desired output in a hacky way).
I really want to remove the custom parse implementation and replace the line
format_to(out, formatString, item);
with
format_to(out, fmt::formatter<ValueType>::format(item, context))
except it isn't valid/doesn't compile.
What is the correct way to do this?
Note: I am completely aware of the pointlessness of extending the type in my example, and that I could have it as a local variable however I am trying to reuse the functionality of the class, so extending it feels like the right direction, even if I haven't found the solution yet.
A list of all the other examples I have found which haven't helped me yet:
format
with a new format string, despite the caller having already specified one and there being an existing parse function for the type...)Upvotes: 5
Views: 1093
Reputation: 2084
You can get rid of your parse
implementation and use the inherited function, and use fmt::formatter<ValueType>::format(item, context)
inside your format
to output each item (godbolt demo):
#include <fmt/core.h>
#include <fmt/format.h>
template<typename ValueType>
struct fmt::formatter<std::vector<ValueType>> : fmt::formatter<ValueType>
{
template <typename FormatContext>
auto format(const std::vector<ValueType>& container, FormatContext& context)
{
auto&& out = context.out();
format_to(out, "{{");
bool first = true;
for (const auto& item : container) {
if (first) {
first = false;
} else {
format_to(out, ", ");
}
fmt::formatter<ValueType>::format(item, context);
}
return format_to(out, "}}");
}
};
int main()
{
fmt::print("{:.3f}\n", std::vector{ 0.0, 321.123, 654398.4328, -0.0000000000012345, 2374651273.7236457862345});
fmt::print("{:.1e}\n", std::vector{ 0.0, 321.123, 654398.4328, -0.0000000000012345, 2374651273.7236457862345});
return 0;
}
You can see other examples of this pattern in the docs under Formatting User-defined Types.
Upvotes: 4