iterative-solver 0.0
Logger.h
1#ifndef LINEARALGEBRA_SRC_MOLPRO_LINALG_ITSOLV_LOGGER_H
2#define LINEARALGEBRA_SRC_MOLPRO_LINALG_ITSOLV_LOGGER_H
3#include <array>
4#include <format>
5#include <iomanip>
6#include <iterator>
7#include <limits>
8#include <ranges>
9#include <sstream>
10#include <string>
11#include <string_view>
12#include <type_traits>
13#include <typeinfo>
14#include <tuple>
15#include <utility>
16
17#include <molpro/iostream.h>
18
19
20namespace molpro::linalg::itsolv {
21 class Logger;
22}
23
25
29enum class Verbosity : short {
30 Trace,
31 Debug,
32 Info,
33 None,
34};
35
36
40enum class Severity : short {
41 Normal,
42 Warning,
43 Error,
44 Fatal,
45};
46
47
51template <typename T>
52concept name_tagged = requires {
53 { T::name } -> std::convertible_to<const char *>;
54};
55
56
60template< typename CRTP, bool fixed_args, typename ...Args >
65 static constexpr const char *name = []() {
66 if constexpr (name_tagged<CRTP>) {
67 return CRTP::name;
68 }
69 return typeid(CRTP).name();
70 };
71
75 static constexpr bool uses_fixed_arguments = fixed_args;
76
77 // Used only as a store for the arg type list
78 using arg_types_tuple = std::tuple<std::remove_cvref_t<Args>...>;
79
83 static constexpr std::size_t num_args = sizeof...(Args);
84
90 template<typename T>
91 static constexpr decltype(auto) to_arg(T &&arg) {
92 if constexpr (std::ranges::view<T>) {
93 return std::forward<T>(arg);
94 } else if constexpr (std::ranges::range<std::remove_cvref_t<T>>) {
95 return std::ranges::views::all(arg);
96 } else {
97 return std::as_const(arg);
98 }
99 }
100
105 template<std::size_t Idx>
106 requires(Idx < num_args && uses_fixed_arguments)
107 using arg_t = decltype(to_arg(std::declval<std::tuple_element_t<Idx, arg_types_tuple>>()));
108
112 template<std::size_t Idx, typename ...Ts>
113 static arg_t<Idx> get_arg(Ts &&...args) {
114 return to_arg(std::get<Idx>(std::forward_as_tuple(std::forward<Ts>(args)...)));
115 }
116};
117
118
122template< typename T >
123concept context = requires(const T &t) {
124 // If this lambda can be called, T must inherit from ContextBase (with arbitrary template parameter)
125 []<bool b, typename...Ts>(const ContextBase<T, b, Ts...> &){}(t);
126};
127
131template< typename Ctx, typename ...Args >
132concept context_uses_correct_arg_types = context<Ctx> && std::derived_from<Ctx, ContextBase<Ctx, Ctx::uses_fixed_arguments, Args...>>;
133
134
138template<typename T>
139concept streamable = requires (std::ostream &stream, const std::remove_cvref_t<T> &t) {
140 stream << t;
141};
142
146template<typename T>
147concept formattable = requires (const std::remove_cvref_t<T> &t, std::format_context ctx) {
148 std::formatter<std::remove_cvref_t<T>>().format(t, ctx);
149};
150
155template<typename T>
156concept string_convertible_range = std::ranges::range<T>
158
159
163template<std::size_t MaxSize>
164requires(MaxSize > 0)
166public:
167 constexpr ConstexprString() : m_data(), m_size(0) {
168 m_data.fill('\0');
169 }
170 template<std::size_t N>
171 requires(N - 1 <= MaxSize)
172 constexpr ConstexprString(const char (&literal)[N]) : ConstexprString() {
173 // -1 as we don't want to copy the terminating null character
174 for (std::size_t i = 0; i < N - 1; ++i) {
175 push_back(literal[i]);
176 }
177 }
178 constexpr ~ConstexprString() {}
179
180 constexpr void push_back(char c) {
181 if (m_size == MaxSize) {
182 throw std::out_of_range("Maximum capacity has been reached - can't append further chars");
183 }
184
185 m_data.at(m_size) = c;
186 ++m_size;
187 }
188
189 constexpr std::size_t size() const { return m_size; }
190
191 constexpr char &operator[](std::size_t idx) {
192 if (idx >= m_size) {
193 throw std::out_of_range("Requested index is out of range");
194 }
195
196 return m_data.at(idx);
197 }
198
199 constexpr std::string_view as_view() const {
200 return {m_data.data(), m_size};
201 }
202
203 constexpr operator std::string_view() const {
204 return as_view();
205 }
206
207private:
208 std::array<char, MaxSize> m_data;
209 std::size_t m_size = 0;
210};
211
212
216constexpr std::size_t num_digits(std::size_t val) {
217 if (val == 0) {
218 return 1;
219 }
220
221 std::size_t num = 0;
222
223 while (val > 0){
224 val /= 10;
225 ++num;
226 }
227
228 return num;
229}
230
231
236template<std::size_t precision>
238 ConstexprString<num_digits(precision) + 4> str;
239
240 str.push_back('{');
241 str.push_back(':');
242 str.push_back('.');
243
244 if (precision == 0) {
245 str.push_back('0');
246 } else {
247 std::size_t begin = str.size();
248
249 // This create the digits in reverse order
250 std::size_t prec_val = precision;
251 while (prec_val > 0) {
252 std::size_t tmp = prec_val / 10;
253 std::size_t digit = prec_val - tmp * 10;
254 prec_val = tmp;
255
256 str.push_back('0' + static_cast<char>(digit));
257 }
258
259 std::size_t num_digits = str.size() - begin;
260
261 // Reverse order of digits in-place
262 for (std::size_t i = 0; i < num_digits / 2; ++i) {
263 std::swap(str[i + begin], str[str.size() - i - 1]);
264 }
265 }
266
267 str.push_back('}');
268
269 return str;
270}
271
272
276constexpr const std::size_t default_precision = std::numeric_limits<std::size_t>::max();
277
278
286template<context Context, typename T, std::size_t precision = default_precision>
291 static constexpr auto format_string() {
292 if constexpr (precision != default_precision) {
293 return create_fmt_string_for_precision<precision>();
294 } else {
295 return ConstexprString<2>{"{}"};
296 }
297 }
301 static constexpr std::string_view range_begin_mark() { return "["; }
305 static constexpr std::string_view range_end_mark() { return "]"; }
309 static constexpr std::string_view range_separator() { return ", "; }
313 static constexpr void prepare_stream(std::ostream &stream) {
314 if constexpr (precision != default_precision) {
315 stream << std::setprecision(precision);
316 }
317 }
318};
319
320
324template<context Context, std::size_t precision = default_precision, typename T>
325requires(formattable<T> || streamable<T> || string_convertible_range<T>)
326static std::string stringify(T &&t) {
327 using FOpts = FormatOption<Context, std::remove_cvref_t<T>, precision>;
328
329 if constexpr (formattable<T>) {
330 static constexpr auto fmt = FOpts::format_string();
331 return std::format(fmt.as_view(), t);
332 } else if constexpr (streamable<T>) {
333 std::stringstream sstream;
334 FOpts::prepare_stream(sstream);
335 sstream << t;
336 return sstream.str();
337 } else {
338 std::stringstream sstream;
339
340 using std::ranges::begin;
341 using std::ranges::end;
342
343 sstream << FOpts::range_begin_mark();
344
345 for (auto it = begin(t); it != end(t); ++it) {
346 sstream << stringify<Context, precision>(*it);
347
348 if (it + 1 != end(t)) {
349 sstream << FOpts::range_separator();
350 }
351 }
352
353 sstream << FOpts::range_end_mark();
354
355 return sstream.str();
356 }
357}
358
359
364template<typename Context, std::size_t precision, typename T>
365concept stringify_supported = requires (std::remove_cvref_t<T> &t) {
366 { stringify<Context, precision>(t) } -> std::same_as<std::string>;
367};
368
369
370
371// Declaring some default logging contexts
372
376struct Generic : ContextBase<Generic, false> { static constexpr const char *name = "Generic"; };
377static_assert(context<Generic>);
381struct DataDump : ContextBase<DataDump, false> { static constexpr const char *name = "DataDump"; };
382static_assert(context<DataDump>);
383
384
385
392template< context Context >
394 template<typename ...Ts>
395 static void handle(const Logger &logger, Severity severity, Verbosity verbosity, std::string_view message, Ts...args) = delete;
396
397 template<typename ...Ts>
398 void handle(Severity severity, Verbosity verbosity, std::string_view message, Ts...args) = delete;
399};
400
401template< typename Context, typename ...Args >
402concept static_handler_exists = requires (const Logger &logger, Args ...args) {
403 LogHandler<Context>::handle(logger, std::declval<Severity>(), std::declval<Verbosity>(),
404 std::declval<std::string_view>(), args...);
405};
406template< typename Context, typename ...Args >
407concept member_handler_exists = requires (const LogHandler<Context> &handler, Args ...args) {
408 handler.handle(std::declval<Severity>(), std::declval<Verbosity>(),
409 std::declval<std::string_view>(), args...);
410};
411
415template< typename Context, typename ...Args >
416concept handler_exists = static_handler_exists<Context, Args...> || member_handler_exists<Context, Args...>;
417
418
419} // namespace molpro::linalg::itsolv::log
420
421
422
423namespace molpro::linalg::itsolv {
424
428class Logger {
429public:
431 : m_min_severity(min_severity), m_verbosity(verbosity), m_dump_data(enable_data_dumps) {}
432
436 template< log::context Context = log::Generic, std::size_t precision = log::default_precision, typename ...Ts >
437 void msg(log::Severity severity, log::Verbosity verbosity, std::string_view message, Ts && ...args) const {
438 log_ctx(Context::name, sizeof...(args));
439
440 if (verbosity < m_verbosity || severity < m_min_severity) {
441 return;
442 }
443
444 static_assert((log::stringify_supported<Context, precision, Ts> && ...), "All types passed to logger must be stringify-able");
445 if constexpr (Context::uses_fixed_arguments) {
446 static_assert(log::context_uses_correct_arg_types<Context, std::remove_cvref_t<Ts>...>, "Log function called with parameters that are inconsistent with associated context");
447 }
448
449 if constexpr (log::handler_exists<Context, Ts...>) {
450 using Handler = log::LogHandler<Context>;
451 if constexpr (log::static_handler_exists<Context, Ts...> && log::member_handler_exists<Context, Ts...>) {
452 // Need to figure out whether to call static or member function
453 const Handler *handler = dynamic_cast<const Handler *>(this);
454 if (handler) {
455 handler->handle(severity, verbosity, message, std::forward<Ts>(args)...);
456 } else {
457 Handler::handle(*this, severity, verbosity, message, std::forward<Ts>(args)...);
458 }
459 } else if constexpr (log::member_handler_exists<Context, Ts...>) {
460 dynamic_cast<const Handler &>(*this).handle(severity, verbosity, message, std::forward<Ts>(args)...);
461 } else {
462 static_assert(log::static_handler_exists<Context, Ts...>);
463 Handler::handle(*this, severity, verbosity, message, std::forward<Ts>(args)...);
464 }
465 } else if constexpr (sizeof...(args) > 0) {
466 std::string arg_str = ((log::stringify<Context, precision>(std::forward<Ts>(args)) + ", ") + ...);
467
468 if (!message.empty() && message.back() != ' ') {
469 // If message doesn't end with a blank, we assume simply concatenating the argument string
470 // to it would produce a nonsense message. Hopefully a generic separator will do better.
471 arg_str = " -> " + arg_str;
472 }
473
474 default_message_handler(Context::name, severity, verbosity, std::string(message) + arg_str);
475 } else {
476 default_message_handler(Context::name, severity, verbosity, message);
477 }
478 }
479
480 template< log::context Context = log::Generic, typename ...Ts>
481 void trace(std::string_view message, Ts && ...args) const {
482 msg<Context>(log::Severity::Normal, log::Verbosity::Trace, message, std::forward<Ts>(args)...);
483 }
484
485 template< log::context Context = log::Generic, typename ...Ts>
486 void debug(std::string_view message, Ts && ...args) const {
487 msg<Context>(log::Severity::Normal, log::Verbosity::Debug, message, std::forward<Ts>(args)...);
488 }
489
490 template< log::context Context = log::Generic, typename ...Ts>
491 void info(std::string_view message, Ts && ...args) const {
492 msg<Context>(log::Severity::Normal, log::Verbosity::Info, message, std::forward<Ts>(args)...);
493 }
494
495 template< log::context Context = log::Generic, typename ...Ts>
496 void warn(std::string_view message, Ts && ...args) const {
497 msg<Context>(log::Severity::Warning, log::Verbosity::None, message, std::forward<Ts>(args)...);
498 }
499
500 template< log::context Context = log::Generic, typename ...Ts>
501 void error(std::string_view message, Ts && ...args) const {
502 msg<Context>(log::Severity::Error, log::Verbosity::None, message, std::forward<Ts>(args)...);
503 }
504
505 template< log::context Context = log::Generic, typename ...Ts>
506 void fatal(std::string_view message, Ts && ...args) const {
507 msg<Context>(log::Severity::Fatal, log::Verbosity::None, message, std::forward<Ts>(args)...);
508 }
509
510
511 template<std::size_t precision = log::default_precision, typename ...Ts>
512 void data_dump(std::string_view what, Ts...data) const {
513 if (!m_dump_data) {
514 log_ctx(log::DataDump::name, sizeof...(data));
515 return;
516 }
517
518 msg<log::DataDump, precision>(log::Severity::Normal, log::Verbosity::Trace, what, data...);
519 }
520
521 log::Severity min_severity() const { return m_min_severity; }
522
523 void set_min_severity(log::Severity severity) { m_min_severity = severity; }
524
525 log::Verbosity verbosity() const { return m_verbosity; }
526
528
529 bool data_dumps_enabled() const { return m_dump_data; }
530
531 void enable_data_dumps(bool enable) { m_dump_data = enable; }
532
533protected:
534 // These functions are the traditional customization points by means of subclassing and overwriting
535
536 virtual void default_message_handler(std::string_view ctx, log::Severity severity, log::Verbosity verbosity, std::string_view msg) const {
537 bool add_colon = false;
538 switch (severity) {
540 break;
542 molpro::cout << "[WARNING]";
543 add_colon = true;
544 break;
546 molpro::cout << "[ERROR]:";
547 add_colon = true;
548 break;
550 molpro::cout << "[FATAL]:";
551 add_colon = true;
552 break;
553 }
554 switch (verbosity) {
556 break;
558 molpro::cout << "[INFO]";
559 add_colon = true;
560 break;
562 molpro::cout << "[DEBUG]";
563 add_colon = true;
564 break;
566 molpro::cout << "[TRACE]";
567 add_colon = true;
568 break;
569 };
570
571 if (ctx != log::Generic::name) {
572 molpro::cout << "[" << ctx << "]";
573 add_colon = true;
574 }
575
576 molpro::cout << (add_colon ? ": " : "") << msg;
577 if (severity >= log::Severity::Error) {
578 molpro::cout << std::endl;
579 } else {
580 molpro::cout << "\n";
581 }
582 }
583
584 virtual void log_ctx(std::string_view ctx, std::size_t num_args) const {
585 // By default we don't do anything
586 }
587
588private:
592 log::Severity m_min_severity = log::Severity::Normal;
594 bool m_dump_data = false;
595};
596
597} // namespace molpro::linalg::itsolv
598
599#endif // LINEARALGEBRA_SRC_MOLPRO_LINALG_ITSOLV_LOGGER_H
Definition: Logger.h:428
void set_min_severity(log::Severity severity)
Definition: Logger.h:523
void info(std::string_view message, Ts &&...args) const
Definition: Logger.h:491
Logger(log::Severity min_severity=log::Severity::Normal, log::Verbosity verbosity=log::Verbosity::Info, bool enable_data_dumps=false)
Definition: Logger.h:430
void warn(std::string_view message, Ts &&...args) const
Definition: Logger.h:496
void debug(std::string_view message, Ts &&...args) const
Definition: Logger.h:486
log::Verbosity verbosity() const
Definition: Logger.h:525
void msg(log::Severity severity, log::Verbosity verbosity, std::string_view message, Ts &&...args) const
Definition: Logger.h:437
void error(std::string_view message, Ts &&...args) const
Definition: Logger.h:501
void data_dump(std::string_view what, Ts...data) const
Definition: Logger.h:512
virtual void default_message_handler(std::string_view ctx, log::Severity severity, log::Verbosity verbosity, std::string_view msg) const
Definition: Logger.h:536
void fatal(std::string_view message, Ts &&...args) const
Definition: Logger.h:506
bool data_dumps_enabled() const
Definition: Logger.h:529
log::Severity min_severity() const
Definition: Logger.h:521
void trace(std::string_view message, Ts &&...args) const
Definition: Logger.h:481
void enable_data_dumps(bool enable)
Definition: Logger.h:531
virtual void log_ctx(std::string_view ctx, std::size_t num_args) const
Definition: Logger.h:584
void set_verbosity(log::Verbosity verbosity)
Definition: Logger.h:527
constexpr ~ConstexprString()
Definition: Logger.h:178
constexpr ConstexprString()
Definition: Logger.h:167
constexpr ConstexprString(const char(&literal)[N])
Definition: Logger.h:172
constexpr void push_back(char c)
Definition: Logger.h:180
constexpr char & operator[](std::size_t idx)
Definition: Logger.h:191
constexpr std::size_t size() const
Definition: Logger.h:189
constexpr std::string_view as_view() const
Definition: Logger.h:199
Definition: Logger.h:123
Definition: IterativeSolverTemplate.h:125
constexpr const std::size_t default_precision
Definition: Logger.h:276
constexpr std::size_t num_digits(std::size_t val)
Definition: Logger.h:216
Verbosity
Different levels of logging.
Definition: Logger.h:29
constexpr ConstexprString< num_digits(precision)+4 > create_fmt_string_for_precision()
Definition: Logger.h:237
Severity
Severity of a given message.
Definition: Logger.h:40
4-parameter interpolation of a 1-dimensional function given two points for which function values and ...
Definition: helper.h:11
static constexpr const char * name
Definition: Logger.h:65
static arg_t< Idx > get_arg(Ts &&...args)
Definition: Logger.h:113
std::tuple< std::remove_cvref_t< Args >... > arg_types_tuple
Definition: Logger.h:78
decltype(to_arg(std::declval< std::tuple_element_t< Idx, arg_types_tuple > >())) arg_t
Definition: Logger.h:107
static constexpr bool uses_fixed_arguments
Definition: Logger.h:75
static constexpr decltype(auto) to_arg(T &&arg)
Definition: Logger.h:91
static constexpr std::size_t num_args
Definition: Logger.h:83
static constexpr const char * name
Definition: Logger.h:381
static constexpr void prepare_stream(std::ostream &stream)
Definition: Logger.h:313
static constexpr std::string_view range_separator()
Definition: Logger.h:309
static constexpr auto format_string()
Definition: Logger.h:291
static constexpr std::string_view range_begin_mark()
Definition: Logger.h:301
static constexpr std::string_view range_end_mark()
Definition: Logger.h:305
Definition: Logger.h:376
static constexpr const char * name
Definition: Logger.h:376
void handle(Severity severity, Verbosity verbosity, std::string_view message, Ts...args)=delete
static void handle(const Logger &logger, Severity severity, Verbosity verbosity, std::string_view message, Ts...args)=delete