#pragma once /* * Copyright (C) 2024 Brett Terpstra * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BLT_LOGGING_LOGGING_H #define BLT_LOGGING_LOGGING_H #include #include #include #include #include #include #include #include #include namespace blt::logging { struct logger_t { explicit logger_t(std::ostream& stream): m_stream(stream) {} template std::string log(std::string fmt, Args&&... args) { auto sequence = std::make_integer_sequence{}; m_arg_print_funcs.clear(); m_arg_print_funcs.resize(sizeof...(Args)); create_conv_funcs(sequence, std::forward(args)...); compile(std::move(fmt)); process_strings(); return to_string(); } [[nodiscard]] std::string to_string() const; private: template void create_conv_funcs(std::integer_sequence, Args&&... args) { ((handle_func(std::forward(args))), ...); } template void handle_func(const T& t) { m_arg_print_funcs[index] = [&t, this](std::ostream& stream, const fmt_spec_t& type) { switch (type.sign) { case fmt_sign_t::SPACE: if constexpr (std::is_arithmetic_v) { if (type.type != fmt_type_t::BINARY && static_cast(t) >= 0l) stream << ' '; } break; case fmt_sign_t::PLUS: case fmt_sign_t::MINUS: break; } switch (type.type) { case fmt_type_t::BINARY: { if constexpr (std::is_trivially_copyable_v) { // copy bytes of type char buffer[sizeof(T)]; std::memcpy(buffer, &t, sizeof(T)); // display prefix if (type.alternate_form) stream << '0' << (type.uppercase ? 'B' : 'b'); // print bytes for (size_t i = 0; i < sizeof(T); ++i) { // print bits for (size_t j = 0; j < 8; ++j) stream << ((buffer[i] & (1 << j)) ? '1' : '0'); // special seperator defined via sign (weird hack, change?) if (type.sign == fmt_sign_t::SPACE && i != sizeof(T) - 1) stream << ' '; } } else { if constexpr (blt::meta::is_streamable_v) stream << t; else stream << "{INVALID TYPE}"; } break; } case fmt_type_t::CHAR: if constexpr (std::is_arithmetic_v || std::is_convertible_v) { stream << static_cast(t); } else { if constexpr (blt::meta::is_streamable_v) stream << t; else stream << "{INVALID TYPE}"; } break; case fmt_type_t::TYPE: stream << blt::type_string(); break; default: handle_type(stream, type); if constexpr (blt::meta::is_streamable_v) { if constexpr (std::is_same_v || std::is_same_v) stream << static_cast(t); else if constexpr (std::is_same_v) stream << static_cast(t); else stream << t; } else stream << "{INVALID TYPE}"; } }; } [[nodiscard]] size_t find_ending_brace(size_t begin) const; void setup_stream(const fmt_spec_t& spec) const; void process_strings(); static void handle_type(std::ostream& stream, const fmt_spec_t& spec); static void exponential(std::ostream& stream); static void fixed(std::ostream& stream); void compile(std::string fmt); std::optional> consume_to_next_fmt(); std::string m_fmt; std::ostream& m_stream; fmt_parser_t m_parser{m_arg_print_funcs}; // normal sections of string std::vector m_string_sections; // processed format specs std::vector m_fmt_specs; std::vector> m_arg_print_funcs; size_t m_last_fmt_pos = 0; size_t m_arg_pos = 0; }; void print(const std::string& str); void newline(); logger_t& get_global_logger(); logging_config_t& get_global_config(); void set_thread_name(const std::string& name); const std::string& get_thread_name(); template void print(std::string fmt, Args&&... args) { auto& logger = get_global_logger(); print(logger.log(std::move(fmt), std::forward(args)...)); } template void print(std::ostream& stream, std::string fmt, Args&&... args) { auto& logger = get_global_logger(); stream << logger.log(std::move(fmt), std::forward(args)...); } template void println(std::string fmt, Args&&... args) { print(std::move(fmt), std::forward(args)...); newline(); } template void println(std::ostream& stream, std::string fmt, Args&&... args) { print(stream, std::move(fmt), std::forward(args)...); stream << std::endl; } template void log(log_level_t level, const char* file, const i32 line, std::string fmt, Args&&... args) { auto& logger = get_global_logger(); auto& config = get_global_config(); auto user_str = logger.log(std::move(fmt), std::forward(args)...); auto log_fmt_str = config.generate(user_str, get_thread_name(), level, file, line); if (log_fmt_str) print(std::move(*log_fmt_str)); } namespace detail {} } #define BLT_LOG(level, fmt, ...) blt::logging::log(level, __FILE__, __LINE__, fmt, ##__VA_ARGS__) #endif // BLT_LOGGING_LOGGING_H