Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,18 @@ jobs:
working-directory: build
run: |
ctest -C Debug --verbose

windows:
timeout-minutes: 15
runs-on: windows-latest

steps:
- uses: actions/checkout@v3
- name: Build tests
run: |
cmake -DNODEC_BUILD_TESTS=ON -B ./build
cmake --build ./build --config Debug -j 4
- name: Run tests
working-directory: build
run: |
ctest -C Debug --verbose
16 changes: 2 additions & 14 deletions include/nodec/formatter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,6 @@

namespace nodec {

/*
* The Text Formatter
*
* if you use C++20, consider to use std::format.
* * <https://en.cppreference.com/w/cpp/utility/format>
*
* It is designed for easy use of text format even though under C++20.
*
* Implementation refs:
* * <https://stackoverflow.com/questions/12261915/how-to-throw-stdexceptions-with-variable-messages>
*
*/

class Formatter {
public:
Formatter() noexcept {}
Expand Down Expand Up @@ -57,7 +44,8 @@ class ErrorFormatter {
}

operator std::string() noexcept {
stream_ << "\n" << std::dec
stream_ << "\n"
<< std::dec
<< "[File] " << file_ << "\n"
<< "[Line] " << line_;
return stream_.str();
Expand Down
29 changes: 17 additions & 12 deletions include/nodec/logging/formatters/simple_formatter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <sstream>

#include "../../string_builder.hpp"
#include "../log_record.hpp"

namespace nodec {
Expand All @@ -11,40 +12,44 @@ namespace formatters {

struct SimpleFormatter {
std::string operator()(const LogRecord &record) {
std::ostringstream oss;
std::string text;
StringBuilder builder(text);

// std::ostringstream builder;

switch (record.level) {
case nodec::logging::Level::Unset:
oss << "[UNSET]";
builder << "[UNSET]";
break;
case nodec::logging::Level::Debug:
oss << "[DEBUG]";
builder << "[DEBUG]";
break;
case nodec::logging::Level::Info:
oss << "[INFO] ";
builder << "[INFO] ";
break;
case nodec::logging::Level::Warn:
oss << "[WARN] ";
builder << "[WARN] ";
break;
case nodec::logging::Level::Error:
oss << "[ERROR]";
builder << "[ERROR]";
break;
case nodec::logging::Level::Fatal:
oss << "[FATAL]";
builder << "[FATAL]";
break;
default:
oss << "[???] ";
builder << "[???] ";
break;
}

if (!record.name.empty()) {
oss << " [" << record.name << "]";
builder << " [" << record.name << "]";
}

oss << " - " << record.message << "\n";
oss << "(" << record.file << " line " << record.line << ")\n";
builder << " - " << record.message << "\n";
builder << "(" << record.file << " line " << record.line << ")\n";

return oss.str();
return std::move(text);
// return builder.str();
}
};

Expand Down
14 changes: 9 additions & 5 deletions include/nodec/logging/log_record.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#include <chrono>
#include <cstddef>
#include <string>
#include <string_view>

#include "level.hpp"

Expand All @@ -12,14 +12,18 @@ namespace logging {

struct LogRecord {
public:
LogRecord(std::chrono::system_clock::time_point time, const std::string &name, Level level, const std::string &message, const char *file, std::size_t line)
LogRecord(std::chrono::system_clock::time_point time,
std::string_view name,
Level level,
std::string_view message,
std::string_view file, std::size_t line)
: time(time), name(name), level(level), message(message), file(file), line(line) {}

const std::chrono::system_clock::time_point time;
const std::string &name;
std::string_view name;
const Level level;
const std::string &message;
const char *file;
std::string_view message;
std::string_view file;
const std::size_t line;
};

Expand Down
10 changes: 6 additions & 4 deletions include/nodec/logging/logger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "../macros.hpp"
#include "../optional.hpp"
#include "../signals/signal.hpp"
#include "../string_builder.hpp"
#include "log_record.hpp"

namespace nodec {
Expand Down Expand Up @@ -67,15 +68,15 @@ class Logger {
class LogStream {
public:
LogStream(Logger &logger, Level level, const char *file, std::size_t line)
: logger_(logger), level_(level), file_(file), line_(line) {}
: logger_(logger), level_(level), file_(file), line_(line), builder_(message) {}

~LogStream() {
logger_.log(level_, stream_.str(), file_, line_);
logger_.log(level_, message, file_, line_);
}

template<typename T>
LogStream &operator<<(const T &value) {
stream_ << value;
builder_ << value;
return *this;
}

Expand All @@ -84,7 +85,8 @@ class Logger {
Level level_;
const char *file_;
std::size_t line_;
std::ostringstream stream_;
std::string message;
StringBuilder builder_;
};

public:
Expand Down
160 changes: 160 additions & 0 deletions include/nodec/string_builder.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#ifndef NODEC__STRING_BUILDER_HPP_
#define NODEC__STRING_BUILDER_HPP_

#include <string>

namespace nodec {

template<typename CharT = char, typename Traits = std::char_traits<CharT>>
class BasicStringBuilder {
private:
class BasicStringBuilderStreamBuf : public std::basic_streambuf<CharT, Traits> {
public:
using base_type = std::basic_streambuf<CharT, Traits>;
using string_type = std::basic_string<CharT, Traits>;
using string_view_type = std::basic_string_view<CharT, Traits>;
using size_type = typename string_type::size_type;
using int_type = typename Traits::int_type;
using char_type = CharT;
using traits_type = Traits;
using pos_type = typename Traits::pos_type;
using off_type = typename Traits::off_type;

BasicStringBuilderStreamBuf(string_type &dest): dest_(dest) {}

protected:
/**
* @note This function is called when sputc()
*/
int_type overflow(int_type c = Traits::eof()) override {
if (Traits::eq_int_type(c, Traits::eof())) {
return Traits::not_eof(c);
}
dest_.push_back(Traits::to_char_type(c));
return c;
}

std::streamsize xsputn(const char_type *s, std::streamsize count) override {
if (count <= 0) {
return 0;
}

dest_.append(s, static_cast<size_type>(count));
return count;
}

private:
string_type &dest_;
};

public:
using string_type = std::basic_string<CharT, Traits>;
using string_view_type = std::basic_string_view<CharT, Traits>;
using size_type = typename string_type::size_type;
using stringbuf_type = BasicStringBuilderStreamBuf;

BasicStringBuilder(string_type &dest): base_stream_(&buffer_), buffer_(dest) {}

string_type str() const {
return buffer_.str();
}

BasicStringBuilder &operator<<(const string_view_type str) {
using size_type = typename string_view_type::size_type;

const size_type str_size = str.size();
size_type pad;

if (base_stream_.width() <= 0 || static_cast<size_type>(base_stream_.width()) <= str_size) {
pad = 0;
} else {
pad = static_cast<size_type>(base_stream_.width()) - str_size;
}

// 左詰め以外の場合は左側にパディング
if ((base_stream_.flags() & std::ios_base::adjustfield) != std::ios_base::left) {
for (; 0 < pad; --pad) {
buffer_.sputc(base_stream_.fill());
}
}

// 文字列本体を出力
buffer_.sputn(str.data(), static_cast<std::streamsize>(str_size));

// 右側にパディング(左詰めの場合)
for (; 0 < pad; --pad) {
buffer_.sputc(base_stream_.fill());
}

// width をリセット
base_stream_.width(0);

return *this;
}

/**
* @brief Inserts a null-terminated character string into the stream
*
* This operator performs formatted output of a C-style string with support for
* width, fill character, and alignment manipulators (setw, setfill, left, right, internal).
* The behavior is consistent with std::ostream::operator<<(const char*).
*
* @param str Pointer to a null-terminated character string
* @return Reference to this BasicStringBuilder object for method chaining
*
* @pre str must be a valid pointer to a null-terminated string (str != nullptr)
* @warning Passing nullptr results in undefined behavior
*
* @note The width setting is automatically reset to 0 after the operation,
* following standard stream behavior
*
* @example
* @code
* std::string buffer;
* StringBuilder builder(buffer);
* builder << std::setw(10) << std::setfill('*') << "Hello";
* // buffer contains "*****Hello"
* @endcode
*/
BasicStringBuilder &operator<<(const char *str) {
std::streamsize count = static_cast<std::streamsize>(Traits::length(str));

std::streamsize pad = base_stream_.width() <= 0 || base_stream_.width() <= count ? 0 : base_stream_.width() - count;

// 左詰め以外の場合は左側にパディング
if ((base_stream_.flags() & std::ios_base::adjustfield) != std::ios_base::left) {
for (; 0 < pad; --pad) {
buffer_.sputc(base_stream_.fill());
}
}

// 文字列本体を出力
buffer_.sputn(str, count);

// 右側にパディング(左詰めの場合)
for (; 0 < pad; --pad) {
buffer_.sputc(base_stream_.fill());
}

// width をリセット
base_stream_.width(0);

return *this;
}

template<typename T>
BasicStringBuilder &operator<<(const T &value) {
base_stream_ << value;
return *this;
}

private:
stringbuf_type buffer_;
std::basic_ostream<CharT, Traits> base_stream_;
};

using StringBuilder = BasicStringBuilder<char>;

} // namespace nodec

#endif
13 changes: 10 additions & 3 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,17 @@ add_basic_test("nodec__entitites__storage" entities/storage.cpp)
add_basic_test("nodec__asyncio__event_loop" asyncio/event_loop.cpp)
add_basic_test("nodec__asyncio__event_promise" asyncio/event_promise.cpp)
add_basic_test("nodec__flags" flags/flags.cpp)
add_basic_test("nodec__formatter" formatter/formatter.cpp)

add_basic_test("nodec__string_builder__basic_test" string_builder/basic_test.cpp)
add_basic_test("nodec__string_builder__benchmark_test" string_builder/benchmark_test.cpp)
add_basic_test("nodec__string_builder__oss_compat_test" string_builder/oss_compat_test.cpp)

add_basic_test("nodec__gfx" gfx/gfx.cpp)
add_basic_test("nodec__logging__logging" logging/logging.cpp)
add_basic_test("nodec__logging_bench" logging/bench.cpp)

add_basic_test("nodec__logging__logger_test" logging/logger_test.cpp)
add_basic_test("nodec__logging__bench_test" logging/bench_test.cpp)
add_basic_test("nodec__logging__simple_formatter_test" logging/simple_formatter_test.cpp)

add_basic_test("nodec__math" math/math.cpp)
add_basic_test("nodec__matrix4x4" matrix4x4/matrix4x4.cpp)
add_basic_test("nodec__observers" observers/observers.cpp)
Expand Down
10 changes: 0 additions & 10 deletions tests/formatter/formatter.cpp

This file was deleted.

Loading
Loading