implement STL pretty printing using Boost HOF

Viewed 127

pretty print container of arbitrary type

The following code of few lines provides same output as prettyprint.hpp in question, but the output stream is limited to std::cout.

How to rewrite those code using boost::hof to provide print(std::ostream&, ...) like interface?

#include <iostream>
#include <boost/hof.hpp>

BOOST_HOF_STATIC_LAMBDA_FUNCTION(simple_print) = //boost::hof::proj(
    boost::hof::fix(boost::hof::first_of(
        [](auto, const auto& x) -> decltype(std::cout << x, void()) {
          std::cout << x;
        },
        [](auto self, const auto& range)
         -> decltype(self(*std::begin(range)), void()) {
          bool sep = false;
          std::cout << '{';
          for (const auto& x : range) {
            if (sep)
              std::cout << ',';
            else
              sep = true;
            self(x);
          }
          std::cout << '}';
        },
        [](auto self, const auto& tuple) {
          using namespace boost::hof;
          std::cout << '(';
          bool sep = false;
          unpack(proj([&](const auto& i) {
            if (sep)
              std::cout << ',';
            else
              sep = true;
            self(i);
          }))(tuple);
          std::cout << ')';
        }));//})));

template <typename... Args>
void print(Args&&... args) {
  simple_print(std::make_tuple(std::forward<Args>(args)...));
}


//---- user code ---
struct XX {
  int n = 0;

  friend std::ostream& operator<<(std::ostream& os, const XX& o) {
    return os << o.n << "XX";
  }
};

int main() {

  std::vector v                = {1, 2, 3, 4};
  std::map<std::string, int> m = {{"a", 30}, {"bb", 31}, {"ccc", 32}};

  auto t  = std::make_tuple(6, 7, 8, 9);
  auto t2 = std::make_tuple(11, std::ref(v), t);
  auto t3 = std::make_tuple(t2, std::vector{1234, 23, 2, 3, 3}, "abc",
      std::vector{
          std::vector{11, 12, 13}, std::vector{15, 16, 17}, std::vector{19}});
  print(t3, "xxxx", 55, m, std::vector<std::string>{"x"}, XX{66});
  // (((11, [1, 2, 3, 4], (6, 7, 8, 9)), [1234, 23, 2, 3, 3], abc, [[11, 12,
  // 13], [15, 16, 17], [19]]), xxxx, 55, [(a, 30), (bb, 31), (ccc, 32)], [x],
  // 66XX)
}
1 Answers

Just adding a parameter to pass the ostream would suffice:

BOOST_HOF_STATIC_LAMBDA_FUNCTION(simple_print_ex) = boost::hof::fix(
    boost::hof::first_of(
        [](auto, auto& os, const auto &x) -> decltype(os << x, void()) { os << x; },
        [](auto self, auto& os, const auto &range) -> decltype(self(os, *std::begin(range)), void()) {
            bool sep = false;
            os << '{';
            for (const auto &x : range) {
                sep = !sep || os << ',';
                self(os, x);
            }
            os << '}';
        },
        [](auto self, auto& os, const auto &tuple) {
            using namespace boost::hof;
            os << '(';
            bool sep = false;
            unpack(proj([&](const auto &i) {
                sep = !sep || os << ',';
                self(os, i);
            }))(tuple);
            os << ')';
        }));

template <typename Ostream, typename... Args> void print_ex(Ostream& os, Args &&... args) { simple_print_ex(os, std::make_tuple(std::forward<Args>(args)...)); }

Now you can use it like so:

Live On Wandbox

std::ofstream ofs("test.txt");
print_ex(ofs, t3, "xxxx", 55, m, std::vector<std::string>{ "x" }, XX{ 66 });
ofs << "\n";

Of course, the old print can be a trivial forwarding wrapper now:

template <typename... Args> void print(Args &&... args) {
    print_ex(std::cout, std::forward<Args>(args)...);
}

Listing

Live On Wandbox

#include <boost/hof.hpp>
#include <iostream>

BOOST_HOF_STATIC_LAMBDA_FUNCTION(simple_print_ex) = boost::hof::fix(
    boost::hof::first_of(
        [](auto, auto& os, const auto &x) -> decltype(os << x, void()) { os << x; },
        [](auto self, auto& os, const auto &range) -> decltype(self(os, *std::begin(range)), void()) {
            bool sep = false;
            os << '{';
            for (const auto &x : range) {
                sep = !sep || os << ',';
                self(os, x);
            }
            os << '}';
        },
        [](auto self, auto& os, const auto &tuple) {
            using namespace boost::hof;
            os << '(';
            bool sep = false;
            unpack(proj([&](const auto &i) {
                sep = !sep || os << ',';
                self(os, i);
            }))(tuple);
            os << ')';
        }));

template <typename Ostream, typename... Args> void print_ex(Ostream& os, Args &&... args) { simple_print_ex(os, std::make_tuple(std::forward<Args>(args)...)); }
template <typename... Args> void print(Args &&... args) { print_ex(std::cout, std::forward<Args>(args)...); }

//---- user code ---
struct XX {
    int n = 0;

    friend std::ostream &operator<<(std::ostream &os, const XX &o) { return os << o.n << "XX"; }
};

#include <map>
#include <vector>
#include <fstream>
int main() {
    using namespace std::string_literals;

    std::vector v = { 1, 2, 3, 4 };
    std::map m { std::pair { "a"s, 30 }, { "bb", 31 }, { "ccc", 32 } };

    auto t = std::make_tuple(6, 7, 8, 9);
    auto t2 = std::make_tuple(11, std::ref(v), t);
    auto t3 = std::make_tuple(t2, std::vector{ 1234, 23, 2, 3, 3 }, "abc",
                              std::vector{ std::vector{ 11, 12, 13 }, std::vector{ 15, 16, 17 }, std::vector{ 19 } });
    std::ofstream ofs("test.txt");
    print_ex(ofs, t3, "xxxx", 55, m, std::vector<std::string>{ "x" }, XX{ 66 });
    ofs << "\n";

    print(t3, "xxxx", 55, m, std::vector<std::string>{ "x" }, XX{ 66 });
}
Related