aboutsummaryrefslogtreecommitdiff
path: root/src/openvic-simulation/utility/Logger.hpp
blob: 7a2c3d0b73dd2dd9b079cdb5e3222830b88a343d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#pragma once

#include <functional>
#include <iostream>
#include <queue>
#include <sstream>

#ifdef __cpp_lib_source_location
#include <source_location>
#endif

#include "openvic-simulation/utility/StringUtils.hpp"

namespace OpenVic {

#ifndef __cpp_lib_source_location
#include <string>
   // Implementation of std::source_location for compilers that do not support it
   // Note: uses non-standard extensions that are supported by Clang, GCC, and MSVC
   // https://clang.llvm.org/docs/LanguageExtensions.html#source-location-builtins
   // https://stackoverflow.com/a/67970107
   class source_location {
      std::string _file;
      int _line;
      std::string _function;

      source_location(std::string f, int l, std::string n) : _file(f), _line(l), _function(n) {}

   public:
      static source_location current(
         std::string f = __builtin_FILE(), int l = __builtin_LINE(), std::string n = __builtin_FUNCTION()
      ) {
         return source_location(f, l, n);
      }

      inline char const* file_name() const {
         return _file.c_str();
      }
      inline int line() const {
         return _line;
      }
      inline char const* function_name() const {
         return _function.c_str();
      }
   };
#endif

   class Logger final {
      using log_func_t = std::function<void(std::string&&)>;
      using log_queue_t = std::queue<std::string>;

#ifdef __cpp_lib_source_location
      using source_location = std::source_location;
#else
      using source_location = OpenVic::source_location;
#endif

   public:
      static void set_logger_funcs() {
         set_info_func([](std::string&& str) {
            std::cout << "[INFO] " << str;
         });
         set_warning_func([](std::string&& str) {
            std::cerr << "[WARNING] " << str;
         });
         set_error_func([](std::string&& str) {
            std::cerr << "[ERROR] " << str;
         });
      }

   private:
      struct log_channel_t {
         log_func_t func;
         log_queue_t queue;
         size_t message_count;
      };

      template<typename... Args>
      struct log {
         log(log_channel_t& log_channel, Args&&... args, source_location const& location) {
            std::stringstream stream;
            stream << StringUtils::get_filename(location.file_name()) << "("
               /* Function name removed to reduce clutter. It is already included
               * in Godot's print functions, so this was repeating it. */
               //<< location.line() << ") `" << location.function_name() << "`: ";
               << location.line() << "): ";
            ((stream << std::forward<Args>(args)), ...);
            stream << std::endl;
            log_channel.queue.push(stream.str());
            if (log_channel.func) {
               do {
                  log_channel.func(std::move(log_channel.queue.front()));
                  log_channel.queue.pop();
                  /* Only count printed messages, so that message_count matches what is seen in the console. */
                  log_channel.message_count++;
               } while (!log_channel.queue.empty());
            }
         }
      };

#define LOG_FUNC(name) \
private: \
   static inline log_channel_t name##_channel {}; \
\
public: \
   static inline void set_##name##_func(log_func_t log_func) { \
      name##_channel.func = log_func; \
   } \
   static inline size_t get_##name##_count() { \
      return name##_channel.message_count; \
   } \
   template<typename... Args> \
   struct name { \
      name(Args&&... args, source_location const& location = source_location::current()) { \
         log<Args...> { name##_channel, std::forward<Args>(args)..., location }; \
      } \
   }; \
   template<typename... Args> \
   name(Args&&...) -> name<Args...>;

      LOG_FUNC(info)
      LOG_FUNC(warning)
      LOG_FUNC(error)

#undef LOG_FUNC

      template<typename... Args>
      static inline constexpr void warn_or_error(bool warn, Args&&... args) {
         if (warn) {
            warning(std::forward<Args>(args)...);
         } else {
            error(std::forward<Args>(args)...);
         }
      }
   };
}