OILS / vendor / souffle / utility / StreamUtil.h View on Github | oilshell.org

349 lines, 148 significant
1/*
2 * Souffle - A Datalog Compiler
3 * Copyright (c) 2021, The Souffle Developers. All rights reserved
4 * Licensed under the Universal Permissive License v 1.0 as shown at:
5 * - https://opensource.org/licenses/UPL
6 * - <souffle root>/licenses/SOUFFLE-UPL.txt
7 */
8
9/************************************************************************
10 *
11 * @file StreamUtil.h
12 *
13 * @brief Datalog project utilities
14 *
15 ***********************************************************************/
16
17#pragma once
18
19#include <map>
20#include <memory>
21#include <ostream>
22#include <set>
23#include <string>
24#include <type_traits>
25#include <unordered_map>
26#include <unordered_set>
27#include <utility>
28#include <vector>
29
30#include "souffle/utility/ContainerUtil.h"
31#include "souffle/utility/span.h"
32
33// -------------------------------------------------------------------------------
34// General Print Utilities
35// -------------------------------------------------------------------------------
36
37namespace souffle {
38
39// Usage: `using namespace stream_write_qualified_char_as_number;`
40// NB: `using` must appear in the same namespace as the `<<` callers.
41// Putting the `using` in a parent namespace will have no effect.
42// Motivation: Octet sized numeric types are often defined as aliases of a qualified
43// `char`. e.g. `using uint8_t = unsigned char'`
44// `std::ostream` has an overload which converts qualified `char`s to plain `char`.
45// You don't usually want to print a `uint8_t` as an ASCII character.
46//
47// NOTE: `char`, `signed char`, and `unsigned char` are distinct types.
48namespace stream_write_qualified_char_as_number {
49inline std::ostream& operator<<(std::ostream& os, signed char c) {
50 return os << int(c);
51}
52
53inline std::ostream& operator<<(std::ostream& os, unsigned char c) {
54 return os << unsigned(c);
55}
56} // namespace stream_write_qualified_char_as_number
57
58template <typename A>
59struct IsPtrLike : std::is_pointer<A> {};
60template <typename A>
61struct IsPtrLike<Own<A>> : std::true_type {};
62template <typename A>
63struct IsPtrLike<std::shared_ptr<A>> : std::true_type {};
64template <typename A>
65struct IsPtrLike<std::weak_ptr<A>> : std::true_type {};
66
67namespace detail {
68
69/**
70 * A auxiliary class to be returned by the join function aggregating the information
71 * required to print a list of elements as well as the implementation of the printing
72 * itself.
73 */
74template <typename Iter, typename Printer>
75class joined_sequence {
76 /** The begin of the range to be printed */
77 Iter begin;
78
79 /** The end of the range to be printed */
80 Iter end;
81
82 /** The seperator to be utilized between elements */
83 std::string sep;
84
85 /** A functor printing an element */
86 Printer p;
87
88public:
89 /** A constructor setting up all fields of this class */
90 joined_sequence(const Iter& a, const Iter& b, std::string sep, Printer p)
91 : begin(a), end(b), sep(std::move(sep)), p(std::move(p)) {}
92
93 /** The actual print method */
94 friend std::ostream& operator<<(std::ostream& out, const joined_sequence& s) {
95 auto cur = s.begin;
96 if (cur == s.end) {
97 return out;
98 }
99
100 s.p(out, *cur);
101 ++cur;
102 for (; cur != s.end; ++cur) {
103 out << s.sep;
104 s.p(out, *cur);
105 }
106 return out;
107 }
108};
109
110/**
111 * A generic element printer.
112 *
113 * @tparam Extractor a functor preparing a given value before being printed.
114 */
115template <typename Extractor>
116struct print {
117 template <typename T>
118 void operator()(std::ostream& out, const T& value) const {
119 // extract element to be printed from the given value and print it
120 Extractor ext;
121 out << ext(value);
122 }
123};
124} // namespace detail
125
126/**
127 * A functor representing the identity function for a generic type T.
128 *
129 * @tparam T some arbitrary type
130 */
131template <typename T>
132struct id {
133 T& operator()(T& t) const {
134 return t;
135 }
136 const T& operator()(const T& t) const {
137 return t;
138 }
139};
140
141/**
142 * A functor dereferencing a given type
143 *
144 * @tparam T some arbitrary type with an overloaded * operator (deref)
145 */
146template <typename T>
147struct deref {
148 auto operator()(T& t) const -> decltype(*t) {
149 return *t;
150 }
151 auto operator()(const T& t) const -> decltype(*t) {
152 return *t;
153 }
154};
155
156/**
157 * A functor printing elements after dereferencing it. This functor
158 * is mainly intended to be utilized when printing sequences of elements
159 * of a pointer type when using the join function below.
160 */
161template <typename T>
162struct print_deref : public detail::print<deref<T>> {};
163
164/**
165 * Creates an object to be forwarded to some output stream for printing
166 * sequences of elements interspersed by a given separator.
167 *
168 * For use cases see the test case {util_test.cpp}.
169 */
170template <typename Iter, typename Printer>
171detail::joined_sequence<Iter, Printer> join(const Iter& a, const Iter& b, std::string sep, const Printer& p) {
172 return souffle::detail::joined_sequence<Iter, Printer>(a, b, std::move(sep), p);
173}
174
175/**
176 * Creates an object to be forwarded to some output stream for printing
177 * sequences of elements interspersed by a given separator.
178 *
179 * For use cases see the test case {util_test.cpp}.
180 */
181template <typename Iter, typename T = typename Iter::value_type>
182detail::joined_sequence<Iter, detail::print<id<T>>> join(
183 const Iter& a, const Iter& b, const std::string& sep = ",") {
184 return join(a, b, sep, detail::print<id<T>>());
185}
186
187/**
188 * Creates an object to be forwarded to some output stream for printing
189 * the content of containers interspersed by a given separator.
190 *
191 * For use cases see the test case {util_test.cpp}.
192 */
193template <typename Container, typename Printer, typename Iter = typename Container::const_iterator>
194detail::joined_sequence<Iter, Printer> join(const Container& c, std::string sep, const Printer& p) {
195 return join(c.begin(), c.end(), std::move(sep), p);
196}
197
198// Decide if the sane default is to deref-then-print or just print.
199// Right now, deref anything deref-able *except* for a `const char*` (which handled as a C-string).
200template <typename A>
201constexpr bool JoinShouldDeref = IsPtrLike<A>::value && !std::is_same_v<A, char const*>;
202
203/**
204 * Creates an object to be forwarded to some output stream for printing
205 * the content of containers interspersed by a given separator.
206 *
207 * For use cases see the test case {util_test.cpp}.
208 */
209template <typename Container, typename Iter = typename Container::const_iterator,
210 typename T = typename std::iterator_traits<Iter>::value_type>
211std::enable_if_t<!JoinShouldDeref<T>, detail::joined_sequence<Iter, detail::print<id<T>>>> join(
212 const Container& c, std::string sep = ",") {
213 return join(c.begin(), c.end(), std::move(sep), detail::print<id<T>>());
214}
215
216template <typename Container, typename Iter = typename Container::const_iterator,
217 typename T = typename std::iterator_traits<Iter>::value_type>
218std::enable_if_t<JoinShouldDeref<T>, detail::joined_sequence<Iter, detail::print<deref<T>>>> join(
219 const Container& c, std::string sep = ",") {
220 return join(c.begin(), c.end(), std::move(sep), detail::print<deref<T>>());
221}
222
223template <typename C, typename F>
224auto joinMap(const C& c, F&& map) {
225 return join(c.begin(), c.end(), ",", [&](auto&& os, auto&& x) { return os << map(x); });
226}
227
228template <typename C, typename F>
229auto joinMap(const C& c, std::string sep, F&& map) {
230 return join(c.begin(), c.end(), std::move(sep), [&](auto&& os, auto&& x) { return os << map(x); });
231}
232
233} // end namespace souffle
234
235#ifndef __EMBEDDED_SOUFFLE__
236
237namespace std {
238
239/**
240 * Enables the generic printing of `array`s assuming their element types
241 * are printable.
242 */
243template <typename T, std::size_t E>
244ostream& operator<<(ostream& out, const array<T, E>& v) {
245 return out << "[" << souffle::join(v) << "]";
246}
247
248/**
249 * Introduces support for printing pairs as long as their components can be printed.
250 */
251template <typename A, typename B>
252ostream& operator<<(ostream& out, const pair<A, B>& p) {
253 return out << "(" << p.first << "," << p.second << ")";
254}
255
256/**
257 * Enables the generic printing of vectors assuming their element types
258 * are printable.
259 */
260template <typename T, typename A>
261ostream& operator<<(ostream& out, const vector<T, A>& v) {
262 return out << "[" << souffle::join(v) << "]";
263}
264
265/**
266 * Enables the generic printing of `span`s assuming their element types
267 * are printable.
268 */
269template <typename T, std::size_t E>
270ostream& operator<<(ostream& out, const souffle::span<T, E>& v) {
271 return out << "[" << souffle::join(v) << "]";
272}
273
274/**
275 * Enables the generic printing of sets assuming their element types
276 * are printable.
277 */
278template <typename K, typename C, typename A>
279ostream& operator<<(ostream& out, const set<K, C, A>& s) {
280 return out << "{" << souffle::join(s) << "}";
281}
282
283/**
284 * Enables the generic printing of multisets assuming their element types
285 * are printable.
286 */
287template <typename K, typename C, typename A>
288ostream& operator<<(ostream& out, const multiset<K, C, A>& s) {
289 return out << "{" << souffle::join(s) << "}";
290}
291
292/**
293 * Enables the generic printing of maps assuming their element types
294 * are printable.
295 */
296template <typename K, typename T, typename C, typename A>
297ostream& operator<<(ostream& out, const map<K, T, C, A>& m) {
298 return out << "{" << souffle::join(m, ",", [](ostream& out, const pair<K, T>& cur) {
299 out << cur.first << "->" << cur.second;
300 }) << "}";
301}
302
303template <typename K, typename H, typename A>
304ostream& operator<<(ostream& out, const unordered_set<K, H, A>& s) {
305 return out << "{" << souffle::join(s) << "}";
306}
307
308template <typename K, typename T, typename H, typename E, typename A>
309ostream& operator<<(ostream& out, const unordered_map<K, T, H, E, A>& m) {
310 return out << "{" << souffle::join(m, ",", [](ostream& out, const pair<K, T>& cur) {
311 out << cur.first << "->" << cur.second;
312 }) << "}";
313}
314
315} // end namespace std
316
317#endif
318
319namespace souffle {
320
321namespace detail {
322
323/**
324 * A utility class required for the implementation of the times function.
325 */
326template <typename T>
327struct multiplying_printer {
328 const T& value;
329 unsigned times;
330 multiplying_printer(const T& value, unsigned times) : value(value), times(times) {}
331
332 friend std::ostream& operator<<(std::ostream& out, const multiplying_printer& printer) {
333 for (unsigned i = 0; i < printer.times; i++) {
334 out << printer.value;
335 }
336 return out;
337 }
338};
339} // namespace detail
340
341/**
342 * A utility printing a given value multiple times.
343 */
344template <typename T>
345detail::multiplying_printer<T> times(const T& value, unsigned num) {
346 return detail::multiplying_printer<T>(value, num);
347}
348
349} // namespace souffle