basic logger works

main
Brett 2025-03-04 00:44:48 -05:00
parent 74878d6b43
commit b2c3820ed0
6 changed files with 280 additions and 209 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20)
include(cmake/color.cmake)
set(BLT_VERSION 5.1.2)
set(BLT_VERSION 5.1.3)
set(BLT_TARGET BLT)

View File

@ -21,12 +21,18 @@
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
#include <blt/meta/meta.h>
#include <cstring>
#include <functional>
#include <blt/logging/fmt_tokenizer.h>
namespace blt::logging
{
namespace detail
{
}
struct logger_t
{
explicit logger_t() = default;
@ -36,83 +42,116 @@ namespace blt::logging
{
compile(std::move(fmt));
auto sequence = std::make_integer_sequence<size_t, sizeof...(Args)>{};
while (auto pair = consume_until_fmt())
{
auto [begin, end] = *pair;
if (end - begin > 0)
{
auto format_data = handle_fmt(m_fmt.substr(begin + 1, begin - end - 1));
auto [arg_pos, fmt_type] = format_data;
if (arg_pos == -1)
arg_pos = static_cast<i64>(m_arg_pos++);
if (fmt_type)
{
if (fmt_type == fmt_type_t::GENERAL)
{
apply_func([this](auto&& value)
{
if (static_cast<u64>(value) > 0xFFFFFFFFFul)
exponential();
else
fixed();
m_stream << std::forward<decltype(value)>(value);
}, arg_pos, sequence, std::forward<Args>(args)...);
} else if (fmt_type == fmt_type_t::CHAR)
{
} else if (fmt_type == fmt_type_t::BINARY)
{
}
}
else
{
apply_func([this](auto&& value)
{
m_stream << std::forward<decltype(value)>(value);
}, arg_pos, sequence, std::forward<Args>(args)...);
}
}
else
apply_func([this](auto&& value)
{
m_stream << std::forward<decltype(value)>(value);
}, m_arg_pos++, sequence, std::forward<Args>(args)...);
}
finish();
m_arg_print_funcs.clear();
m_arg_print_funcs.resize(sizeof...(Args));
create_conv_funcs(sequence, std::forward<Args>(args)...);
process_strings();
return to_string();
}
std::string to_string();
private:
template <typename Func, typename... Args, size_t... Indexes>
void apply_func(const Func& func, const size_t arg, std::integer_sequence<size_t, Indexes...>, Args&&... args)
template <typename... Args, size_t... Indexes>
void create_conv_funcs(std::integer_sequence<size_t, Indexes...>, Args&&... args)
{
((handle_func<Indexes>(func, arg, std::forward<Args>(args))), ...);
((handle_func<Indexes>(std::forward<Args>(args))), ...);
}
template <size_t index, typename Func, typename T>
void handle_func(const Func& func, const size_t arg, T&& t)
template <size_t index, typename T>
void handle_func(const T& t)
{
if (index == arg)
func(std::forward<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<T>)
{
if (t >= 0)
stream << ' ';
}
break;
case fmt_sign_t::PLUS:
if constexpr (std::is_arithmetic_v<T>)
{
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<T>)
{
char buffer[sizeof(T)];
std::memcpy(buffer, &t, sizeof(T));
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<T> || std::is_convertible_v<T, char>)
{
stream << static_cast<char>(t);
} else
{
stream << t;
}
break;
case fmt_type_t::GENERAL:
if constexpr (std::is_arithmetic_v<T>)
{
if (static_cast<u64>(t) > 10e12)
exponential(stream);
else
fixed(stream);
stream << t;
} else
{
stream << t;
}
break;
default:
handle_type(stream, type.type);
stream << t;
}
};
}
[[nodiscard]] std::pair<i64, std::optional<fmt_type_t>> handle_fmt(std::string_view fmt);
void setup_stream(const fmt_spec_t& spec);
void process_strings();
static void handle_type(std::ostream& stream, fmt_type_t type);
void exponential();
void fixed();
static void exponential(std::ostream& stream);
static void fixed(std::ostream& stream);
void compile(std::string fmt);
std::optional<std::pair<size_t, size_t>> consume_until_fmt();
void finish();
std::optional<std::pair<size_t, size_t>> consume_to_next_fmt();
std::string m_fmt;
std::stringstream m_stream;
fmt_parser_t m_parser;
// normal sections of string
std::vector<std::string_view> m_string_sections;
// processed format specs
std::vector<fmt_spec_t> m_fmt_specs;
std::vector<std::function<void(std::ostream&, const fmt_spec_t&)>> m_arg_print_funcs;
size_t m_last_fmt_pos = 0;
size_t m_arg_pos = 0;
};

