diff --git a/src/utils/algorithm.hpp b/src/utils/algorithm.hpp index 1c493e49f..a5063513b 100644 --- a/src/utils/algorithm.hpp +++ b/src/utils/algorithm.hpp @@ -11,6 +11,32 @@ namespace utils { +/** + * Outputs a collection of items to the given stream, separating them with the + * given delimiter. + * + * @param stream Destination stream. + * @param first Starting iterator of collection which items are going to be + * printed. + * @param last Ending iterator of the collection. + * @param delim Delimiter that is put between items. + * @param streamer Function which accepts a TStream and an item and streams the + * item to the stream. + */ +template <typename TStream, typename TIterator, typename TStreamer> +inline void PrintIterable(TStream *stream, TIterator first, TIterator last, + const std::string &delim = ", ", + TStreamer streamer = {}) { + if (first != last) { + streamer(*stream, *first); + ++first; + } + for (; first != last; ++first) { + *stream << delim; + streamer(*stream, *first); + } +} + /** * Outputs a collection of items to the given stream, separating them with the * given delimiter. @@ -25,14 +51,7 @@ template <typename TStream, typename TIterable, typename TStreamer> inline void PrintIterable(TStream &stream, const TIterable &iterable, const std::string &delim = ", ", TStreamer streamer = {}) { - bool first = true; - for (const auto &item : iterable) { - if (first) - first = false; - else - stream << delim; - streamer(stream, item); - } + PrintIterable(&stream, iterable.begin(), iterable.end(), delim, streamer); } /** diff --git a/tests/unit/utils_algorithm.cpp b/tests/unit/utils_algorithm.cpp index 39e533113..a21517ead 100644 --- a/tests/unit/utils_algorithm.cpp +++ b/tests/unit/utils_algorithm.cpp @@ -1,4 +1,6 @@ #include <list> +#include <map> +#include <sstream> #include <string> #include <vector> @@ -11,6 +13,40 @@ using vec = std::vector<std::string>; using namespace std::string_literals; using namespace utils; +TEST(Algorithm, PrintIterable) { + // Checkss both variants of the function (using iterators and collections) + auto check = [](const std::vector<int> &iterable, + const std::string &expected_output) { + { + std::ostringstream oss; + PrintIterable(oss, iterable, ", "); + EXPECT_EQ(oss.str(), expected_output); + } + { + auto streamer = [](auto &stream, const auto &item) { stream << item; }; + std::ostringstream oss; + PrintIterable(&oss, iterable.begin(), iterable.end(), ", ", streamer); + EXPECT_EQ(oss.str(), expected_output); + } + }; + + check(std::vector<int>{1, 2, 3, 4}, "1, 2, 3, 4"); + check(std::vector<int>{1}, "1"); + check(std::vector<int>{}, ""); + + { + // Use custom streamer + auto map_streamer = [](auto &stream, const auto &item) { + stream << item.first << ": " << item.second; + }; + std::ostringstream oss; + std::map<std::string, std::string> map; + map.emplace("a", "x"); + map.emplace("b", "y"); + PrintIterable(&oss, map.begin(), map.end(), ", ", map_streamer); + EXPECT_EQ(oss.str(), "a: x, b: y"); + } +} TEST(Algorithm, Reversed) { EXPECT_EQ(Reversed(""s), ""s);