#pragma once #include #include #ifdef __cpp_lib_source_location #include #endif namespace OpenVic2 { #ifndef __cpp_lib_source_location #include //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; #ifdef __cpp_lib_source_location using source_location = std::source_location; #else using source_location = OpenVic2::source_location; #endif static log_func_t info_func, error_func; static char const* get_filename(char const* filepath); template 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)), ...); 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 struct info { info(Ts&&... ts, const source_location& location = source_location::current()) { log{ info_func, std::forward(ts)..., location }; } }; template info(Ts&&...) -> info; template struct error { error(Ts&&... ts, const source_location& location = source_location::current()) { log{ error_func, std::forward(ts)..., location }; } }; template error(Ts&&...) -> error; }; }