#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 namespace blt::logging { namespace detail { } struct logger_t { explicit logger_t() = default; template std::string log(std::string fmt, Args&&... args) { compile(std::move(fmt)); 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)...); process_strings(); return to_string(); } std::string to_string(); 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 && t >= 0) stream << ' '; } break; case fmt_sign_t::PLUS: if constexpr (std::is_arithmetic_v) { if (t >= 0) stream << '+'; } break; case fmt_sign_t::MINUS: break; } switch (type.type) { case fmt_type_t::BINARY: { if constexpr (std::is_trivially_copyable_v) { char buffer[sizeof(T)]; std::memcpy(buffer, &t, sizeof(T)); if (type.alternate_form) stream << '0' << (type.uppercase ? 'B' : 'b'); for (size_t i = 0; i < sizeof(T); ++i) { for (size_t j = 0; j < 8; ++j) stream << ((buffer[i] & (1 << j)) ? '1' : '0'); if (type.sign == fmt_sign_t::SPACE && i != sizeof(T) - 1) stream << ' '; } } else { stream << t; } break; } case fmt_type_t::CHAR: if constexpr (std::is_arithmetic_v || std::is_convertible_v) { stream << static_cast(t); } else { stream << t; } break; case fmt_type_t::GENERAL: if constexpr (std::is_arithmetic_v) { if (static_cast(t) > 10e12) exponential(stream); else fixed(stream); stream << t; } else { stream << t; } break; default: handle_type(stream, type); stream << t; } }; } void setup_stream(const fmt_spec_t& spec); 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::stringstream m_stream; fmt_parser_t m_parser; // 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& fmt); void newline(); logger_t& get_global_logger(); template void print(std::string fmt, Args&&... args) { auto& logger = get_global_logger(); print(logger.log(std::move(fmt), std::forward(args)...)); } template void println(std::string fmt, Args&&... args) { print(std::move(fmt), std::forward(args)...); newline(); } } #endif // BLT_LOGGING_LOGGING_H