One thing I like of iostreams is that the standard syntax supports passing the file object in depth. If you have a custom type and want to print it, you just implement operator<<(), get a reference to the std::ostream and do what you want with it. Using printf you either have to first write to a temporary string and then print it (which requires more memory and more time to go through it, if the representation of the object is big) or break the flow anyway with something like:
printf("The object is ");
print_object(object, stdout);
printf("\n");
Does fmt support printing a custom type without breaking the format string and without using a temporary string?
fmt does this. For a given object, you specialize the “formatter” class. The formatter class parses the format string and stores the contents of the formatter string. It then writes out the formatted string to an output iterator.
fmt has a “buffer” class which is used as an interface between formatters and their two primary use cases, which are formatting to strings and formatting to files. A buffer is an abstract base class which exposes a pointer to a region of memory and has a virtual function to flush the output (sort of). When you call fmt::format or fmt::print, you’re getting std::back_insert_iterator for a buffer.
I would describe this as “surgical usage of a virtual function” because it is used exactly in a place where it is not called often (you mostly write to a buffer, and flush it less often) and its use reduces the number of template instantiations in your project. That said, the ergonomics for custom formatters is not great.
It may look like a lot to implement, but it worked flawlessly for me several years ago for a simple case, just by copying and pasting the example code there.