Clingo
Loading...
Searching...
No Matches
print.hh
1#pragma once
2
3#include <algorithm>
4#include <cassert>
5#include <charconv>
6#include <cstring>
7#include <ostream>
8#include <span>
9#include <streambuf>
10#include <string>
11#include <string_view>
12#include <vector>
13
14namespace CppClingo::Util {
15
18
27 public:
29 using value_type = char;
30
32 OutputBuffer(FILE *out = nullptr) : out_{out} {}
33
37 void flush() {
38 if (out_ != nullptr) {
39 fwrite(buf_.data(), sizeof(char), size_, out_);
40 fflush(out_);
41 size_ = 0;
42 }
43 }
44
48 void endl() {
49 constexpr auto n = 8192;
50 if (out_ != nullptr && size_ > n) {
51 fwrite(buf_.data(), sizeof(char), size_, out_);
52 size_ = 0;
53 }
54 }
55
57 [[nodiscard]] auto size() const -> size_t { return static_cast<size_t>(size_); }
58
60 [[nodiscard]] auto empty() const -> bool { return size_ == 0; }
61
63 [[nodiscard]] auto view() const -> std::string_view {
64 return std::string_view{buf_.data(), static_cast<size_t>(size_)};
65 }
66
68 [[nodiscard]] auto span() -> std::span<char> { return std::span{buf_.data(), static_cast<size_t>(size_)}; }
69
71 [[nodiscard]] auto str() const -> std::string { return std::string{buf_.data(), static_cast<size_t>(size_)}; }
72
74 [[nodiscard]] auto c_str() -> char const * {
75 *ensure_(1) = '\0';
76 return buf_.data();
77 }
78
80 auto reset() -> OutputBuffer & {
81 size_ = 0;
82 return *this;
83 }
84
86 auto release() -> std::vector<char> {
87 buf_.resize(size_);
88 buf_.emplace_back('\0');
89 auto ret = std::move(buf_);
90 buf_.clear();
91 size_ = 0;
92 return ret;
93 }
94
96 void append(char const *str) {
97 auto n = static_cast<std::ptrdiff_t>(std::strlen(str));
98 std::copy(str, std::next(str, n), ensure_(n));
99 size_ += n;
100 }
101
103 void append(std::string_view str) {
104 auto n = static_cast<std::ptrdiff_t>(str.length());
105 std::ranges::copy(str, ensure_(n));
106 size_ += n;
107 }
108
110 void append(char c) {
111 *ensure_(1) = c;
112 ++size_;
113 }
114
116 void push_back(char c) { append(c); }
117
119 void pop() { --size_; }
120
122 template <std::integral T> void append(T num, int base = 10) { // NOLINT
123 constexpr auto n = 256;
124 auto *end = ensure_(n);
125 auto res = std::to_chars(end, limit_(), num, base);
126 size_ += res.ptr - end;
127 }
128
132 auto reserve(std::ptrdiff_t n) -> std::span<char> {
133 auto *begin = ensure_(n);
134 size_ += n;
135 return {begin, std::next(begin, n)};
136 }
137
141 void trim_zero(std::ptrdiff_t len) {
142 auto sp = std::span{buf_.data(), static_cast<size_t>(size_)};
143 auto ie = sp.end();
144 auto ib = sp.begin() + (size_ - len);
145 auto it = std::find(ib, ie, '\0');
146 size_ -= ie - it;
147 }
148
150 template <std::integral T> friend auto operator<<(OutputBuffer &out, T num) -> OutputBuffer & {
151 out.append(num);
152 return out;
153 }
154
156 friend auto operator<<(OutputBuffer &out, char c) -> OutputBuffer & {
157 out.append(c);
158 return out;
159 }
160
162 friend auto operator<<(OutputBuffer &out, std::string_view str) -> OutputBuffer & {
163 out.append(str);
164 return out;
165 }
166
168 friend auto operator<<(OutputBuffer &out, double value) -> OutputBuffer & {
169 static constexpr std::ptrdiff_t n = 32;
170 auto *begin = out.ensure_(n);
171 auto *end = std::next(begin, n);
172 auto [res, ec] = std::to_chars(begin, end, value);
173 out.size_ += std::distance(begin, res);
174 return out;
175 }
176
178 friend auto operator<<(OutputBuffer &out, char const *str) -> OutputBuffer & {
179 out.append(str);
180 return out;
181 }
182
183 private:
184 auto limit_() -> char * { return std::next(buf_.data(), static_cast<std::ptrdiff_t>(buf_.size())); }
185
186 auto ensure_(std::ptrdiff_t n) -> char * {
187 auto m = size_ + n;
188 assert(n >= 0 && m >= 0);
189 if (buf_.size() < static_cast<size_t>(m)) {
190 buf_.reserve(2 * static_cast<size_t>(m));
191 buf_.resize(buf_.capacity());
192 }
193 return std::next(buf_.data(), size_);
194 }
195
196 std::vector<char> buf_;
197 std::ptrdiff_t size_ = 0;
198 FILE *out_;
199};
200
202class OutputStream : public std::ostream {
203 public:
205 OutputStream(FILE *out) : std::ostream{nullptr}, buf_{out} { rdbuf(&buf_); }
206
208 auto buffer() -> OutputBuffer & { return buf_.buffer(); }
209
210 private:
211 class Streambuf : public std::streambuf {
212 public:
213 Streambuf(FILE *out) : buf_{out} {}
214 auto buffer() -> OutputBuffer & { return buf_; }
215
216 protected:
217 auto overflow(int ch) -> int override {
218 if (ch != traits_type::eof()) {
219 buf_.push_back(static_cast<char>(ch));
220 return traits_type::not_eof(ch);
221 }
222 return traits_type::eof();
223 }
224
225 auto xsputn(char const *s, std::streamsize n) -> std::streamsize override {
226 buf_.append(std::string_view{s, static_cast<size_t>(n)});
227 return n;
228 }
229
230 auto sync() -> int override {
231 buf_.flush();
232 return 0;
233 }
234
235 private:
236 OutputBuffer buf_;
237 };
238
239 Streambuf buf_;
240};
241
242namespace Detail {
243
245struct PrintSelf {
247 template <class Out> void operator()(Out &out, auto const &x) { out << x; }
248};
249
251template <class It, class F> class PrintRange {
252 public:
254 template <class A>
255 PrintRange(It first, It last, char const *sep, A &&fun)
256 : first_{first}, last_{last}, sep_{sep}, fun_{std::forward<A>(fun)} {}
258 template <class Out> friend auto operator<<(Out &out, PrintRange rng) -> Out & {
259 if (rng.first_ != rng.last_) {
260 rng.fun_(out, *rng.first_);
261 for (++rng.first_; rng.first_ != rng.last_; ++rng.first_) {
262 out << rng.sep_;
263 rng.fun_(out, *rng.first_);
264 }
265 }
266 return out;
267 }
268
269 private:
270 It first_;
271 It last_;
272 char const *sep_;
273 [[no_unique_address]] F fun_;
274};
275
276template <class It, class F> PrintRange(It, It, char const *, F &&) -> PrintRange<It, std::unwrap_ref_decay_t<F>>;
277
279template <class F> class PrintFun {
280 public:
282 template <class A> PrintFun([[maybe_unused]] int tag, A &&fun) : fun_{std::forward<A>(fun)} {}
284 template <class Out> friend auto operator<<(Out &out, PrintFun x) -> Out & {
285 x.fun_(out);
286 return out;
287 }
288
289 private:
290 F fun_;
291};
292
293template <class F> PrintFun(int, F &&) -> PrintFun<std::unwrap_ref_decay_t<F>>;
294
296class PrintQuoted {
297 public:
299 PrintQuoted(std::string_view str, bool fstring) : str_{str}, fstring_{fstring} {}
301 template <class Out> friend auto operator<<(Out &out, PrintQuoted x) -> Out & {
302 if (!x.fstring_) {
303 out << '"';
304 }
305 for (auto c : x.str_) {
306 if (c == '\\') {
307 out << "\\\\";
308 } else if (x.fstring_ && c == '{') {
309 out << "{{";
310 } else if (x.fstring_ && c == '}') {
311 out << "}}";
312 } else if (c == '\n') {
313 out << "\\n";
314 } else if (c == '\t') {
315 out << "\\t";
316 } else if (c == '"') {
317 out << "\\\"";
318 } else {
319 out << c;
320 }
321 }
322 if (!x.fstring_) {
323 out << '"';
324 }
325 return out;
326 }
327
328 private:
329 std::string_view str_;
330 bool fstring_ = false;
331};
332
333} // namespace Detail
334
336class fill {
337 public:
339 fill(size_t n, char c = ' ') : n_{n}, c_{c} {}
342 std::ranges::fill(out.reserve(static_cast<std::ptrdiff_t>(x.n_)), x.c_);
343 return out;
344 }
345
346 private:
347 size_t n_;
348 char c_;
349};
350
352template <class F> auto p_fun(F &&fun) {
353 return Detail::PrintFun(0, std::forward<F>(fun));
354}
355
357template <class T, class F> auto p_range(T const &rng, char const *sep, F &&fun) {
358 using std::begin, std::end;
359 return Detail::PrintRange{begin(rng), end(rng), sep, std::forward<F>(fun)};
360}
361
363template <class T> auto p_range(T const &rng) {
364 return p_range(rng, ",", Detail::PrintSelf{});
365}
366
368template <class T, class F> auto p_range(T const &rng, F &&fun) {
369 return p_range(rng, ",", std::forward<F>(fun));
370}
371
373template <class T> auto p_range(T const &rng, char const *sep) {
374 return p_range(rng, sep, Detail::PrintSelf{});
375}
376
378inline auto p_quoted(std::string_view str, bool fstring = false) {
379 return Detail::PrintQuoted{str, fstring};
380}
381
383
384} // namespace CppClingo::Util
Create an output buffer that bears some similarities with C++'s iostreams.
Definition print.hh:26
auto size() const -> size_t
Get the number of bytes currently stored in the buffer.
Definition print.hh:57
auto release() -> std::vector< char >
Empty the buffer and return a vector with the previous content.
Definition print.hh:86
void append(T num, int base=10)
Append an integral to the buffer.
Definition print.hh:122
friend auto operator<<(OutputBuffer &out, T num) -> OutputBuffer &
Append the given integral to the buffer.
Definition print.hh:150
auto span() -> std::span< char >
Get a char span of the current buffer content.
Definition print.hh:68
void flush()
Flush the buffer.
Definition print.hh:37
auto empty() const -> bool
Check if the buffer is currently emtpy.
Definition print.hh:60
auto view() const -> std::string_view
Get a string view of the current buffer content.
Definition print.hh:63
void append(char c)
Append a char to the buffer.
Definition print.hh:110
auto c_str() -> char const *
Get a C string with the current buffer content.
Definition print.hh:74
friend auto operator<<(OutputBuffer &out, double value) -> OutputBuffer &
Append the given double to the buffer.
Definition print.hh:168
friend auto operator<<(OutputBuffer &out, char const *str) -> OutputBuffer &
Append the given string to the buffer.
Definition print.hh:178
void pop()
Pop a char from the buffer.
Definition print.hh:119
void append(std::string_view str)
Append a string to the buffer.
Definition print.hh:103
void trim_zero(std::ptrdiff_t len)
Trim trailing zeros.
Definition print.hh:141
friend auto operator<<(OutputBuffer &out, std::string_view str) -> OutputBuffer &
Append the given string to the buffer.
Definition print.hh:162
auto str() const -> std::string
Get a string with the current buffer content.
Definition print.hh:71
auto reserve(std::ptrdiff_t n) -> std::span< char >
Append n bytes at the end of the buffer.
Definition print.hh:132
void endl()
Flush the buffer if it has a predefined minimum size.
Definition print.hh:48
void push_back(char c)
Alias for append(char) to support std::back_inserter.
Definition print.hh:116
OutputBuffer(FILE *out=nullptr)
Construct the buffer with an optional file handle.
Definition print.hh:32
friend auto operator<<(OutputBuffer &out, char c) -> OutputBuffer &
Append the given char to the buffer.
Definition print.hh:156
auto reset() -> OutputBuffer &
Empty the buffer.
Definition print.hh:80
char value_type
The value type of the buffer.
Definition print.hh:29
void append(char const *str)
Append a string to the buffer.
Definition print.hh:96
Output stream with an underlying OutputBuffer.
Definition print.hh:202
auto buffer() -> OutputBuffer &
Get the underlying buffer.
Definition print.hh:208
OutputStream(FILE *out)
Construct the buffer with an optional file handle.
Definition print.hh:205
Helper for iostreams to fill with a fixed number of characters.
Definition print.hh:336
fill(size_t n, char c=' ')
The constructor.
Definition print.hh:339
friend auto operator<<(CppClingo::Util::OutputBuffer &out, fill const &x) -> CppClingo::Util::OutputBuffer &
Operator to print the fill helper.
Definition print.hh:341
auto operator<<(std::ostream &out, Sign sign) -> std::ostream &
Output the given sign.
auto p_quoted(std::string_view str, bool fstring=false)
Quote and print the given string.
Definition print.hh:378
auto p_fun(F &&fun)
Print with a function.
Definition print.hh:352
auto p_range(T const &rng, char const *sep, F &&fun)
Print a range with a separator.
Definition print.hh:357