diff options
Diffstat (limited to 'src/crepe/util')
-rw-r--r-- | src/crepe/util/CMakeLists.txt | 12 | ||||
-rw-r--r-- | src/crepe/util/Log.cpp | 38 | ||||
-rw-r--r-- | src/crepe/util/Log.h | 63 | ||||
-rw-r--r-- | src/crepe/util/Log.hpp | 17 | ||||
-rw-r--r-- | src/crepe/util/LogColor.cpp | 46 | ||||
-rw-r--r-- | src/crepe/util/LogColor.h | 80 | ||||
-rw-r--r-- | src/crepe/util/OptionalRef.h | 66 | ||||
-rw-r--r-- | src/crepe/util/OptionalRef.hpp | 44 | ||||
-rw-r--r-- | src/crepe/util/Proxy.h | 34 | ||||
-rw-r--r-- | src/crepe/util/Proxy.hpp | 27 | ||||
-rw-r--r-- | src/crepe/util/color.h | 41 | ||||
-rw-r--r-- | src/crepe/util/dbg.h | 18 | ||||
-rw-r--r-- | src/crepe/util/log.cpp | 50 | ||||
-rw-r--r-- | src/crepe/util/log.h | 36 |
14 files changed, 442 insertions, 130 deletions
diff --git a/src/crepe/util/CMakeLists.txt b/src/crepe/util/CMakeLists.txt index 100f028..94ed906 100644 --- a/src/crepe/util/CMakeLists.txt +++ b/src/crepe/util/CMakeLists.txt @@ -1,9 +1,15 @@ target_sources(crepe PUBLIC - log.cpp + LogColor.cpp + Log.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES - color.h - log.h + LogColor.h + Log.h + Log.hpp + Proxy.h + Proxy.hpp + OptionalRef.h + OptionalRef.hpp ) diff --git a/src/crepe/util/Log.cpp b/src/crepe/util/Log.cpp new file mode 100644 index 0000000..ce25a1d --- /dev/null +++ b/src/crepe/util/Log.cpp @@ -0,0 +1,38 @@ +#include <iostream> +#include <string> + +#include "../api/Config.h" + +#include "Log.h" +#include "LogColor.h" + +using namespace crepe; +using namespace std; + +string Log::prefix(const Level & level) { + switch (level) { + case Level::TRACE: + return LogColor().fg_white().str("[TRACE]") + " "; + case Level::DEBUG: + return LogColor().fg_magenta().str("[DEBUG]") + " "; + case Level::INFO: + return LogColor().fg_blue().str("[INFO]") + " "; + case Level::WARNING: + return LogColor().fg_yellow().str("[WARN]") + " "; + case Level::ERROR: + return LogColor().fg_red().str("[ERROR]") + " "; + } + return ""; +} + +void Log::log(const Level & level, const string & msg) { + auto & cfg = Config::get_instance(); + if (level < cfg.log.level) return; + + string out = Log::prefix(level) + msg; + if (!out.ends_with("\n")) out += "\n"; + + // TODO: also log to file or smth + cout.write(out.data(), out.size()); + cout.flush(); +} diff --git a/src/crepe/util/Log.h b/src/crepe/util/Log.h new file mode 100644 index 0000000..b43fe30 --- /dev/null +++ b/src/crepe/util/Log.h @@ -0,0 +1,63 @@ +#pragma once + +#include <format> + +namespace crepe { + +/** + * \brief Logging utility + * + * This class is used to output log messages to the console and/or log files. + */ +class Log { +public: + //! Log message severity + enum Level { + TRACE, //!< Include (internal) function calls + DEBUG, //!< Include dbg_logf output + INFO, //!< General-purpose messages + WARNING, //!< Non-fatal errors + ERROR, //!< Fatal errors + }; + + /** + * \brief Log a formatted message + * + * \param level Message severity + * \param msg Formatted message + */ + static void log(const Level & level, const std::string & msg); + + /** + * \brief Format a message and log it + * + * \param level Message severity + * \param fmt Message format + * \param args Format arguments + */ + template <class... Args> + static void logf(const Level & level, std::format_string<Args...> fmt, Args &&... args); + + /** + * \brief Format a message and log it (with default severity \c INFO) + * + * \param fmt Message format + * \param args Format arguments + */ + template <class... Args> + static void logf(std::format_string<Args...> fmt, Args &&... args); + +private: + /** + * \brief Output a message prefix depending on the log level + * + * \param level Message severity + * + * \return Colored message severity prefix string + */ + static std::string prefix(const Level & level); +}; + +} // namespace crepe + +#include "Log.hpp" diff --git a/src/crepe/util/Log.hpp b/src/crepe/util/Log.hpp new file mode 100644 index 0000000..c2156cd --- /dev/null +++ b/src/crepe/util/Log.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "Log.h" + +namespace crepe { + +template <class... Args> +void Log::logf(std::format_string<Args...> fmt, Args &&... args) { + Log::logf(Level::INFO, fmt, std::forward<Args>(args)...); +} + +template <class... Args> +void Log::logf(const Level & level, std::format_string<Args...> fmt, Args &&... args) { + Log::log(level, std::format(fmt, std::forward<Args>(args)...)); +} + +} // namespace crepe diff --git a/src/crepe/util/LogColor.cpp b/src/crepe/util/LogColor.cpp new file mode 100644 index 0000000..5411898 --- /dev/null +++ b/src/crepe/util/LogColor.cpp @@ -0,0 +1,46 @@ +#include <cstdarg> + +#include "../api/Config.h" + +#include "LogColor.h" + +using namespace crepe; +using namespace std; + +static constexpr const char * RESET_CODE = "\e[0m"; + +const string LogColor::str(const string & content) const { + auto & cfg = Config::get_instance(); + string out = content; + if (cfg.log.color) out = this->code + out; + if (content.size() == 0) return out; + if (cfg.log.color) out = out + RESET_CODE; + return out; +} + +LogColor & LogColor::add_code(unsigned int code) { + this->code += format("\e[{}m", code); + return *this; +} + +LogColor & LogColor::reset() { + this->code = RESET_CODE; + return *this; +} + +LogColor & LogColor::fg_black(bool bright) { return this->add_code(bright ? 90 : 30); } +LogColor & LogColor::fg_red(bool bright) { return this->add_code(bright ? 91 : 31); } +LogColor & LogColor::fg_green(bool bright) { return this->add_code(bright ? 92 : 32); } +LogColor & LogColor::fg_yellow(bool bright) { return this->add_code(bright ? 93 : 33); } +LogColor & LogColor::fg_blue(bool bright) { return this->add_code(bright ? 94 : 34); } +LogColor & LogColor::fg_magenta(bool bright) { return this->add_code(bright ? 95 : 35); } +LogColor & LogColor::fg_cyan(bool bright) { return this->add_code(bright ? 96 : 36); } +LogColor & LogColor::fg_white(bool bright) { return this->add_code(bright ? 97 : 37); } +LogColor & LogColor::bg_black(bool bright) { return this->add_code(bright ? 100 : 40); } +LogColor & LogColor::bg_red(bool bright) { return this->add_code(bright ? 101 : 41); } +LogColor & LogColor::bg_green(bool bright) { return this->add_code(bright ? 102 : 42); } +LogColor & LogColor::bg_yellow(bool bright) { return this->add_code(bright ? 103 : 43); } +LogColor & LogColor::bg_blue(bool bright) { return this->add_code(bright ? 104 : 44); } +LogColor & LogColor::bg_magenta(bool bright) { return this->add_code(bright ? 105 : 45); } +LogColor & LogColor::bg_cyan(bool bright) { return this->add_code(bright ? 106 : 46); } +LogColor & LogColor::bg_white(bool bright) { return this->add_code(bright ? 107 : 47); } diff --git a/src/crepe/util/LogColor.h b/src/crepe/util/LogColor.h new file mode 100644 index 0000000..132fb94 --- /dev/null +++ b/src/crepe/util/LogColor.h @@ -0,0 +1,80 @@ +#pragma once + +#include <string> + +namespace crepe { + +/** + * \brief Utility class for coloring text using ANSI escape codes + * + * \note Most methods in this class return a reference to \c this, which may be + * used to chain multiple display attributes. + */ +class LogColor { +public: + /** + * \brief Get color code as STL string + * + * \param content If given, color this string and append a color reset escape sequence. + * + * \returns Color escape sequence + */ + const std::string str(const std::string & content = "") const; + +public: + //! Reset color to default foreground and background color + LogColor & reset(); + +public: + /** + * \name Foreground colors + * + * These functions set the foreground (text) color. The \c bright parameter + * makes the color brighter, or bold on some terminals. + * \{ + */ + LogColor & fg_black(bool bright = false); + LogColor & fg_red(bool bright = false); + LogColor & fg_green(bool bright = false); + LogColor & fg_yellow(bool bright = false); + LogColor & fg_blue(bool bright = false); + LogColor & fg_magenta(bool bright = false); + LogColor & fg_cyan(bool bright = false); + LogColor & fg_white(bool bright = false); + /// \} + +public: + /** + * \name Background colors + * + * These functions set the background color. The \c bright parameter makes + * the color brighter. + * \{ + */ + LogColor & bg_black(bool bright = false); + LogColor & bg_red(bool bright = false); + LogColor & bg_green(bool bright = false); + LogColor & bg_yellow(bool bright = false); + LogColor & bg_blue(bool bright = false); + LogColor & bg_magenta(bool bright = false); + LogColor & bg_cyan(bool bright = false); + LogColor & bg_white(bool bright = false); + /// \} + +private: + /** + * \brief Append SGR escape sequence to \c this->code + * + * \param code SGR attribute number + * + * See <https://en.wikipedia.org/wiki/ANSI_escape_code> for magic number + * reference. + */ + LogColor & add_code(unsigned int code); + +private: + //! Color escape sequence + std::string code = ""; +}; + +} // namespace crepe diff --git a/src/crepe/util/OptionalRef.h b/src/crepe/util/OptionalRef.h new file mode 100644 index 0000000..1b2cb3f --- /dev/null +++ b/src/crepe/util/OptionalRef.h @@ -0,0 +1,66 @@ +#pragma once + +namespace crepe { + +/** + * \brief Optional reference utility + * + * This class doesn't need to know the full definition of \c T to be used. + * + * \tparam T Value type + */ +template <typename T> +class OptionalRef { +public: + //! Initialize empty (nonexistant) reference + OptionalRef() = default; + //! Initialize reference with value + OptionalRef(T & ref); + /** + * \brief Assign new reference + * + * \param ref Reference to assign + * + * \return Reference to this (required for operator) + */ + OptionalRef<T> & operator=(T & ref); + /** + * \brief Retrieve this reference (cast) + * + * \returns Internal reference if it is set + * + * \throws std::runtime_error if this function is called while the reference it not set + */ + operator T &() const; + /** + * \brief Retrieve this reference (member access) + * + * \returns Internal reference if it is set + * + * \throws std::runtime_error if this function is called while the reference it not set + */ + T * operator->() const; + /** + * \brief Check if this reference is not empty + * + * \returns `true` if reference is set, or `false` if it is not + */ + explicit operator bool() const noexcept; + + /** + * \brief Make this reference empty + */ + void clear() noexcept; + +private: + /** + * \brief Reference to the value of type \c T + * + * \note This raw pointer is *not* managed, and only used as a reference! + */ + T * ref = nullptr; +}; + +} // namespace crepe + +#include "OptionalRef.hpp" diff --git a/src/crepe/util/OptionalRef.hpp b/src/crepe/util/OptionalRef.hpp new file mode 100644 index 0000000..5e36b3a --- /dev/null +++ b/src/crepe/util/OptionalRef.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include <stdexcept> + +#include "OptionalRef.h" + +namespace crepe { + +template <typename T> +OptionalRef<T>::OptionalRef(T & ref) { + this->ref = &ref; +} + +template <typename T> +OptionalRef<T>::operator T &() const { + if (this->ref == nullptr) + throw std::runtime_error("OptionalRef: attempt to dereference nullptr"); + return *this->ref; +} + +template <typename T> +T * OptionalRef<T>::operator->() const { + if (this->ref == nullptr) + throw std::runtime_error("OptionalRef: attempt to dereference nullptr"); + return this->ref; +} + +template <typename T> +OptionalRef<T> & OptionalRef<T>::operator=(T & ref) { + this->ref = &ref; + return *this; +} + +template <typename T> +OptionalRef<T>::operator bool() const noexcept { + return this->ref != nullptr; +} + +template <typename T> +void OptionalRef<T>::clear() noexcept { + this->ref = nullptr; +} + +} // namespace crepe diff --git a/src/crepe/util/Proxy.h b/src/crepe/util/Proxy.h new file mode 100644 index 0000000..789144a --- /dev/null +++ b/src/crepe/util/Proxy.h @@ -0,0 +1,34 @@ +#pragma once + +#include "ValueBroker.h" + +namespace crepe { + +/** + * \brief Utility wrapper for \c ValueBroker + * + * This class can be used to to wrap a ValueBroker instance so it behaves like a regular + * variable. + * + * \tparam T Type of the underlying variable + */ +template <typename T> +class Proxy { +public: + //! Set operator + Proxy & operator=(Proxy &); + //! Set operator + Proxy & operator=(const T &); + //! Get operator + operator const T &(); + +public: + Proxy(ValueBroker<T>); + +private: + ValueBroker<T> broker; +}; + +} // namespace crepe + +#include "Proxy.hpp" diff --git a/src/crepe/util/Proxy.hpp b/src/crepe/util/Proxy.hpp new file mode 100644 index 0000000..ef2b69f --- /dev/null +++ b/src/crepe/util/Proxy.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "Proxy.h" + +namespace crepe { + +template <typename T> +Proxy<T>::Proxy(ValueBroker<T> broker) : broker(broker) {} + +template <typename T> +Proxy<T> & Proxy<T>::operator=(const T & val) { + this->broker.set(val); + return *this; +} + +template <typename T> +Proxy<T> & Proxy<T>::operator=(Proxy & proxy) { + this->broker.set(T(proxy)); + return *this; +} + +template <typename T> +Proxy<T>::operator const T &() { + return this->broker.get(); +} + +} // namespace crepe diff --git a/src/crepe/util/color.h b/src/crepe/util/color.h deleted file mode 100644 index 066c9d3..0000000 --- a/src/crepe/util/color.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -namespace crepe::util::color { - -constexpr const char * RESET = "\e[0m"; - -constexpr const char * FG_BLACK = "\e[30m"; -constexpr const char * FG_RED = "\e[31m"; -constexpr const char * FG_GREEN = "\e[32m"; -constexpr const char * FG_YELLOW = "\e[33m"; -constexpr const char * FG_BLUE = "\e[34m"; -constexpr const char * FG_MAGENTA = "\e[35m"; -constexpr const char * FG_CYAN = "\e[36m"; -constexpr const char * FG_WHITE = "\e[37m"; -constexpr const char * BG_BLACK = "\e[40m"; -constexpr const char * BG_RED = "\e[41m"; -constexpr const char * BG_GREEN = "\e[42m"; -constexpr const char * BG_YELLOW = "\e[43m"; -constexpr const char * BG_BLUE = "\e[44m"; -constexpr const char * BG_MAGENTA = "\e[45m"; -constexpr const char * BG_CYAN = "\e[46m"; -constexpr const char * BG_WHITE = "\e[47m"; - -constexpr const char * FG_BLACK_BRIGHT = "\e[90m"; -constexpr const char * FG_RED_BRIGHT = "\e[91m"; -constexpr const char * FG_GREEN_BRIGHT = "\e[92m"; -constexpr const char * FG_YELLOW_BRIGHT = "\e[93m"; -constexpr const char * FG_BLUE_BRIGHT = "\e[94m"; -constexpr const char * FG_MAGENTA_BRIGHT = "\e[95m"; -constexpr const char * FG_CYAN_BRIGHT = "\e[96m"; -constexpr const char * FG_WHITE_BRIGHT = "\e[97m"; -constexpr const char * BG_BLACK_BRIGHT = "\e[100m"; -constexpr const char * BG_RED_BRIGHT = "\e[101m"; -constexpr const char * BG_GREEN_BRIGHT = "\e[102m"; -constexpr const char * BG_YELLOW_BRIGHT = "\e[103m"; -constexpr const char * BG_BLUE_BRIGHT = "\e[104m"; -constexpr const char * BG_MAGENTA_BRIGHT = "\e[105m"; -constexpr const char * BG_CYAN_BRIGHT = "\e[106m"; -constexpr const char * BG_WHITE_BRIGHT = "\e[107m"; - -} // namespace crepe::util::color diff --git a/src/crepe/util/dbg.h b/src/crepe/util/dbg.h new file mode 100644 index 0000000..c7283ee --- /dev/null +++ b/src/crepe/util/dbg.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Log.h" +#include "LogColor.h" + +// utility macros +#define _crepe_logf_here(level, fmt, ...) \ + crepe::Log::logf(level, "{}" fmt, \ + crepe::LogColor().fg_white(false).str(std::format( \ + "{} ({}:{})", __PRETTY_FUNCTION__, __FILE_NAME__, __LINE__)), \ + __VA_ARGS__) + +// very illegal global function-style macros +// NOLINTBEGIN +#define dbg_logf(fmt, ...) _crepe_logf_here(crepe::Log::Level::DEBUG, ": " fmt, __VA_ARGS__) +#define dbg_log(str) _crepe_logf_here(crepe::Log::Level::DEBUG, ": {}", str) +#define dbg_trace() _crepe_logf_here(crepe::Log::Level::TRACE, "", "") +// NOLINTEND diff --git a/src/crepe/util/log.cpp b/src/crepe/util/log.cpp deleted file mode 100644 index f91d52c..0000000 --- a/src/crepe/util/log.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include <cstdarg> -#include <cstdio> -#include <cstdlib> -#include <string> - -#include "log.h" - -using namespace crepe::util; - -static const char * const LOG_PREFIX[] = { - [log_level::DEBUG] = "[DBG] ", - [log_level::INFO] = "[INFO] ", - [log_level::WARNING] = "[WARN] ", - [log_level::ERROR] = "[ERR] ", -}; - -static void va_logf(enum log_level level, va_list args, const std::string fmt) { - va_list args_copy; - va_copy(args_copy, args); - - // prepend log level and ensure newline - std::string format_fixed = LOG_PREFIX[level] + fmt; - if (!format_fixed.ends_with("\n")) format_fixed += "\n"; - - size_t sz = vsnprintf(NULL, 0, format_fixed.c_str(), args_copy) + 1; - char * msg = (char *) malloc(sz); - va_end(args_copy); - - vsnprintf(msg, sz, format_fixed.c_str(), args); - - // TODO: also log to file or smth - printf("%s", msg); - fflush(stdout); - - free(msg); -} - -void crepe::util::logf(const char * fmt, ...) { - va_list args; - va_start(args, fmt); - va_logf(crepe::util::log_level::DEBUG, args, fmt); - va_end(args); -} - -void crepe::util::logf(log_level level, const char * fmt, ...) { - va_list args; - va_start(args, fmt); - va_logf(level, args, fmt); - va_end(args); -} diff --git a/src/crepe/util/log.h b/src/crepe/util/log.h deleted file mode 100644 index fa5f633..0000000 --- a/src/crepe/util/log.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -// allow user to disable debug macros -#ifndef CREPE_DISABLE_MACROS - -#include "color.h" - -// utility macros -#define _crepe_logf_here(fmt, ...) \ - crepe::util::logf(util::log_level::DEBUG, "%s%s (%s:%d)" fmt "\n", \ - crepe::util::color::FG_WHITE, __PRETTY_FUNCTION__, \ - __FILE_NAME__, __LINE__, crepe::util::color::RESET, \ - __VA_ARGS__) - -// very illegal global function-style macros -// NOLINTBEGIN -#define dbg_logf(fmt, ...) _crepe_logf_here(": " fmt, __VA_ARGS__) -#define dbg_log(str) _crepe_logf_here("%s: " str, "") -#define dbg_trace() _crepe_logf_here("%s", "") -// NOLINTEND - -#endif - -namespace crepe::util { - -enum log_level { - DEBUG, - INFO, - WARNING, - ERROR, -}; - -void logf(const char * fmt, ...); -void logf(enum log_level level, const char * fmt, ...); - -} // namespace crepe::util |