@ -1 +1 @@
Subproject commit 7ef2e733416953b222851f9a360d7fc72d068ee5
Subproject commit 93201da2ba5a6aba0a6e57ada64973555629b3e3

View File

@ -16,6 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <sstream>
#include <blt/logging/fmt_tokenizer.h>
namespace blt::logging
@ -97,10 +98,7 @@ namespace blt::logging
void fmt_parser_t::parse_fmt_field()
{
if (!has_next())
{
std::cerr << "Expected token when parsing format field" << std::endl;
std::exit(EXIT_FAILURE);
}
throw std::runtime_error("Expected token when parsing format field");
const auto [type, value] = peek();
if (type == fmt_token_type::COLON)
{
@ -118,16 +116,14 @@ namespace blt::logging
parse_fmt_spec_stage_1();
}
else
{
std::cerr << "Expected ':' when parsing format field after arg id!" << std::endl;
std::exit(EXIT_FAILURE);
}
throw std::runtime_error("Expected ':' when parsing format field after arg id!");
}
}
else
{
std::cerr << "Expected unknown token '" << static_cast<u8>(type) << "' value '" << value << "' when parsing format field" << std::endl;
std::exit(EXIT_FAILURE);
std::stringstream ss;
ss << "Expected unknown token '" << static_cast<u8>(type) << "' value '" << value << "' when parsing format field";
throw std::runtime_error(ss.str());
}
if (has_next())
parse_type();
@ -136,15 +132,13 @@ namespace blt::logging
void fmt_parser_t::parse_arg_id()
{
if (!has_next())
{
std::cerr << "Missing token when parsing arg id" << std::endl;
std::exit(EXIT_FAILURE);
}
throw std::runtime_error("Missing token when parsing arg id");
const auto [type, value] = next();
if (type != fmt_token_type::NUMBER)
{
std::cerr << "Expected number when parsing arg id, unexpected value '" << value << '\'' << std::endl;
std::exit(EXIT_FAILURE);
std::stringstream ss;
ss << "Expected number when parsing arg id, unexpected value '" << value << '\'';
throw std::runtime_error(ss.str());
}
m_spec.arg_id = std::stoll(std::string(value));
}
@ -159,8 +153,11 @@ namespace blt::logging
{
case fmt_token_type::STRING:
case fmt_token_type::COLON:
std::cerr << "(Stage 1) Invalid token type " << static_cast<u8>(type) << " value " << value << std::endl;
std::exit(EXIT_FAILURE);
{
std::stringstream ss;
ss << "(Stage 1) Invalid token type " << static_cast<u8>(type) << " value " << value;
throw std::runtime_error(ss.str());
}
case fmt_token_type::NUMBER:
parse_width();
parse_fmt_spec_stage_3();
@ -191,8 +188,11 @@ namespace blt::logging
case fmt_token_type::MINUS:
case fmt_token_type::PLUS:
case fmt_token_type::SPACE:
std::cerr << "(Stage 2) Invalid token type " << static_cast<u8>(type) << " value " << value << std::endl;
std::exit(EXIT_FAILURE);
{
std::stringstream ss;
ss << "(Stage 2) Invalid token type " << static_cast<u8>(type) << " value " << value;
throw std::runtime_error(ss.str());
}
case fmt_token_type::NUMBER:
parse_width();
parse_fmt_spec_stage_3();
@ -217,8 +217,11 @@ namespace blt::logging
case fmt_token_type::PLUS:
case fmt_token_type::SPACE:
case fmt_token_type::NUMBER:
std::cerr << "(Stage 3) Invalid token type " << static_cast<u8>(type) << " value " << value << std::endl;
std::exit(EXIT_FAILURE);
{
std::stringstream ss;
ss << "(Stage 3) Invalid token type " << static_cast<u8>(type) << " value " << value;
throw std::runtime_error(ss.str());
}
case fmt_token_type::DOT:
consume();
parse_precision();
@ -231,8 +234,9 @@ namespace blt::logging
auto [_, value] = next();
if (value.size() > 1)
{
std::cerr << "Sign contains more than one character, we are not sure how to interpret this. Value '" << value << "'\n";
std::exit(EXIT_FAILURE);
std::stringstream ss;
ss << "Sign contains more than one character, we are not sure how to interpret this. Value '" << value << "'";
throw std::runtime_error(ss.str());
}
switch (value[0])
{
@ -246,8 +250,11 @@ namespace blt::logging
m_spec.sign = fmt_sign_t::SPACE;
break;
default:
std::cerr << "Invalid sign " << value[0] << std::endl;
std::exit(EXIT_FAILURE);
{
std::stringstream ss;
ss << "Invalid sign " << value[0];
throw std::runtime_error(ss.str());
}
}
}
@ -262,10 +269,7 @@ namespace blt::logging
void fmt_parser_t::parse_precision()
{
if (!has_next())
{
std::cerr << "Missing token when parsing precision" << std::endl;
std::exit(EXIT_FAILURE);
}
throw std::runtime_error("Missing token when parsing precision");
auto [_, value] = next();
m_spec.precision = std::stoll(std::string(value));
}
@ -273,15 +277,13 @@ namespace blt::logging
void fmt_parser_t::parse_type()
{
if (!has_next())
{
std::cerr << "Missing token when parsing type" << std::endl;
std::exit(EXIT_FAILURE);
}
throw std::runtime_error("Missing token when parsing type");
auto [_, value] = next();
if (value.size() != 1)
{
std::cerr << "Type contains more than one character, we are not sure how to interpret this value '" << value << "'\n";
std::exit(EXIT_FAILURE);
std::stringstream ss;
ss << "Type contains more than one character, we are not sure how to interpret this value '" << value << "'";
throw std::runtime_error(ss.str());
}
m_spec.uppercase = std::isupper(value.front());
switch (value.front())
@ -320,8 +322,9 @@ namespace blt::logging
m_spec.type = fmt_type_t::GENERAL;
break;
default:
std::cerr << "Invalid type " << value << std::endl;
std::exit(EXIT_FAILURE);
std::stringstream ss;
ss << "Invalid type " << value;
throw std::runtime_error(ss.str());
}
}
}

