/* * Souffle - A Datalog Compiler * Copyright (c) 2021, The Souffle Developers. All rights reserved * Licensed under the Universal Permissive License v 1.0 as shown at: * - https://opensource.org/licenses/UPL * - /licenses/SOUFFLE-UPL.txt */ /************************************************************************ * * @file StreamUtil.h * * @brief Datalog project utilities * ***********************************************************************/ #pragma once #include #include #include #include #include #include #include #include #include "souffle/utility/ContainerUtil.h" #include "souffle/utility/span.h" // ------------------------------------------------------------------------------- // General Print Utilities // ------------------------------------------------------------------------------- namespace souffle { // Usage: `using namespace stream_write_qualified_char_as_number;` // NB: `using` must appear in the same namespace as the `<<` callers. // Putting the `using` in a parent namespace will have no effect. // Motivation: Octet sized numeric types are often defined as aliases of a qualified // `char`. e.g. `using uint8_t = unsigned char'` // `std::ostream` has an overload which converts qualified `char`s to plain `char`. // You don't usually want to print a `uint8_t` as an ASCII character. // // NOTE: `char`, `signed char`, and `unsigned char` are distinct types. namespace stream_write_qualified_char_as_number { inline std::ostream& operator<<(std::ostream& os, signed char c) { return os << int(c); } inline std::ostream& operator<<(std::ostream& os, unsigned char c) { return os << unsigned(c); } } // namespace stream_write_qualified_char_as_number template struct IsPtrLike : std::is_pointer {}; template struct IsPtrLike> : std::true_type {}; template struct IsPtrLike> : std::true_type {}; template struct IsPtrLike> : std::true_type {}; namespace detail { /** * A auxiliary class to be returned by the join function aggregating the information * required to print a list of elements as well as the implementation of the printing * itself. */ template class joined_sequence { /** The begin of the range to be printed */ Iter begin; /** The end of the range to be printed */ Iter end; /** The seperator to be utilized between elements */ std::string sep; /** A functor printing an element */ Printer p; public: /** A constructor setting up all fields of this class */ joined_sequence(const Iter& a, const Iter& b, std::string sep, Printer p) : begin(a), end(b), sep(std::move(sep)), p(std::move(p)) {} /** The actual print method */ friend std::ostream& operator<<(std::ostream& out, const joined_sequence& s) { auto cur = s.begin; if (cur == s.end) { return out; } s.p(out, *cur); ++cur; for (; cur != s.end; ++cur) { out << s.sep; s.p(out, *cur); } return out; } }; /** * A generic element printer. * * @tparam Extractor a functor preparing a given value before being printed. */ template struct print { template void operator()(std::ostream& out, const T& value) const { // extract element to be printed from the given value and print it Extractor ext; out << ext(value); } }; } // namespace detail /** * A functor representing the identity function for a generic type T. * * @tparam T some arbitrary type */ template struct id { T& operator()(T& t) const { return t; } const T& operator()(const T& t) const { return t; } }; /** * A functor dereferencing a given type * * @tparam T some arbitrary type with an overloaded * operator (deref) */ template struct deref { auto operator()(T& t) const -> decltype(*t) { return *t; } auto operator()(const T& t) const -> decltype(*t) { return *t; } }; /** * A functor printing elements after dereferencing it. This functor * is mainly intended to be utilized when printing sequences of elements * of a pointer type when using the join function below. */ template struct print_deref : public detail::print> {}; /** * Creates an object to be forwarded to some output stream for printing * sequences of elements interspersed by a given separator. * * For use cases see the test case {util_test.cpp}. */ template detail::joined_sequence join(const Iter& a, const Iter& b, std::string sep, const Printer& p) { return souffle::detail::joined_sequence(a, b, std::move(sep), p); } /** * Creates an object to be forwarded to some output stream for printing * sequences of elements interspersed by a given separator. * * For use cases see the test case {util_test.cpp}. */ template detail::joined_sequence>> join( const Iter& a, const Iter& b, const std::string& sep = ",") { return join(a, b, sep, detail::print>()); } /** * Creates an object to be forwarded to some output stream for printing * the content of containers interspersed by a given separator. * * For use cases see the test case {util_test.cpp}. */ template detail::joined_sequence join(const Container& c, std::string sep, const Printer& p) { return join(c.begin(), c.end(), std::move(sep), p); } // Decide if the sane default is to deref-then-print or just print. // Right now, deref anything deref-able *except* for a `const char*` (which handled as a C-string). template constexpr bool JoinShouldDeref = IsPtrLike::value && !std::is_same_v; /** * Creates an object to be forwarded to some output stream for printing * the content of containers interspersed by a given separator. * * For use cases see the test case {util_test.cpp}. */ template ::value_type> std::enable_if_t, detail::joined_sequence>>> join( const Container& c, std::string sep = ",") { return join(c.begin(), c.end(), std::move(sep), detail::print>()); } template ::value_type> std::enable_if_t, detail::joined_sequence>>> join( const Container& c, std::string sep = ",") { return join(c.begin(), c.end(), std::move(sep), detail::print>()); } template auto joinMap(const C& c, F&& map) { return join(c.begin(), c.end(), ",", [&](auto&& os, auto&& x) { return os << map(x); }); } template auto joinMap(const C& c, std::string sep, F&& map) { return join(c.begin(), c.end(), std::move(sep), [&](auto&& os, auto&& x) { return os << map(x); }); } } // end namespace souffle #ifndef __EMBEDDED_SOUFFLE__ namespace std { /** * Enables the generic printing of `array`s assuming their element types * are printable. */ template ostream& operator<<(ostream& out, const array& v) { return out << "[" << souffle::join(v) << "]"; } /** * Introduces support for printing pairs as long as their components can be printed. */ template ostream& operator<<(ostream& out, const pair& p) { return out << "(" << p.first << "," << p.second << ")"; } /** * Enables the generic printing of vectors assuming their element types * are printable. */ template ostream& operator<<(ostream& out, const vector& v) { return out << "[" << souffle::join(v) << "]"; } /** * Enables the generic printing of `span`s assuming their element types * are printable. */ template ostream& operator<<(ostream& out, const souffle::span& v) { return out << "[" << souffle::join(v) << "]"; } /** * Enables the generic printing of sets assuming their element types * are printable. */ template ostream& operator<<(ostream& out, const set& s) { return out << "{" << souffle::join(s) << "}"; } /** * Enables the generic printing of multisets assuming their element types * are printable. */ template ostream& operator<<(ostream& out, const multiset& s) { return out << "{" << souffle::join(s) << "}"; } /** * Enables the generic printing of maps assuming their element types * are printable. */ template ostream& operator<<(ostream& out, const map& m) { return out << "{" << souffle::join(m, ",", [](ostream& out, const pair& cur) { out << cur.first << "->" << cur.second; }) << "}"; } } // end namespace std #endif namespace souffle { namespace detail { /** * A utility class required for the implementation of the times function. */ template struct multiplying_printer { const T& value; unsigned times; multiplying_printer(const T& value, unsigned times) : value(value), times(times) {} friend std::ostream& operator<<(std::ostream& out, const multiplying_printer& printer) { for (unsigned i = 0; i < printer.times; i++) { out << printer.value; } return out; } }; } // namespace detail /** * A utility printing a given value multiple times. */ template detail::multiplying_printer times(const T& value, unsigned num) { return detail::multiplying_printer(value, num); } } // namespace souffle