I'm writing approval tests using the excellent ApprovalTests.cpp library. This library automates generation of "snapshots" of results from a function. Snapshots are generated serializing results of type T
to a file using the ostream& operator<< (T val)
.
This operator has always been the C++ convention for formatting some value to a textual representation. While primitive types support this operator and you can write your own implementation for custom types, there isn't a standard implementation for STL containers like std::vector
.
You can implement your own, even using other libraries like fmt
or pprint
. Here are some example with similar outputs. I use the generic type STREAM
as parameter instead of the concrete type ostream
as recommended by ApprovalTests.cpp but the idea doesn't change.
for loop
template <typename STREAM, typename T> STREAM& operator<<(STREAM& os, const std::vector<T>& vec) {
os << "[";
for (const auto& x : vec) {
os << x << ", ";
}
os << "]";
return os;
}
ostream_iterator
template <typename T> std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) {
using namespace std;
os << "[";
copy(v.begin(), v.end(), ostream_iterator<T>(os, ", "));
os << "]";
return os;
}
fmt
template <typename STREAM, typename T> STREAM& operator<<(STREAM& os, const std::vector<T>& vec) {
fmt::print(os, "[{}]", fmt::join(vec, ", "));
return os;
}
With <fmt/ranges.h
header:
template <typename STREAM, typename T> STREAM& operator<<(STREAM& os, const std::vector<T>& vec) {
fmt::print(os, "{}", vec);
return os;
}
Pretty print
https://github.com/p-ranav/pprint
template <typename STREAM, typename T> STREAM& operator<<(STREAM& os, const std::vector<T>& vec) {
pprint::PrettyPrinter printer{os};
printer.print(vec);
return os;
}
cxx-prettyprint
Just include prettyprint.hpp and it works for STL containers.
This seems to be the simplest solution, but has the same problem as other solutions, it may break other code.
A convention?
After some experience with Rust, I find tedious to do this for every C++ STL container. Doing this may break other code where, for example, the same operator has been overloaded for vector.
In Rust you can just add #[Debug]
over the struct
you want to format to text and it can be automatically converted to a textual representation, or implement the trait yourself if you need some non-canonical representation. It is responsibility of a struct author to define its Debug
implementation. This is why every container in the Rust standard library has its own Debug
implementation.
I'm asking if some convention exists in C++ or there is some similar proposal for the standard. It could be useful for approval tests, like in my case, but also for logging or debugging (the debugger could use this formatter to show a variable value to the user).