libsemigroups  v3.2.0
C++ library for semigroups and monoids
Loading...
Searching...
No Matches
report.hpp
1//
2// libsemigroups - C++ library for semigroups and monoids
3// Copyright (C) 2019-2025 James D. Mitchell
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <http://www.gnu.org/licenses/>.
17//
18
19// This file contains a class for reporting things during a computation.
20
21#ifndef LIBSEMIGROUPS_DETAIL_REPORT_HPP_
22#define LIBSEMIGROUPS_DETAIL_REPORT_HPP_
23
24#include <chrono> // for std::chrono
25#include <cstddef> // for size_t
26#include <functional> // for std::function
27#include <mutex> // for mutex, lock_guard
28#include <string> // for string
29#include <thread> // for get_id, thread, thread::id
30#include <utility> // for pair
31
32#include "libsemigroups/exception.hpp" // LIBSEMIGROUPS_EXCEPTION
33
34#include "fmt.hpp" // for fmtlib includes
35#include "formatters.hpp" // for custom formatters
36#include "timer.hpp" // for string_format, to_strin
37
38namespace libsemigroups {
39
40 namespace detail {
41 using t_id = size_t;
42
43 t_id this_threads_id();
44 t_id thread_id(std::thread::id);
45 void reset_thread_ids();
46
47 bool is_report_suppressed_for(std::string_view);
48
49 // This class provides a thread-safe means of calling a function every
50 // second in a detached thread. The purpose of this class is so that the
51 // TickerImpl, which contains the data required by the function to be
52 // called, inside the Ticker, can outlive the Ticker, the TickerImpl is
53 // deleted by the function called in the thread.
54 // TODO(1) prevent too many Tickers being created at a time, really just
55 // launch a single thread to do reporting for the duration.
56 class Ticker {
57 class TickerImpl;
58
59 TickerImpl* _ticker_impl;
60
61 public:
62 // Construct a Ticker that calls \c func every \c time, until the Ticker
63 // object is destructed.
64 template <typename Func, typename Time = std::chrono::seconds>
65 explicit Ticker(Func&& func, Time time = std::chrono::seconds(1));
66
67 template <typename Func, typename Time = std::chrono::seconds>
68 void operator()(Func&& func, Time time = std::chrono::seconds(1));
69
70 Ticker() : _ticker_impl(nullptr) {}
71 Ticker(Ticker const&) = delete;
72 Ticker(Ticker&&) = delete;
73 Ticker& operator=(Ticker const&) = delete;
74 Ticker& operator=(Ticker&&) = delete;
75
76 ~Ticker();
77 };
78
79 enum class Align : uint8_t { left, right };
80
81 // This object is a helper for formatting information reported by various
82 // classes in libsemigroups such as ToddCoxeterImpl, KnuthBendixImpl, etc.
83 //
84 // The idea is to store the rows in the _rows, and to properly align the
85 // values in each column. This is done by storing the rows and their widths,
86 // then using std::apply to call report_default on the properly aligned
87 // columns. This happens when the ReportCell object is destroyed.
88 template <size_t C>
89 class ReportCell {
90 private:
91 using Row = std::array<std::string, C + 1>;
92 std::array<Align, C + 1> _align;
93 std::array<size_t, C + 1> _col_widths;
94 std::vector<Row> _rows;
95
96 public:
97 ReportCell() : _align(), _col_widths(), _rows() {
98 _align.fill(Align::right);
99 _col_widths.fill(0);
100 }
101
102 ReportCell(ReportCell const&) = default;
103 ReportCell(ReportCell&&) = default;
104 ReportCell& operator=(ReportCell const&) = default;
105 ReportCell& operator=(ReportCell&&) = default;
106
107 ~ReportCell() {
108 emit();
109 }
110
111 // Set the minimum width of every column
112 ReportCell& min_width(size_t val) {
113 _col_widths.fill(val);
114 return *this;
115 }
116
117 // Set the minimum width of a specific column
118 ReportCell& min_width(size_t col, size_t val) {
119 LIBSEMIGROUPS_ASSERT(col < C);
120 _col_widths[col + 1] = val;
121 return *this;
122 }
123
124 ReportCell& align(size_t col, Align val) noexcept {
125 LIBSEMIGROUPS_ASSERT(col < C);
126 _align[col + 1] = val;
127 return *this;
128 }
129
130 ReportCell& align(Align val) noexcept {
131 _align.fill(val);
132 return *this;
133 }
134
135 Align align(size_t col) const noexcept {
136 LIBSEMIGROUPS_ASSERT(col < C);
137 return _align[col + 1];
138 }
139
140 // Insert a row using a format string and arguments
141 template <typename... Args>
142 void operator()(std::string_view fmt_str, Args&&... args);
143
144 // Insert a row using a function, format string, and arguments. The
145 // function is called on each of the arguments.
146 template <typename Func, typename... Args>
147 void operator()(Func&& f, char const* fmt_str, Args&&... args) {
148 operator()(fmt_str, f(args)...);
149 }
150
151 private:
152 void emit();
153 }; // ReportCell
154
155 } // namespace detail
156
158 bool reporting_enabled() noexcept;
159
161 template <typename... Args>
162 std::string fmt_default(std::string_view sv, Args&&... args) {
163 std::string prefix = fmt::format("#{}: ", detail::this_threads_id());
164 return prefix + fmt::format(sv, std::forward<Args>(args)...);
165 }
166
169
171 // This function is provided to allow multiple lines of output to block other
172 // lines from being interspersed, by first locking the report_mutex.
173 template <typename... Args>
174 void report_no_lock_no_prefix(std::string_view sv, Args&&... args) {
175 auto line = fmt::format(sv, std::forward<Args>(args)...);
176 fmt::print("{}", line);
177 }
178
180 template <typename... Args>
181 void report_no_prefix(std::string_view sv, Args&&... args) {
182 if (reporting_enabled()) {
185 }
186 }
187
189 template <typename... Args>
190 void report_default(std::string_view sv, Args&&... args) {
191 if (reporting_enabled()) {
192 std::string_view prefix(sv);
193 auto pos = prefix.find(":");
194 if (pos != std::string::npos) {
195 prefix.remove_suffix(prefix.size() - prefix.find(":"));
196 if (detail::is_report_suppressed_for((prefix))) {
197 return;
198 }
199 }
201 }
202 }
203
213 struct ReportGuard {
219 explicit ReportGuard(bool val = true);
220 ~ReportGuard();
221 };
222
223 class SuppressReportFor {
224 std::string_view _prefix;
225
226 public:
227 explicit SuppressReportFor(std::string_view);
228 ~SuppressReportFor();
229 };
230} // namespace libsemigroups
231
232#include "report.tpp"
233
234#endif // LIBSEMIGROUPS_DETAIL_REPORT_HPP_
T forward(T... args)
Namespace for everything in the libsemigroups library.
Definition action.hpp:44
bool reporting_enabled() noexcept
No doc.
void report_default(std::string_view sv, Args &&... args)
No doc.
Definition report.hpp:190
std::string fmt_default(std::string_view sv, Args &&... args)
No doc.
Definition report.hpp:162
void report_no_lock_no_prefix(std::string_view sv, Args &&... args)
No doc.
Definition report.hpp:174
void report_no_prefix(std::string_view sv, Args &&... args)
No doc.
Definition report.hpp:181
std::mutex & report_mutex()
No doc.
ReportGuard(bool val=true)
Constructs a ReportGuard with reporting enabled by default.