| @@ -1,7 +1,7 @@ | |||||
| // | // | ||||
| // Lol Engine | // Lol Engine | ||||
| // | // | ||||
| // Copyright © 2010—2020 Sam Hocevar <sam@hocevar.net> | |||||
| // Copyright © 2010–2024 Sam Hocevar <sam@hocevar.net> | |||||
| // | // | ||||
| // Lol Engine is free software. It comes without any warranty, to | // Lol Engine is free software. It comes without any warranty, to | ||||
| // the extent permitted by applicable law. You can redistribute it | // the extent permitted by applicable law. You can redistribute it | ||||
| @@ -13,11 +13,13 @@ | |||||
| #pragma once | #pragma once | ||||
| #if !_DEBUG | #if !_DEBUG | ||||
| #include "../base/env.h" // sys::getenv | |||||
| #include "../base/env.h" // lol::sys::getenv | |||||
| #endif | #endif | ||||
| #include <cstdarg> // va_start | #include <cstdarg> // va_start | ||||
| #include <cstdio> // std::vfprintf | |||||
| #include <cstdio> // std::vfprintf | |||||
| #include <functional> // std::function | |||||
| #include <string> // std::string | |||||
| namespace lol | namespace lol | ||||
| { | { | ||||
| @@ -48,6 +50,12 @@ struct msg | |||||
| helper(message_type::error, fmt, args...); | helper(message_type::error, fmt, args...); | ||||
| } | } | ||||
| // Allow the user to provide their own logging function | |||||
| static void set_output(std::function<bool(std::string const&)> fn) | |||||
| { | |||||
| get_output() = fn; | |||||
| } | |||||
| private: | private: | ||||
| msg() = delete; | msg() = delete; | ||||
| @@ -59,6 +67,12 @@ private: | |||||
| error, | error, | ||||
| }; | }; | ||||
| static std::function<bool(std::string const&)>& get_output() | |||||
| { | |||||
| static std::function<bool(std::string const&)> output; | |||||
| return output; | |||||
| }; | |||||
| static void helper(message_type type, char const *fmt, ...) | static void helper(message_type type, char const *fmt, ...) | ||||
| { | { | ||||
| // Unless this is a debug build, ignore debug messages unless | // Unless this is a debug build, ignore debug messages unless | ||||
| @@ -72,7 +86,7 @@ private: | |||||
| } | } | ||||
| #endif | #endif | ||||
| static char const * const prefix[] = | |||||
| static std::string prefix[] | |||||
| { | { | ||||
| "DEBUG", | "DEBUG", | ||||
| "INFO", | "INFO", | ||||
| @@ -80,14 +94,25 @@ private: | |||||
| "ERROR", | "ERROR", | ||||
| }; | }; | ||||
| fprintf(stdout, "%s: ", prefix[int(type)]); | |||||
| std::string str = prefix[int(type)] + ": "; | |||||
| size_t pos = str.size(); | |||||
| va_list ap; | va_list ap; | ||||
| va_start(ap, fmt); | va_start(ap, fmt); | ||||
| vfprintf(stdout, fmt, ap); | |||||
| size_t count = std::vsnprintf(nullptr, 0, fmt, ap); | |||||
| str.resize(pos + count); | |||||
| // This is OK because C++ strings are indeed null-terminated | |||||
| std::vsnprintf(str.data() + pos, count + 1, fmt, ap); | |||||
| va_end(ap); | va_end(ap); | ||||
| fflush(stdout); | |||||
| // If a user function was provided, use it, and if it returns true, stop | |||||
| // processing the message immediately. | |||||
| if (auto &fn = get_output(); fn && fn(str)) | |||||
| return; | |||||
| fprintf(stderr, "%s", str.c_str()); | |||||
| fflush(stderr); | |||||
| } | } | ||||
| }; | }; | ||||
| } // namespace lol | } // namespace lol | ||||