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
137template <typename T>
138concept pair_like = requires(const std::remove_cvref_t<T> &t) {
139 t.first;
140 t.second;
141};
142
146template<typename T>
147concept streamable = requires (std::ostream &stream, const std::remove_cvref_t<T> &t) {
148 stream << t;
149};
150
154template<typename T>
155concept formattable = requires (const std::remove_cvref_t<T> &t, std::format_context ctx) {
156 std::formatter<std::remove_cvref_t<T>>().format(t, ctx);
157};
158
163template <typename T>
167
171template<std::size_t MaxSize>
172requires(MaxSize > 0)
174public:
175 constexpr ConstexprString() : m_data(), m_size(0) {
176 m_data.fill('\0');
177 }
178 template<std::size_t N>
179 requires(N - 1 <= MaxSize)
180 constexpr ConstexprString(const char (&literal)[N]) : ConstexprString() {
181 // -1 as we don't want to copy the terminating null character
182 for (std::size_t i = 0; i < N - 1; ++i) {
183 push_back(literal[i]);
184 }
185 }
186 constexpr ~ConstexprString() {}
187
188 constexpr void push_back(char c) {
189 if (m_size == MaxSize) {
190 throw std::out_of_range("Maximum capacity has been reached - can't append further chars");
191 }
192
193 m_data.at(m_size) = c;
194 ++m_size;
195 }
196
197 constexpr std::size_t size() const { return m_size; }
198
199 constexpr char &operator[](std::size_t idx) {
200 if (idx >= m_size) {
201 throw std::out_of_range("Requested index is out of range");
202 }
203
204 return m_data.at(idx);
205 }
206
207 constexpr std::string_view as_view() const {
208 return {m_data.data(), m_size};
209 }
210
211 constexpr operator std::string_view() const {
212 return as_view();
213 }
214
215private:
216 std::array<char, MaxSize> m_data;
217 std::size_t m_size = 0;
218};
219
220
224constexpr std::size_t num_digits(std::size_t val) {
225 if (val == 0) {
226 return 1;
227 }
228
229 std::size_t num = 0;
230
231 while (val > 0){
232 val /= 10;
233 ++num;
234 }
235
236 return num;
237}
238
239
244template<std::size_t precision>
246 ConstexprString<num_digits(precision) + 4> str;
247
248 str.push_back('{');
249 str.push_back(':');
250 str.push_back('.');
251
252 if (precision == 0) {
253 str.push_back('0');
254 } else {
255 std::size_t begin = str.size();
256
257 // This create the digits in reverse order
258 std::size_t prec_val = precision;
259 while (prec_val > 0) {
260 std::size_t tmp = prec_val / 10;
261 std::size_t digit = prec_val - tmp * 10;
262 prec_val = tmp;
263
264 str.push_back('0' + static_cast<char>(digit));
265 }
266
267 std::size_t num_digits = str.size() - begin;
268
269 // Reverse order of digits in-place
270 for (std::size_t i = 0; i < num_digits / 2; ++i) {
271 std::swap(str[i + begin], str[str.size() - i - 1]);
272 }
273 }
274
275 str.push_back('}');
276
277 return str;
278}
279
280
284constexpr const std::size_t default_precision = std::numeric_limits<std::size_t>::max();
285
286
294template<context Context, typename T, std::size_t precision = default_precision>
299 static constexpr auto format_string() {
300 if constexpr (precision != default_precision) {
301 return create_fmt_string_for_precision<precision>();
302 } else {
303 return ConstexprString<2>{"{}"};
304 }
305 }
309 static constexpr std::string_view range_begin_mark() { return "["; }
313 static constexpr std::string_view range_end_mark() { return "]"; }
317 static constexpr std::string_view range_separator() { return ", "; }
321 static constexpr void prepare_stream(std::ostream &stream) {
322 if constexpr (precision != default_precision) {
323 stream << std::setprecision(precision);
324 }
325 }
326};
327
331template <context Context, std::size_t precision = default_precision, typename T>
332 requires(formattable<T> || streamable<T> || string_convertible_range<T> || pair_like<T>)
333static std::string stringify(T &&t) {
334 using FOpts = FormatOption<Context, std::remove_cvref_t<T>, precision>;
335
336 if constexpr (formattable<T>) {
337 static constexpr auto fmt = FOpts::format_string();
338 return std::format(fmt.as_view(), t);
339 } else if constexpr (streamable<T>) {
340 std::stringstream sstream;
341 FOpts::prepare_stream(sstream);
342 sstream << t;
343 return sstream.str();
344 } else if constexpr (pair_like<T>) {
345 std::stringstream sstream;
346
347 sstream << "{ " << stringify<Context, precision>(t.first) << ": " << stringify<Context, precision>(t.second)
348 << " }";
349
350 return sstream.str();
351 } else {
352 std::stringstream sstream;
353
354 using std::ranges::begin;
355 using std::ranges::end;
356
357 sstream << FOpts::range_begin_mark();
358
359 for (auto it = begin(t); it != end(t); ++it) {
360 sstream << stringify<Context, precision>(*it);
361
362 if (it + 1 != end(t)) {
363 sstream << FOpts::range_separator();
364 }
365 }
366
367 sstream << FOpts::range_end_mark();
368
369 return sstream.str();
370 }
371}
372
373
378template<typename Context, std::size_t precision, typename T>
379concept stringify_supported = requires (std::remove_cvref_t<T> &t) {
380 { stringify<Context, precision>(t) } -> std::same_as<std::string>;
381};
382
383
384
385// Declaring some default logging contexts
386
390struct Generic : ContextBase<Generic, false> { static constexpr const char *name = "Generic"; };
391static_assert(context<Generic>);
395struct DataDump : ContextBase<DataDump, false> { static constexpr const char *name = "DataDump"; };
396static_assert(context<DataDump>);
397
398
399
406template< context Context >
408 template<typename ...Ts>
409 static void handle(const Logger &logger, Severity severity, Verbosity verbosity, std::string_view message, Ts...args) = delete;
410
411 template<typename ...Ts>
412 void handle(Severity severity, Verbosity verbosity, std::string_view message, Ts...args) = delete;
413};
414
415template< typename Context, typename ...Args >
416concept static_handler_exists = requires (const Logger &logger, Args ...args) {
417 LogHandler<Context>::handle(logger, std::declval<Severity>(), std::declval<Verbosity>(),
418 std::declval<std::string_view>(), args...);
419};
420template< typename Context, typename ...Args >
421concept member_handler_exists = requires (const LogHandler<Context> &handler, Args ...args) {
422 handler.handle(std::declval<Severity>(), std::declval<Verbosity>(),
423 std::declval<std::string_view>(), args...);
424};
425
429template< typename Context, typename ...Args >
430concept handler_exists = static_handler_exists<Context, Args...> || member_handler_exists<Context, Args...>;
431
432
433} // namespace molpro::linalg::itsolv::log
434
435
436
437namespace molpro::linalg::itsolv {
438
442class Logger {
443public:
445 : m_min_severity(min_severity), m_verbosity(verbosity), m_dump_data(enable_data_dumps) {}
446
450 template< log::context Context = log::Generic, std::size_t precision = log::default_precision, typename ...Ts >
451 void msg(log::Severity severity, log::Verbosity verbosity, std::string_view message, Ts && ...args) const {
452 log_ctx(Context::name, sizeof...(args));
453
454 if (verbosity < m_verbosity || severity < m_min_severity) {
455 return;
456 }
457
458 static_assert((log::stringify_supported<Context, precision, Ts> && ...), "All types passed to logger must be stringify-able");
459 if constexpr (Context::uses_fixed_arguments) {
460 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");
461 }
462
463 if constexpr (log::handler_exists<Context, Ts...>) {
464 using Handler = log::LogHandler<Context>;
465 if constexpr (log::static_handler_exists<Context, Ts...> && log::member_handler_exists<Context, Ts...>) {
466 // Need to figure out whether to call static or member function
467 const Handler *handler = dynamic_cast<const Handler *>(this);
468 if (handler) {
469 handler->handle(severity, verbosity, message, std::forward<Ts>(args)...);
470 } else {
471 Handler::handle(*this, severity, verbosity, message, std::forward<Ts>(args)...);
472 }
473 } else if constexpr (log::member_handler_exists<Context, Ts...>) {
474 dynamic_cast<const Handler &>(*this).handle(severity, verbosity, message, std::forward<Ts>(args)...);
475 } else {
476 static_assert(log::static_handler_exists<Context, Ts...>);
477 Handler::handle(*this, severity, verbosity, message, std::forward<Ts>(args)...);
478 }
479 } else if constexpr (sizeof...(args) > 0) {
480 std::string arg_str = ((log::stringify<Context, precision>(std::forward<Ts>(args)) + ", ") + ...);
481
482 if (!message.empty() && message.back() != ' ') {
483 // If message doesn't end with a blank, we assume simply concatenating the argument string
484 // to it would produce a nonsense message. Hopefully a generic separator will do better.
485 arg_str = " -> " + arg_str;
486 }
487
488 default_message_handler(Context::name, severity, verbosity, std::string(message) + arg_str);
489 } else {
490 default_message_handler(Context::name, severity, verbosity, message);
491 }
492 }
493
494 template< log::context Context = log::Generic, typename ...Ts>
495 void trace(std::string_view message, Ts && ...args) const {
496 msg<Context>(log::Severity::Normal, log::Verbosity::Trace, message, std::forward<Ts>(args)...);
497 }
498
499 template< log::context Context = log::Generic, typename ...Ts>
500 void debug(std::string_view message, Ts && ...args) const {
501 msg<Context>(log::Severity::Normal, log::Verbosity::Debug, message, std::forward<Ts>(args)...);
502 }
503
504 template< log::context Context = log::Generic, typename ...Ts>
505 void info(std::string_view message, Ts && ...args) const {
506 msg<Context>(log::Severity::Normal, log::Verbosity::Info, message, std::forward<Ts>(args)...);
507 }
508
509 template< log::context Context = log::Generic, typename ...Ts>
510 void warn(std::string_view message, Ts && ...args) const {
511 msg<Context>(log::Severity::Warning, log::Verbosity::None, message, std::forward<Ts>(args)...);
512 }
513
514 template< log::context Context = log::Generic, typename ...Ts>
515 void error(std::string_view message, Ts && ...args) const {
516 msg<Context>(log::Severity::Error, log::Verbosity::None, message, std::forward<Ts>(args)...);
517 }
518
519 template< log::context Context = log::Generic, typename ...Ts>
520 void fatal(std::string_view message, Ts && ...args) const {
521 msg<Context>(log::Severity::Fatal, log::Verbosity::None, message, std::forward<Ts>(args)...);
522 }
523
524
525 template<std::size_t precision = log::default_precision, typename ...Ts>
526 void data_dump(std::string_view what, Ts...data) const {
527 if (!m_dump_data) {
528 log_ctx(log::DataDump::name, sizeof...(data));
529 return;
530 }
531
532 msg<log::DataDump, precision>(log::Severity::Normal, log::Verbosity::Trace, what, data...);
533 }
534
535 log::Severity min_severity() const { return m_min_severity; }
536
537 void set_min_severity(log::Severity severity) { m_min_severity = severity; }
538
539 log::Verbosity verbosity() const { return m_verbosity; }
540
542
543 bool data_dumps_enabled() const { return m_dump_data; }
544
545 void enable_data_dumps(bool enable) { m_dump_data = enable; }
546
547protected:
548 // These functions are the traditional customization points by means of subclassing and overwriting
549
550 virtual void default_message_handler(std::string_view ctx, log::Severity severity, log::Verbosity verbosity, std::string_view msg) const {
551 bool add_colon = false;
552 switch (severity) {
554 break;
556 molpro::cout << "[WARNING]";
557 add_colon = true;
558 break;
560 molpro::cout << "[ERROR]:";
561 add_colon = true;
562 break;
564 molpro::cout << "[FATAL]:";
565 add_colon = true;
566 break;
567 }
568 switch (verbosity) {
570 break;
572 molpro::cout << "[INFO]";
573 add_colon = true;
574 break;
576 molpro::cout << "[DEBUG]";
577 add_colon = true;
578 break;
580 molpro::cout << "[TRACE]";
581 add_colon = true;
582 break;
583 };
584
585 if (ctx != log::Generic::name) {
586 molpro::cout << "[" << ctx << "]";
587 add_colon = true;
588 }
589
590 molpro::cout << (add_colon ? ": " : "") << msg;
591 if (severity >= log::Severity::Error) {
592 molpro::cout << std::endl;
593 } else {
594 molpro::cout << "\n";
595 }
596 }
597
598 virtual void log_ctx(std::string_view ctx, std::size_t num_args) const {
599 // By default we don't do anything
600 }
601
602private:
606 log::Severity m_min_severity = log::Severity::Normal;
608 bool m_dump_data = false;
609};
610
611} // namespace molpro::linalg::itsolv
612
613#endif // LINEARALGEBRA_SRC_MOLPRO_LINALG_ITSOLV_LOGGER_H
Definition: Logger.h:442
void set_min_severity(log::Severity severity)
Definition: Logger.h:537
void info(std::string_view message, Ts &&...args) const
Definition: Logger.h:505
Logger(log::Severity min_severity=log::Severity::Normal, log::Verbosity verbosity=log::Verbosity::Info, bool enable_data_dumps=false)
Definition: Logger.h:444
void warn(std::string_view message, Ts &&...args) const
Definition: Logger.h:510
void debug(std::string_view message, Ts &&...args) const
Definition: Logger.h:500
log::Verbosity verbosity() const
Definition: Logger.h:539
void msg(log::Severity severity, log::Verbosity verbosity, std::string_view message, Ts &&...args) const
Definition: Logger.h:451
void error(std::string_view message, Ts &&...args) const
Definition: Logger.h:515
void data_dump(std::string_view what, Ts...data) const
Definition: Logger.h:526
virtual void default_message_handler(std::string_view ctx, log::Severity severity, log::Verbosity verbosity, std::string_view msg) const
Definition: Logger.h:550
void fatal(std::string_view message, Ts &&...args) const
Definition: Logger.h:520
bool data_dumps_enabled() const
Definition: Logger.h:543
log::Severity min_severity() const
Definition: Logger.h:535
void trace(std::string_view message, Ts &&...args) const
Definition: Logger.h:495
void enable_data_dumps(bool enable)
Definition: Logger.h:545
virtual void log_ctx(std::string_view ctx, std::size_t num_args) const
Definition: Logger.h:598
void set_verbosity(log::Verbosity verbosity)
Definition: Logger.h:541
constexpr ~ConstexprString()
Definition: Logger.h:186
constexpr ConstexprString()
Definition: Logger.h:175
constexpr ConstexprString(const char(&literal)[N])
Definition: Logger.h:180
constexpr void push_back(char c)
Definition: Logger.h:188
constexpr char & operator[](std::size_t idx)
Definition: Logger.h:199
constexpr std::size_t size() const
Definition: Logger.h:197
constexpr std::string_view as_view() const
Definition: Logger.h:207
Definition: Logger.h:123
Definition: IterativeSolverTemplate.h:127
constexpr const std::size_t default_precision
Definition: Logger.h:284
constexpr std::size_t num_digits(std::size_t val)
Definition: Logger.h:224
Verbosity
Different levels of logging.
Definition: Logger.h:29
constexpr ConstexprString< num_digits(precision)+4 > create_fmt_string_for_precision()
Definition: Logger.h:245
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:14
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:395
static constexpr void prepare_stream(std::ostream &stream)
Definition: Logger.h:321
static constexpr std::string_view range_separator()
Definition: Logger.h:317
static constexpr auto format_string()
Definition: Logger.h:299
static constexpr std::string_view range_begin_mark()
Definition: Logger.h:309
static constexpr std::string_view range_end_mark()
Definition: Logger.h:313
Definition: Logger.h:390
static constexpr const char * name
Definition: Logger.h:390
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