summaryrefslogtreecommitdiff
path: root/src/openvic/Logger.hpp
blob: 2c603e2f9227647cc8cf00c77a4da58baceba8a9 (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
#pragma once

#include <functional>
#include <sstream>
#ifdef __cpp_lib_source_location
#include <source_location>
#endif

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;

      public:
      source_location(std::string f, int l, std::string n) : _file(f), _line(l),  _function(n) {}
      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 {
      using log_func_t = std::function<void(std::string&&)>;

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

      static log_func_t info_func, error_func;

      static char const* get_filename(char const* filepath);

      template <typename... Ts>
      struct log {
         log(log_func_t log_func, Ts&&... ts, const source_location& location) {
            if (log_func) {
               std::stringstream stream;
               stream << std::endl << get_filename(location.file_name()) << "(" << location.line() << ") `" << location.function_name() << "`: ";
               ((stream << std::forward<Ts>(ts)), ...);
               stream << std::endl;
               log_func(stream.str());
            }
         }
      };
   public:
      static void set_info_func(log_func_t log_func);
      static void set_error_func(log_func_t log_func);

      template <typename... Ts>
      struct info {
         info(Ts&&... ts, const source_location& location = source_location::current()) {
            log<Ts...>{ info_func, std::forward<Ts>(ts)..., location };
         }
      };

      template <typename... Ts>
      info(Ts&&...) -> info<Ts...>;

      template <typename... Ts>
      struct error {
         error(Ts&&... ts, const source_location& location = source_location::current()) {
            log<Ts...>{ error_func, std::forward<Ts>(ts)..., location };
         }
      };

      template <typename... Ts>
      error(Ts&&...) -> error<Ts...>;
   };
}