View File

@ -23,122 +23,147 @@
namespace blt::logging
{
struct logging_thread_context_t
{
logger_t logger;
};
struct logging_thread_context_t
{
logger_t logger;
};
std::string logger_t::to_string()
{
auto str = m_stream.str();
m_stream.str("");
m_stream.clear();
return str;
}
std::string logger_t::to_string()
{
auto str = m_stream.str();
m_stream.str("");
m_stream.clear();
return str;
}
std::pair<i64, std::optional<fmt_type_t>> logger_t::handle_fmt(const std::string_view fmt)
{
const auto spec = m_parser.parse(fmt);
if (spec.leading_zeros)
m_stream << std::setfill('0');
else
m_stream << std::setfill(' ');
if (spec.width > 0)
m_stream << std::setw(static_cast<i32>(spec.width));
else
m_stream << std::setw(0);
if (spec.precision > 0)
m_stream << std::setprecision(static_cast<i32>(spec.precision));
else
m_stream << std::setprecision(2);
if (spec.uppercase)
m_stream << std::uppercase;
else
m_stream << std::nouppercase;
std::optional<fmt_type_t> type;
switch (spec.type)
{
case fmt_type_t::BINARY:
case fmt_type_t::CHAR:
case fmt_type_t::GENERAL:
type = spec.type;
break;
case fmt_type_t::DECIMAL:
m_stream << std::dec;
break;
case fmt_type_t::OCTAL:
m_stream << std::oct;
break;
case fmt_type_t::HEX:
m_stream << std::hex;
break;
case fmt_type_t::HEX_FLOAT:
m_stream << std::hexfloat;
break;
case fmt_type_t::EXPONENT:
m_stream << std::scientific;
break;
case fmt_type_t::FIXED_POINT:
m_stream << std::fixed;
break;
}
return {spec.arg_id, type};
}
void logger_t::setup_stream(const fmt_spec_t& spec)
{
if (spec.leading_zeros)
m_stream << std::setfill('0');
else
m_stream << std::setfill(' ');
if (spec.width > 0)
m_stream << std::setw(static_cast<i32>(spec.width));
else
m_stream << std::setw(0);
if (spec.precision > 0)
m_stream << std::setprecision(static_cast<i32>(spec.precision));
else
m_stream << std::setprecision(static_cast<int>(std::cout.precision()));
if (spec.uppercase)
m_stream << std::uppercase;
else
m_stream << std::nouppercase;
}
void logger_t::exponential()
{
m_stream << std::scientific;
}
void logger_t::process_strings()
{
auto spec_it = m_fmt_specs.begin();
auto str_it = m_string_sections.begin();
for (; spec_it != m_fmt_specs.end(); ++spec_it, ++str_it)
{
m_stream << *str_it;
auto arg_pos = spec_it->arg_id;
if (arg_pos == -1)
arg_pos = static_cast<i64>(m_arg_pos++);
void logger_t::fixed()
{
m_stream << std::fixed;
}
setup_stream(*spec_it);
m_arg_print_funcs[arg_pos](m_stream, *spec_it);
}
m_stream << *str_it;
}
void logger_t::compile(std::string fmt)
{
m_fmt = std::move(fmt);
m_last_fmt_pos = 0;
m_arg_pos = 0;
m_stream.str("");
m_stream.clear();
}
void logger_t::handle_type(std::ostream& stream, const fmt_type_t type)
{
switch (type)
{
case fmt_type_t::DECIMAL:
stream << std::dec;
break;
case fmt_type_t::OCTAL:
stream << std::oct;
break;
case fmt_type_t::HEX:
stream << std::hex;
break;
case fmt_type_t::HEX_FLOAT:
stream << std::hexfloat;
break;
case fmt_type_t::EXPONENT:
stream << std::scientific;
break;
case fmt_type_t::FIXED_POINT:
stream << std::fixed;
break;
default:
break;
}
}
std::optional<std::pair<size_t, size_t>> logger_t::consume_until_fmt()
{
const auto begin = m_fmt.find('{', m_last_fmt_pos);
if (begin == std::string::npos)
return {};
const auto end = m_fmt.find('}', begin);
if (end == std::string::npos)
{
std::cerr << "Invalid format string, missing closing '}' near " << m_fmt.substr(std::min(static_cast<i64>(begin) - 5, 0l)) << std::endl;
std::exit(EXIT_FAILURE);
}
m_stream << std::string_view(m_fmt.data() + static_cast<ptrdiff_t>(m_last_fmt_pos), begin - m_last_fmt_pos);\
m_last_fmt_pos = end + 1;
return std::pair{begin, end};
}
void logger_t::exponential(std::ostream& stream)
{
stream << std::scientific;
}
void logger_t::finish()
{
m_stream << std::string_view(m_fmt.data() + static_cast<ptrdiff_t>(m_last_fmt_pos), m_fmt.size() - m_last_fmt_pos);
m_last_fmt_pos = m_fmt.size();
}
void logger_t::fixed(std::ostream& stream)
{
stream << std::fixed;
}
logger_t& get_global_logger()
{
thread_local logging_thread_context_t context;
return context.logger;
}
void logger_t::compile(std::string fmt)
{
m_fmt = std::move(fmt);
m_last_fmt_pos = 0;
m_arg_pos = 0;
m_stream.str("");
m_stream.clear();
m_string_sections.clear();
m_fmt_specs.clear();
ptrdiff_t last_pos = 0;
while (auto pair = consume_to_next_fmt())
{
const auto [begin, end] = *pair;
m_string_sections.emplace_back(m_fmt.data() + last_pos, begin - last_pos);
if (end - begin > 1)
m_fmt_specs.push_back(m_parser.parse(std::string_view{m_fmt.data() + static_cast<ptrdiff_t>(begin) + 1, end - begin - 1}));
else
m_fmt_specs.emplace_back();
last_pos = static_cast<ptrdiff_t>(end) + 1;
}
m_string_sections.emplace_back(m_fmt.data() + last_pos, m_fmt.size() - last_pos);
m_last_fmt_pos = 0;
}
void print(const std::string& fmt)
{
std::cout << fmt;
}
std::optional<std::pair<size_t, size_t>> logger_t::consume_to_next_fmt()
{
const auto begin = m_fmt.find('{', m_last_fmt_pos);
if (begin == std::string::npos)
return {};
const auto next_begin = m_fmt.find('{', begin + 1);
const auto end = m_fmt.find('}', begin);
if (end == std::string::npos || (next_begin != std::string::npos && next_begin < end))
{
std::stringstream ss;
ss << "Invalid format string, missing closing '}' near " << m_fmt.substr(std::min(static_cast<i64>(begin) - 5, 0l));
throw std::runtime_error(ss.str());
}
m_last_fmt_pos = end + 1;
return std::pair{begin, end};
}
void newline()
{
std::cout << std::endl;
}
logger_t& get_global_logger()
{
thread_local logging_thread_context_t context;
return context.logger;
}
void print(const std::string& fmt)
{
std::cout << fmt;
}
void newline()
{
std::cout << std::endl;
}
}

