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...)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.