View File

@ -25,12 +25,16 @@ int main()
// blt::logging::println("{} | {} | {} | {}", blt::type_string<endl_t>());
blt::logging::println("This is a println!");
blt::logging::println("This is a println with args '{}'", 42);
blt::logging::println("This is a println with multiple args '{}' '{}' '{}'", 42, 32.34231233, "Hello World!");
blt::logging::println("This is a println with multiple args '{}' '{:.100}' '{}'", 42, 32.34231233f, "Hello World!");
blt::logging::println("This is a '{1}' fmt string with positionals '{0}'", "I am a string!", "Well so am I except cooler :3");
blt::logging::println("This is a println with a sign {:+}", 4120);
blt::logging::println("This is a println with a sign {:+}", -4120);
blt::logging::println("This is a println with a space {: }", 4120);
blt::logging::println("This is a println with a with {:3}", 4120);
blt::logging::println("This is a println with a space {: }", -4120);
blt::logging::println("This is a println with a minus {:-}", 4120);
blt::logging::println("This is a println with a minus {:-}", -4120);
blt::logging::println("This is a println with a with {:10}", 4120);
blt::logging::println("This is a println with a with leading zeros {:010}", 4120);
blt::logging::println("This is a println with a precision {:.3}", 42.232342349);
blt::logging::println("This is a println with a precision {:.10f}", 42.232342349);
// blt::logging::println("This is println {}\twith a std::endl in the middle of it");
}