From 46b00e6ad6a4f07f6574e3ec04ba81be617d4136 Mon Sep 17 00:00:00 2001 From: IOE Date: Wed, 2 Jul 2025 00:51:45 +0900 Subject: [PATCH 1/6] test --- include/nodec/formatter.hpp | 624 +++++++++++++++++- include/nodec/optimized_ostringstream.hpp | 318 +++++++++ test_ostringstream.cpp | 10 + tests/CMakeLists.txt | 20 + tests/formatter/complex_format_debug.cpp | 27 + tests/formatter/constructor_debug.cpp | 29 + tests/formatter/debug_test.cpp | 29 + tests/formatter/formatter.cpp | 115 +++- tests/formatter/formatter_benchmark.cpp | 320 +++++++++ .../formatter_detailed_benchmark.cpp | 121 ++++ .../formatter_ostringstream_compat.cpp | 136 ++++ .../formatter/formatter_vector3_benchmark.cpp | 243 +++++++ tests/formatter/hex_debug.cpp | 44 ++ tests/formatter/openmode_examples.cpp | 29 + .../optimized_ostringstream_test.cpp | 206 ++++++ .../formatter/ostringstream_verification.cpp | 15 + tests/formatter/vector3_debug.cpp | 25 + 17 files changed, 2301 insertions(+), 10 deletions(-) create mode 100644 include/nodec/optimized_ostringstream.hpp create mode 100644 test_ostringstream.cpp create mode 100644 tests/formatter/complex_format_debug.cpp create mode 100644 tests/formatter/constructor_debug.cpp create mode 100644 tests/formatter/debug_test.cpp create mode 100644 tests/formatter/formatter_benchmark.cpp create mode 100644 tests/formatter/formatter_detailed_benchmark.cpp create mode 100644 tests/formatter/formatter_ostringstream_compat.cpp create mode 100644 tests/formatter/formatter_vector3_benchmark.cpp create mode 100644 tests/formatter/hex_debug.cpp create mode 100644 tests/formatter/openmode_examples.cpp create mode 100644 tests/formatter/optimized_ostringstream_test.cpp create mode 100644 tests/formatter/ostringstream_verification.cpp create mode 100644 tests/formatter/vector3_debug.cpp diff --git a/include/nodec/formatter.hpp b/include/nodec/formatter.hpp index 1c74590..aff933a 100644 --- a/include/nodec/formatter.hpp +++ b/include/nodec/formatter.hpp @@ -5,9 +5,33 @@ #include #include +#include +#include +#include namespace nodec { +// カスタム操作子の定義 +struct SetWidth { + int width; + explicit SetWidth(int w) : width(w) {} +}; + +struct SetFill { + char fill_char; + explicit SetFill(char c) : fill_char(c) {} +}; + +struct SetPrecision { + int precision; + explicit SetPrecision(int p) : precision(p) {} +}; + +// カスタム操作子のヘルパー関数 +inline SetWidth setw(int width) { return SetWidth(width); } +inline SetFill setfill(char fill_char) { return SetFill(fill_char); } +inline SetPrecision setprecision(int precision) { return SetPrecision(precision); } + /* * The Text Formatter * @@ -15,6 +39,7 @@ namespace nodec { * * * * It is designed for easy use of text format even though under C++20. + * ostringstream compatible formatter with high performance. * * Implementation refs: * * @@ -23,21 +48,608 @@ namespace nodec { class Formatter { public: - Formatter() noexcept {} - ~Formatter() noexcept {} + enum class Alignment { + Left, + Right, + Internal + }; + + enum class FloatFormat { + Default, + Fixed, + Scientific + }; + + Formatter() noexcept = default; + ~Formatter() noexcept = default; + + // 事前リザーブ機能付きコンストラクタ + explicit Formatter(size_t reserve_size) noexcept { + buffer_.reserve(reserve_size); + } + // 基本型の効率的な処理 + Formatter &operator<<(const char* value) noexcept { + if (value) { + append_string_with_format(value); + } + return *this; + } + + Formatter &operator<<(const std::string& value) noexcept { + append_string_with_format(value); + return *this; + } + + Formatter &operator<<(char value) noexcept { + append_char_with_format(value); + return *this; + } + + // 整数型の効率的な処理 + Formatter &operator<<(int value) noexcept { + append_integer_with_format(value); + return *this; + } + + Formatter &operator<<(long value) noexcept { + append_integer_with_format(value); + return *this; + } + + Formatter &operator<<(long long value) noexcept { + append_integer_with_format(value); + return *this; + } + + Formatter &operator<<(unsigned int value) noexcept { + append_unsigned_integer_with_format(value); + return *this; + } + + Formatter &operator<<(unsigned long value) noexcept { + append_unsigned_integer_with_format(value); + return *this; + } + + Formatter &operator<<(unsigned long long value) noexcept { + append_unsigned_integer_with_format(value); + return *this; + } + + // 浮動小数点型の処理 + Formatter &operator<<(float value) noexcept { + append_float_with_format(static_cast(value)); + return *this; + } + + Formatter &operator<<(double value) noexcept { + append_float_with_format(value); + return *this; + } + + // ostream操作子のサポート(hex, dec等) + Formatter &operator<<(std::ios_base& (*manipulator)(std::ios_base&)) noexcept { + if (manipulator == std::hex) { + base_mode_ = 16; + } else if (manipulator == std::dec) { + base_mode_ = 10; + } else if (manipulator == std::oct) { + base_mode_ = 8; + } else if (manipulator == std::fixed) { + float_mode_ = FloatMode::Fixed; + } else if (manipulator == std::scientific) { + float_mode_ = FloatMode::Scientific; + } else if (manipulator == std::uppercase) { + uppercase_ = true; + } else if (manipulator == std::nouppercase) { + uppercase_ = false; + } else if (manipulator == std::showbase) { + showbase_ = true; + } else if (manipulator == std::noshowbase) { + showbase_ = false; + } else if (manipulator == std::boolalpha) { + boolalpha_ = true; + } else if (manipulator == std::noboolalpha) { + boolalpha_ = false; + } else if (manipulator == std::left) { + align_mode_ = AlignMode::Left; + } else if (manipulator == std::right) { + align_mode_ = AlignMode::Right; + } else if (manipulator == std::internal) { + align_mode_ = AlignMode::Internal; + } + return *this; + } + + // カスタム操作子のサポート + Formatter &operator<<(const SetWidth& sw) noexcept { + width_ = sw.width; + return *this; + } + + Formatter &operator<<(const SetFill& sf) noexcept { + fill_char_ = sf.fill_char; + return *this; + } + + Formatter &operator<<(const SetPrecision& sp) noexcept { + precision_ = sp.precision; + return *this; + } + + // bool型の特別処理 + Formatter &operator<<(bool value) noexcept { + if (boolalpha_) { + buffer_ += (value ? "true" : "false"); + } else { + buffer_ += (value ? '1' : '0'); + } + return *this; + } + + // その他の型(フォールバック)- ただし、専用マニピュレータは除外 template - Formatter &operator<<(const T &value) noexcept { - stream_ << value; + typename std::enable_if< + !std::is_same::value && + !std::is_same::value && + !std::is_same::value, + Formatter& + >::type operator<<(const T &value) noexcept { + // 複雑な型はostringstreamにフォールバック + std::ostringstream oss; + oss << value; + buffer_ += oss.str(); return *this; } operator std::string() const noexcept { - return stream_.str(); + return buffer_; + } + + // 追加のユーティリティメソッド + void reserve(size_t size) noexcept { + buffer_.reserve(size); + } + + void clear() noexcept { + buffer_.clear(); + // フォーマット状態をリセット + base_mode_ = 10; + float_mode_ = FloatMode::Default; + precision_ = 6; + width_ = 0; + fill_char_ = ' '; + uppercase_ = false; + showbase_ = false; + boolalpha_ = false; + align_mode_ = AlignMode::Right; + } + + size_t size() const noexcept { + return buffer_.size(); + } + + const std::string& str() const noexcept { + return buffer_; + } + + // マニピュレータ用のpublicメソッド + void apply_setw(int w) noexcept { + width_ = w; + } + + void apply_setfill(char c) noexcept { + fill_char_ = c; + } + + void apply_setprecision(int p) noexcept { + precision_ = p; } private: - std::ostringstream stream_; + enum class FloatMode { + Default, + Fixed, + Scientific + }; + + enum class AlignMode { + Left, + Right, + Internal + }; + + std::string buffer_; + + // フォーマット状態 + int base_mode_ = 10; // 10進数がデフォルト + FloatMode float_mode_ = FloatMode::Default; + int precision_ = 6; // デフォルト精度 + int width_ = 0; // フィールド幅 + char fill_char_ = ' '; // 埋め文字 + bool uppercase_ = false; // 大文字表示 + bool showbase_ = false; // プレフィックス表示 + bool boolalpha_ = false; // bool値をtrue/falseで表示 + AlignMode align_mode_ = AlignMode::Right; // 右揃えがデフォルト + + // マニピュレータ適用 + template + void apply_manipulator(const T& manipulator) noexcept { + // SFINAE を使ってsetw, setfill, setprecisionを識別 + // 実際の実装では型特性を使用するが、簡略化のため条件分岐で対応 + // この部分は実装が複雑になるため、後で詳細化 + } + + // 整数を効率的に文字列に変換 + template + void append_integer(IntType value) noexcept { + if (base_mode_ == 16) { + if (value < 0) { + buffer_ += '-'; + append_hex_with_format(static_cast(-value)); + } else { + append_hex_with_format(static_cast(value)); + } + } else if (base_mode_ == 8) { + if (value < 0) { + buffer_ += '-'; + append_oct_with_format(static_cast(-value)); + } else { + append_oct_with_format(static_cast(value)); + } + } else { + append_decimal_with_format(value); + } + } + + template + void append_unsigned_integer(UIntType value) noexcept { + if (base_mode_ == 16) { + append_hex_with_format(value); + } else if (base_mode_ == 8) { + append_oct_with_format(value); + } else { + append_decimal_with_format(value); + } + } + + // 10進数フォーマット + template + void append_decimal_with_format(IntType value) noexcept { + std::string result; + + if (value == 0) { + result = "0"; + } else { + bool negative = value < 0; + if (negative) { + value = -value; + } + + char temp[32]; + int pos = 0; + while (value > 0) { + temp[pos++] = '0' + (value % 10); + value /= 10; + } + + if (negative) { + result += '-'; + } + + for (int i = pos - 1; i >= 0; --i) { + result += temp[i]; + } + } + + apply_field_formatting(result); + } + + // 16進数フォーマット + void append_hex_with_format(unsigned long long value) noexcept { + std::string result; + + if (showbase_ && value != 0) { + result += "0x"; + } + + if (value == 0) { + result += '0'; + } else { + char temp[32]; + int pos = 0; + const char* hex_chars = uppercase_ ? "0123456789ABCDEF" : "0123456789abcdef"; + + while (value > 0) { + temp[pos++] = hex_chars[value % 16]; + value /= 16; + } + + for (int i = pos - 1; i >= 0; --i) { + result += temp[i]; + } + } + + apply_field_formatting(result); + } + + // 8進数フォーマット + void append_oct_with_format(unsigned long long value) noexcept { + std::string result; + + if (showbase_ && value != 0) { + result += '0'; + } + + if (value == 0) { + result += '0'; + } else { + char temp[32]; + int pos = 0; + + while (value > 0) { + temp[pos++] = '0' + (value % 8); + value /= 8; + } + + for (int i = pos - 1; i >= 0; --i) { + result += temp[i]; + } + } + + apply_field_formatting(result); + } + + // フィールドフォーマット適用 + void apply_field_formatting(const std::string& content) noexcept { + if (width_ <= static_cast(content.length())) { + // 幅指定なし、または内容が幅以上 + buffer_ += content; + width_ = 0; // 一度使用したらリセット + return; + } + + int padding = width_ - static_cast(content.length()); + + switch (align_mode_) { + case AlignMode::Left: + buffer_ += content; + buffer_.append(padding, fill_char_); + break; + + case AlignMode::Right: + buffer_.append(padding, fill_char_); + buffer_ += content; + break; + + case AlignMode::Internal: + // 符号と数値の間に埋め文字を挿入 + if (!content.empty() && (content[0] == '-' || content[0] == '+')) { + buffer_ += content[0]; + buffer_.append(padding, fill_char_); + buffer_ += content.substr(1); + } else if (content.length() >= 2 && content[0] == '0' && (content[1] == 'x' || content[1] == 'X')) { + buffer_ += content.substr(0, 2); + buffer_.append(padding, fill_char_); + buffer_ += content.substr(2); + } else { + buffer_.append(padding, fill_char_); + buffer_ += content; + } + break; + } + + width_ = 0; // 一度使用したらリセット + } + + // フォーマット付きの文字列・数値処理メソッド + void append_string_with_format(const std::string& str) noexcept { + if (width_ > 0 && static_cast(str.length()) < width_) { + apply_field_formatting(str); + } else { + buffer_ += str; + } + width_ = 0; // 使用後リセット + } + + void append_char_with_format(char c) noexcept { + if (width_ > 0 && width_ > 1) { + std::string str(1, c); + apply_field_formatting(str); + } else { + buffer_ += c; + } + width_ = 0; // 使用後リセット + } + + void append_integer_with_format(long long value) noexcept { + std::string num_str = format_integer(value); + if (width_ > 0 && static_cast(num_str.length()) < width_) { + apply_field_formatting(num_str); + } else { + buffer_ += num_str; + } + width_ = 0; // 使用後リセット + } + + void append_unsigned_integer_with_format(unsigned long long value) noexcept { + std::string num_str = format_unsigned_integer(value); + if (width_ > 0 && static_cast(num_str.length()) < width_) { + apply_field_formatting(num_str); + } else { + buffer_ += num_str; + } + width_ = 0; // 使用後リセット + } + + void append_float_with_format(double value) noexcept { + std::ostringstream oss; + oss.precision(precision_); + + // フロートモード設定 + switch (float_mode_) { + case FloatMode::Fixed: + oss << std::fixed; + break; + case FloatMode::Scientific: + oss << std::scientific; + break; + case FloatMode::Default: + // デフォルトフォーマット + break; + } + + // 大文字設定 + if (uppercase_) { + oss << std::uppercase; + } + + oss << value; + std::string result = oss.str(); + + if (width_ > 0 && static_cast(result.length()) < width_) { + apply_field_formatting(result); + } else { + buffer_ += result; + } + width_ = 0; // 使用後リセット + } + + // 数値フォーマット関数 + std::string format_integer(long long value) noexcept { + if (base_mode_ == 16) { + return format_hex_signed(value); + } else if (base_mode_ == 8) { + return format_oct_signed(value); + } else { + return format_decimal(value); + } + } + + std::string format_unsigned_integer(unsigned long long value) noexcept { + if (base_mode_ == 16) { + return format_hex(value); + } else if (base_mode_ == 8) { + return format_oct(value); + } else { + return format_unsigned_decimal(value); + } + } + + std::string format_hex_signed(long long value) noexcept { + if (value < 0) { + return "-" + format_hex(static_cast(-value)); + } else { + return format_hex(static_cast(value)); + } + } + + std::string format_oct_signed(long long value) noexcept { + if (value < 0) { + return "-" + format_oct(static_cast(-value)); + } else { + return format_oct(static_cast(value)); + } + } + + std::string format_decimal(long long value) noexcept { + if (value == 0) return "0"; + + std::string result; + bool negative = value < 0; + if (negative) value = -value; + + while (value > 0) { + result = char('0' + value % 10) + result; + value /= 10; + } + + if (negative) result = "-" + result; + return result; + } + + std::string format_unsigned_decimal(unsigned long long value) noexcept { + if (value == 0) return "0"; + + std::string result; + while (value > 0) { + result = char('0' + value % 10) + result; + value /= 10; + } + return result; + } + + std::string format_hex(unsigned long long value) noexcept { + if (value == 0) { + std::string result = "0"; + if (showbase_) result = "0x" + result; + return result; + } + + std::string result; + const char* hex_chars = uppercase_ ? "0123456789ABCDEF" : "0123456789abcdef"; + + while (value > 0) { + result = hex_chars[value % 16] + result; + value /= 16; + } + + if (showbase_) { + result = (uppercase_ ? "0X" : "0x") + result; + } + + return result; + } + + std::string format_oct(unsigned long long value) noexcept { + if (value == 0) { + std::string result = "0"; + if (showbase_) result = "0" + result; + return result; + } + + std::string result; + while (value > 0) { + result = char('0' + value % 8) + result; + value /= 8; + } + + if (showbase_) { + result = "0" + result; + } + + return result; + } + + void append_float(double value) noexcept { + std::ostringstream oss; + + // 精度設定 + oss.precision(precision_); + + // フロートモード設定 + switch (float_mode_) { + case FloatMode::Fixed: + oss << std::fixed; + break; + case FloatMode::Scientific: + oss << std::scientific; + break; + case FloatMode::Default: + // デフォルトフォーマット + break; + } + + // 大文字設定 + if (uppercase_) { + oss << std::uppercase; + } + + oss << value; + std::string result = oss.str(); + apply_field_formatting(result); + } NODEC_DISABLE_COPY(Formatter) }; diff --git a/include/nodec/optimized_ostringstream.hpp b/include/nodec/optimized_ostringstream.hpp new file mode 100644 index 0000000..c631a93 --- /dev/null +++ b/include/nodec/optimized_ostringstream.hpp @@ -0,0 +1,318 @@ +#ifndef NODEC__OPTIMIZED_OSTRINGSTREAM_HPP_ +#define NODEC__OPTIMIZED_OSTRINGSTREAM_HPP_ + +#include +#include +#include +#include + +namespace nodec { + +/** + * 高度に最適化されたstreambuf - 確実な事前リザーブ機能付き + * std::stringbufとは異なり、真の高性能を追求したシンプルな実装 + */ +template> +class basic_reserved_stringbuf : public std::basic_streambuf { +public: + using base_type = std::basic_streambuf; + using string_type = std::basic_string; + 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; + + // デフォルトコンストラクタ + basic_reserved_stringbuf() + : mode_(std::ios_base::out), buffer_(), reserved_capacity_(0) { + init_put_area(); + } + + // 事前リザーブ付きコンストラクタ + explicit basic_reserved_stringbuf(size_type reserve_size) + : mode_(std::ios_base::out), buffer_(), reserved_capacity_(reserve_size) { + buffer_.reserve(reserve_size); + init_put_area(); + } + + // 初期文字列 + 事前リザーブ付きコンストラクタ + basic_reserved_stringbuf(const string_type &initial_str, size_type reserve_size) + : mode_(std::ios_base::out), buffer_(initial_str), reserved_capacity_(reserve_size) { + ensure_capacity(std::max(reserve_size, initial_str.size())); + init_put_area(); + } + + // ムーブコンストラクタ + basic_reserved_stringbuf(basic_reserved_stringbuf &&other) noexcept + : base_type(std::move(other)), mode_(other.mode_), buffer_(std::move(other.buffer_)), + reserved_capacity_(other.reserved_capacity_) { + init_put_area(); + } + + // ムーブ代入演算子 + basic_reserved_stringbuf &operator=(basic_reserved_stringbuf &&other) noexcept { + if (this != &other) { + base_type::operator=(std::move(other)); + mode_ = other.mode_; + buffer_ = std::move(other.buffer_); + reserved_capacity_ = other.reserved_capacity_; + init_put_area(); + } + return *this; + } + + // デストラクタ + ~basic_reserved_stringbuf() = default; + + // 文字列取得 + string_type str() const { + return buffer_; + } + + // 文字列設定 + void str(const string_type &s) { + buffer_ = s; + init_put_area(); + } + + void str(string_type &&s) { + buffer_ = std::move(s); + init_put_area(); + } + + // 事前リザーブ機能 + void reserve(size_type new_capacity) { + reserved_capacity_ = new_capacity; + ensure_capacity(new_capacity); + } + + // 容量・サイズ情報(リザーブされた容量も考慮) + size_type capacity() const { + return std::max(buffer_.capacity(), reserved_capacity_); + } + + size_type size() const { + return buffer_.size(); + } + +protected: + // オーバーフロー処理(高度に最適化) + int_type overflow(int_type c = Traits::eof()) override { + if (!(mode_ & std::ios_base::out)) { + return Traits::eof(); + } + + if (!Traits::eq_int_type(c, Traits::eof())) { + // 文字を直接buffer_に追加 + try { + buffer_.push_back(Traits::to_char_type(c)); + return c; + } catch (...) { + return Traits::eof(); + } + } + return Traits::not_eof(c); + } + + // バルク書き込み最適化 + std::streamsize xsputn(const char_type *s, std::streamsize count) override { + if (!(mode_ & std::ios_base::out) || count <= 0) { + return 0; + } + + try { + buffer_.append(s, static_cast(count)); + return count; + } catch (...) { + return 0; + } + } + + // 同期処理 + int sync() override { + // 既にbuffer_に直接書き込んでいるので何もしない + return 0; + } + + // シーク処理 + pos_type seekoff(off_type off, std::ios_base::seekdir way, + std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override { + if (!(mode_ & which)) { + return pos_type(off_type(-1)); + } + + off_type newoff; + switch (way) { + case std::ios_base::beg: + newoff = off; + break; + case std::ios_base::cur: + newoff = static_cast(buffer_.size()) + off; + break; + case std::ios_base::end: + newoff = static_cast(buffer_.size()) + off; + break; + default: + return pos_type(off_type(-1)); + } + + if (newoff < 0 || static_cast(newoff) > buffer_.size()) { + return pos_type(off_type(-1)); + } + + buffer_.resize(static_cast(newoff)); + return pos_type(newoff); + } + + pos_type seekpos(pos_type pos, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override { + return seekoff(off_type(pos), std::ios_base::beg, which); + } + +private: + std::ios_base::openmode mode_; + string_type buffer_; + size_type reserved_capacity_; + + // 容量確保の内部メソッド(SSOを考慮) + void ensure_capacity(size_type capacity) { + if (capacity > 0) { + // 実際の容量を確保(SSOを回避) + if (capacity > 15) { // SSOの閾値を超える場合 + string_type temp(capacity, CharT{}); + temp.clear(); + if (temp.capacity() >= capacity) { + // 既存のコンテンツをコピー + temp = buffer_; + buffer_ = std::move(temp); + } + } + } + } + + // Put areaの初期化(ダミー - 実際の書き込みはoverflow/xsputnで処理) + void init_put_area() { + // streambufの要求を満たすためのダミー設定 + // 実際の書き込みはoverflowとxsputnで直接buffer_に行う + char_type dummy{}; + base_type::setp(&dummy, &dummy); + } + + // コピーは禁止、ムーブのみ許可 + basic_reserved_stringbuf(const basic_reserved_stringbuf &) = delete; + basic_reserved_stringbuf &operator=(const basic_reserved_stringbuf &) = delete; +}; + +using reserved_stringbuf = basic_reserved_stringbuf; +using reserved_wstringbuf = basic_reserved_stringbuf; + +/** + * 事前リザーブ機能付きostringstream + * 完全なstd::ostringstreamとの互換性を保ちつつ、高性能を実現 + */ +template> +class basic_optimized_ostringstream : public std::basic_ostream { +public: + using base_type = std::basic_ostream; + using string_type = std::basic_string; + using size_type = typename string_type::size_type; + using stringbuf_type = basic_reserved_stringbuf; + + // デフォルトコンストラクタ + basic_optimized_ostringstream() + : base_type(&buffer_), buffer_() {} + + // 事前リザーブ付きコンストラクタ(曖昧さ回避のため型を明確化) + explicit basic_optimized_ostringstream(size_type reserve_size) + : base_type(&buffer_), buffer_(reserve_size) {} + + // 初期文字列付きコンストラクタ + explicit basic_optimized_ostringstream(const string_type &initial_str) + : base_type(&buffer_), buffer_(initial_str, 0) {} + + // 初期文字列 + 事前リザーブ付きコンストラクタ + basic_optimized_ostringstream(const string_type &initial_str, size_type reserve_size) + : base_type(&buffer_), buffer_(initial_str, reserve_size) {} + + // ムーブコンストラクタ + basic_optimized_ostringstream(basic_optimized_ostringstream &&other) noexcept + : base_type(std::move(other)), buffer_(std::move(other.buffer_)) { + base_type::set_rdbuf(&buffer_); + } + + // ムーブ代入演算子 + basic_optimized_ostringstream &operator=(basic_optimized_ostringstream &&other) noexcept { + if (this != &other) { + base_type::operator=(std::move(other)); + buffer_ = std::move(other.buffer_); + base_type::set_rdbuf(&buffer_); + } + return *this; + } + + // デストラクタ + ~basic_optimized_ostringstream() = default; + + // rdbufアクセス + stringbuf_type *rdbuf() const { + return const_cast(&buffer_); + } + + // 文字列取得 + string_type str() const { + return buffer_.str(); + } + + // 文字列設定 + void str(const string_type &s) { + buffer_.str(s); + } + + void str(string_type &&s) { + buffer_.str(std::move(s)); + } + + // 事前リザーブ機能 + void reserve(size_type new_capacity) { + buffer_.reserve(new_capacity); + } + + // 容量・サイズ情報 + size_type capacity() const { + return buffer_.capacity(); + } + + size_type size() const { + return buffer_.size(); + } + + // clear機能(内容をクリアして再利用) + void clear_content() { + buffer_.str(string_type()); + base_type::clear(); // ストリームの状態もクリア + } + + // 文字列変換演算子(利便性のため) + operator string_type() const { + return str(); + } + +private: + stringbuf_type buffer_; + + // コピーは禁止、ムーブのみ許可 + basic_optimized_ostringstream(const basic_optimized_ostringstream &) = delete; + basic_optimized_ostringstream &operator=(const basic_optimized_ostringstream &) = delete; +}; + +// 型エイリアス +using optimized_ostringstream = basic_optimized_ostringstream; +using optimized_wostringstream = basic_optimized_ostringstream; + +// 利便性のための名前空間エイリアス(既存のFormatterとの互換性) +using FastFormatter = optimized_ostringstream; + +} // namespace nodec + +#endif // NODEC__OPTIMIZED_OSTRINGSTREAM_HPP_ diff --git a/test_ostringstream.cpp b/test_ostringstream.cpp new file mode 100644 index 0000000..8017606 --- /dev/null +++ b/test_ostringstream.cpp @@ -0,0 +1,10 @@ +#include +#include +#include + +int main() { + std::ostringstream oss; + oss << std::hex << std::uppercase << std::showbase << std::setfill('0') << std::setw(8) << 255; + std::cout << "ostringstream result: " << oss.str() << std::endl; + return 0; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 46ae4f4..6bc4585 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,6 +16,14 @@ 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__formatter_benchmark" formatter/formatter_benchmark.cpp) +add_basic_test("nodec__formatter_detailed_benchmark" formatter/formatter_detailed_benchmark.cpp) +add_basic_test("nodec__formatter_vector3_benchmark" formatter/formatter_vector3_benchmark.cpp) +add_basic_test("nodec__formatter_ostringstream_compat" formatter/formatter_ostringstream_compat.cpp) +add_basic_test("nodec__ostringstream_verification" formatter/ostringstream_verification.cpp) +add_basic_test("nodec__hex_debug" formatter/hex_debug.cpp) +add_basic_test("nodec__complex_format_debug" formatter/complex_format_debug.cpp) +add_basic_test("nodec__optimized_ostringstream_test" formatter/optimized_ostringstream_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) @@ -34,6 +42,18 @@ add_basic_test("nodec__unicode" unicode/unicode.cpp) add_basic_test("nodec__utility" utility/utility.cpp) add_basic_test("nodec__vector" vector/vector.cpp) +# Debug test for optimized ostringstream +add_basic_test("nodec__debug_test" formatter/debug_test.cpp) + +# Constructor debug test +add_basic_test("nodec__constructor_debug" formatter/constructor_debug.cpp) + +# OpenMode examples test +add_basic_test("nodec__openmode_examples" formatter/openmode_examples.cpp) + +# Vector3 debug test +add_basic_test("nodec__vector3_debug" formatter/vector3_debug.cpp) + target_compile_definitions( nodec__unicode PRIVATE TEST_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/unicode/data" diff --git a/tests/formatter/complex_format_debug.cpp b/tests/formatter/complex_format_debug.cpp new file mode 100644 index 0000000..a1e350f --- /dev/null +++ b/tests/formatter/complex_format_debug.cpp @@ -0,0 +1,27 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +#include +#include +#include +#include + +TEST_CASE("Complex format chain debugging") { + // 複雑なフォーマットチェーンのデバッグ + std::string result = nodec::Formatter() + << "Value: " << std::hex << std::uppercase << std::showbase + << nodec::setfill('0') << nodec::setw(8) << 255 + << ", Float: " << std::dec << std::fixed << nodec::setprecision(2) << 3.14159 + << ", Bool: " << std::boolalpha << true; + std::cout << "Formatter result: '" << result << "'" << std::endl; + + // ostringstream での同じチェーン + std::ostringstream oss; + oss << "Value: " << std::hex << std::uppercase << std::showbase + << std::setfill('0') << std::setw(8) << 255 + << ", Float: " << std::dec << std::fixed << std::setprecision(2) << 3.14159 + << ", Bool: " << std::boolalpha << true; + std::string expected = oss.str(); + std::cout << "ostringstream result: '" << expected << "'" << std::endl; + + CHECK(result == expected); +} diff --git a/tests/formatter/constructor_debug.cpp b/tests/formatter/constructor_debug.cpp new file mode 100644 index 0000000..f077bd9 --- /dev/null +++ b/tests/formatter/constructor_debug.cpp @@ -0,0 +1,29 @@ +#include +#include + +int main() { + std::cout << "=== Constructor Debug Test ===" << std::endl; + + // Case 1: explicit size_type コンストラクタが呼ばれるべき + std::cout << "Case 1: nodec::optimized_ostringstream oss(1024);" << std::endl; + nodec::optimized_ostringstream oss1(1024); + std::cout << "Capacity: " << oss1.capacity() << std::endl; + + // Case 2: size_typeを明示的にキャスト + std::cout << "\nCase 2: nodec::optimized_ostringstream oss(static_cast(1024));" << std::endl; + nodec::optimized_ostringstream oss2(static_cast(1024)); + std::cout << "Capacity: " << oss2.capacity() << std::endl; + + // Case 3: openmodeコンストラクタを明示的に呼ぶ + std::cout << "\nCase 3: nodec::optimized_ostringstream oss(std::ios_base::out);" << std::endl; + nodec::optimized_ostringstream oss3(std::ios_base::out); + std::cout << "Capacity: " << oss3.capacity() << std::endl; + + // Case 4: どの型が1024として解釈されているかチェック + std::cout << "\n=== Type Analysis ===" << std::endl; + std::cout << "1024 as int: " << static_cast(1024) << std::endl; + std::cout << "1024 as size_t: " << static_cast(1024) << std::endl; + std::cout << "1024 as openmode: " << static_cast(1024) << std::endl; + + return 0; +} diff --git a/tests/formatter/debug_test.cpp b/tests/formatter/debug_test.cpp new file mode 100644 index 0000000..63fac34 --- /dev/null +++ b/tests/formatter/debug_test.cpp @@ -0,0 +1,29 @@ +#include +#include + +int main() { + // デバッグ用テスト + std::cout << "Creating ostringstream with reserve 1024..." << std::endl; + nodec::optimized_ostringstream oss(1024); + + std::cout << "Initial capacity: " << oss.capacity() << std::endl; + std::cout << "Initial size: " << oss.size() << std::endl; + + // 手動でリザーブを試行 + std::cout << "Calling reserve(2048)..." << std::endl; + oss.reserve(2048); + std::cout << "After manual reserve capacity: " << oss.capacity() << std::endl; + + oss << "Test"; + + std::cout << "After write capacity: " << oss.capacity() << std::endl; + std::cout << "After write size: " << oss.size() << std::endl; + std::cout << "Content: " << oss.str() << std::endl; + + // 標準のstd::stringでの比較 + std::string test_string; + test_string.reserve(1024); + std::cout << "Standard string reserve(1024) capacity: " << test_string.capacity() << std::endl; + + return 0; +} diff --git a/tests/formatter/formatter.cpp b/tests/formatter/formatter.cpp index b120a6e..21adf14 100644 --- a/tests/formatter/formatter.cpp +++ b/tests/formatter/formatter.cpp @@ -2,9 +2,116 @@ #include #include +#include -TEST_CASE("Testing Formatter") { - std::string str = nodec::Formatter() << "A" - << "B"; - CHECK(str == "AB"); +TEST_CASE("Testing Formatter - Basic Types") { + // 文字列連結 + std::string result1 = nodec::Formatter() << "Hello" << " " << "World"; + CHECK(result1 == "Hello World"); + + // 整数 + std::string result2 = nodec::Formatter() << "Number: " << 42; + CHECK(result2 == "Number: 42"); + + // 浮動小数点 + std::string result3 = nodec::Formatter() << "Float: " << 3.14; + CHECK(result3 == "Float: 3.14"); + + // 文字 + std::string result4 = nodec::Formatter() << 'A' << 'B' << 'C'; + CHECK(result4 == "ABC"); +} + +TEST_CASE("Testing Formatter - Numeric Types") { + // 様々な整数型 + std::string result1 = nodec::Formatter() << static_cast(123); + CHECK(result1 == "123"); + + std::string result2 = nodec::Formatter() << static_cast(456L); + CHECK(result2 == "456"); + + std::string result3 = nodec::Formatter() << static_cast(789LL); + CHECK(result3 == "789"); + + // 負の数 + std::string result4 = nodec::Formatter() << -42; + CHECK(result4 == "-42"); + + // ゼロ + std::string result5 = nodec::Formatter() << 0; + CHECK(result5 == "0"); +} + +TEST_CASE("Testing Formatter - Hex Mode") { + // 16進数モード + std::string result1 = nodec::Formatter() << "Hex: " << std::hex << 255; + CHECK(result1 == "Hex: ff"); + + // 10進数に戻す + std::string result2 = nodec::Formatter() << std::hex << 16 << ", " << std::dec << 16; + CHECK(result2 == "10, 16"); + + // 複数の16進数 + std::string result3 = nodec::Formatter() << std::hex << 10 << " " << 15 << " " << 255; + CHECK(result3 == "a f ff"); +} + +TEST_CASE("Testing Formatter - Reserve Functionality") { + // 事前リザーブ + nodec::Formatter formatter(100); + formatter << "This is a test string that might be long"; + std::string result = formatter.str(); + CHECK(result == "This is a test string that might be long"); + + // クリア機能 + formatter.clear(); + formatter << "New content"; + CHECK(formatter.str() == "New content"); +} + +TEST_CASE("Testing Formatter - Utility Methods") { + nodec::Formatter formatter; + formatter << "Hello"; + + // サイズチェック + CHECK(formatter.size() == 5); + + // 追加リザーブ + formatter.reserve(100); + formatter << " World"; + CHECK(formatter.str() == "Hello World"); + + // クリア + formatter.clear(); + CHECK(formatter.size() == 0); + CHECK(formatter.str() == ""); +} + +TEST_CASE("Testing Formatter - Vector3 Fallback") { + // Vector3の文字列化テスト(ostream演算子フォールバック) + nodec::Vector3f vec3f(1.5f, 2.7f, 3.9f); + std::string result1 = nodec::Formatter() << "Vector3f: " << vec3f; + CHECK(result1 == "Vector3f: ( 1.5, 2.7, 3.9 )"); + + // Vector3iの文字列化テスト + nodec::Vector3i vec3i(10, 20, 30); + std::string result2 = nodec::Formatter() << "Position: " << vec3i << ", End"; + CHECK(result2 == "Position: ( 10, 20, 30 ), End"); + + // Vector3dの文字列化テスト + nodec::Vector3d vec3d(1.23456, 7.89012, 3.45678); + std::string result3 = nodec::Formatter() << vec3d; + CHECK(result3 == "( 1.23456, 7.89012, 3.45678 )"); + + // 複数のVector3を連結 + nodec::Vector3f pos(1.0f, 2.0f, 3.0f); + nodec::Vector3f dir(0.0f, 1.0f, 0.0f); + std::string result4 = nodec::Formatter() << "Pos: " << pos << ", Dir: " << dir; + CHECK(result4 == "Pos: ( 1, 2, 3 ), Dir: ( 0, 1, 0 )"); + + // ostringstream との比較確認 + std::ostringstream oss; + oss << "Vector3f: " << vec3f; + std::string oss_result = oss.str(); + CHECK(result1 == oss_result); // 同じ結果であることを確認 } \ No newline at end of file diff --git a/tests/formatter/formatter_benchmark.cpp b/tests/formatter/formatter_benchmark.cpp new file mode 100644 index 0000000..c32a1d0 --- /dev/null +++ b/tests/formatter/formatter_benchmark.cpp @@ -0,0 +1,320 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include + +#include +#include +#include +#include +#include + +#include +#include + +// ベンチマーク測定用のヘルパークラス +class BenchmarkTimer { +public: + BenchmarkTimer(const std::string& name) : name_(name) { + start_time_ = std::chrono::high_resolution_clock::now(); + } + + ~BenchmarkTimer() { + auto end_time = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end_time - start_time_); + std::cout << name_ << ": " << duration.count() << " microseconds" << std::endl; + } + +private: + std::string name_; + std::chrono::high_resolution_clock::time_point start_time_; +}; + +TEST_CASE("Benchmark - Simple Concatenation") { + const int iterations = 100000; + + std::cout << "\n--- Simple Concatenation Test ---" << std::endl; + + // nodec::Formatterのベンチマーク + { + BenchmarkTimer timer("nodec::Formatter - Simple concatenation"); + for (int i = 0; i < iterations; ++i) { + std::string result = nodec::Formatter() << "Hello" << " " << "World" << i; + // 結果を使用して最適化を防ぐ + volatile auto len = result.length(); + (void)len; + } + } + + // std::ostringstreamのベンチマーク(比較用) + { + BenchmarkTimer timer("std::ostringstream - Simple concatenation"); + for (int i = 0; i < iterations; ++i) { + std::ostringstream oss; + oss << "Hello" << " " << "World" << i; + std::string result = oss.str(); + // 結果を使用して最適化を防ぐ + volatile auto len = result.length(); + (void)len; + } + } + + // nodec::optimized_ostringstream (FastFormatter)のベンチマーク + { + BenchmarkTimer timer("FastFormatter - Simple concatenation"); + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream oss; + oss << "Hello" << " " << "World" << i; + std::string result = oss.str(); + // 結果を使用して最適化を防ぐ + volatile auto len = result.length(); + (void)len; + } + } + + // std::string + 演算子のベンチマーク(比較用) + { + BenchmarkTimer timer("std::string + operator - Simple concatenation"); + for (int i = 0; i < iterations; ++i) { + std::string result = std::string("Hello") + " " + "World" + std::to_string(i); + // 結果を使用して最適化を防ぐ + volatile auto len = result.length(); + (void)len; + } + } + + // テストが正常に実行されたことを確認 + CHECK(true); +} + +TEST_CASE("Benchmark - Numeric Formatting") { + const int iterations = 100000; + + std::cout << "\n--- Numeric Formatting Test ---" << std::endl; + + // nodec::Formatterのベンチマーク + { + BenchmarkTimer timer("nodec::Formatter - Numeric formatting"); + for (int i = 0; i < iterations; ++i) { + std::string result = nodec::Formatter() << "Number: " << i + << ", Float: " << (i * 3.14) + << ", Hex: " << std::hex << i; + volatile auto len = result.length(); + (void)len; + } + } + + // nodec::optimized_ostringstream (FastFormatter)のベンチマーク + { + BenchmarkTimer timer("FastFormatter - Numeric formatting"); + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream oss; + oss << "Number: " << i << ", Float: " << (i * 3.14) << ", Hex: " << std::hex << i; + std::string result = oss.str(); + volatile auto len = result.length(); + (void)len; + } + } + + // std::ostringstreamのベンチマーク(比較用) + { + BenchmarkTimer timer("std::ostringstream - Numeric formatting"); + for (int i = 0; i < iterations; ++i) { + std::ostringstream oss; + oss << "Number: " << i << ", Float: " << (i * 3.14) << ", Hex: " << std::hex << i; + std::string result = oss.str(); + volatile auto len = result.length(); + (void)len; + } + } + + // テストが正常に実行されたことを確認 + CHECK(true); +} + +TEST_CASE("Benchmark - Long Chain") { + const int iterations = 10000; + + std::cout << "\n--- Long Chain Test ---" << std::endl; + + // nodec::Formatterのベンチマーク - 長いチェーン + { + BenchmarkTimer timer("nodec::Formatter - Long chain"); + for (int i = 0; i < iterations; ++i) { + std::string result = nodec::Formatter() << "Start: " << i + << ", Value1: " << (i * 2) + << ", Value2: " << (i * 3.5) + << ", Value3: " << (i % 100) + << ", Value4: " << std::hex << i + << ", Value5: " << std::dec << (i + 1000) + << ", End"; + volatile auto len = result.length(); + (void)len; + } + } + + // nodec::optimized_ostringstream (FastFormatter)のベンチマーク + { + BenchmarkTimer timer("FastFormatter - Long chain"); + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream oss; + oss << "Start: " << i + << ", Value1: " << (i * 2) + << ", Value2: " << (i * 3.5) + << ", Value3: " << (i % 100) + << ", Value4: " << std::hex << i + << ", Value5: " << std::dec << (i + 1000) + << ", End"; + std::string result = oss.str(); + volatile auto len = result.length(); + (void)len; + } + } + + // std::ostringstreamのベンチマーク(比較用) + { + BenchmarkTimer timer("std::ostringstream - Long chain"); + for (int i = 0; i < iterations; ++i) { + std::ostringstream oss; + oss << "Start: " << i + << ", Value1: " << (i * 2) + << ", Value2: " << (i * 3.5) + << ", Value3: " << (i % 100) + << ", Value4: " << std::hex << i + << ", Value5: " << std::dec << (i + 1000) + << ", End"; + std::string result = oss.str(); + volatile auto len = result.length(); + (void)len; + } + } + + // std::string + 演算子のベンチマーク(比較用) + { + BenchmarkTimer timer("std::string + operator - Long chain"); + for (int i = 0; i < iterations; ++i) { + std::string result = std::string("Start: ") + std::to_string(i) + + ", Value1: " + std::to_string(i * 2) + + ", Value2: " + std::to_string(i * 3.5) + + ", Value3: " + std::to_string(i % 100) + + ", Value4: " + std::to_string(i) // 16進数は複雑なので10進数で代用 + + ", Value5: " + std::to_string(i + 1000) + + ", End"; + volatile auto len = result.length(); + (void)len; + } + } + + // テストが正常に実行されたことを確認 + CHECK(true); +} + +TEST_CASE("Benchmark - Memory Usage") { + const int iterations = 50000; + + std::cout << "\n--- Memory Usage Test ---" << std::endl; + + // 大量のFormatterオブジェクトを作成してメモリ使用量をテスト + { + BenchmarkTimer timer("nodec::Formatter - Multiple instances"); + std::vector results; + results.reserve(iterations); + + for (int i = 0; i < iterations; ++i) { + results.push_back(nodec::Formatter() << "Test " << i << " with value " << (i * 2.5)); + } + + // 結果を使用して最適化を防ぐ + volatile auto total_size = results.size(); + (void)total_size; } + + // nodec::optimized_ostringstream (FastFormatter)のベンチマーク + { + BenchmarkTimer timer("FastFormatter - Multiple instances"); + std::vector results; + results.reserve(iterations); + + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream oss; + oss << "Test " << i << " with value " << (i * 2.5); + results.push_back(oss.str()); + } + + // 結果を使用して最適化を防ぐ + volatile auto total_size = results.size(); + (void)total_size; + } + + { + BenchmarkTimer timer("std::ostringstream - Multiple instances"); + std::vector results; + results.reserve(iterations); + + for (int i = 0; i < iterations; ++i) { + std::ostringstream oss; + oss << "Test " << i << " with value " << (i * 2.5); + results.push_back(oss.str()); + } + + // 結果を使用して最適化を防ぐ + volatile auto total_size = results.size(); + (void)total_size; + } + + // テストが正常に実行されたことを確認 + CHECK(true); +} + +// doctestが自動的にテストを実行するため、main関数は不要 + +TEST_CASE("Benchmark - Reserve Capacity") { + const int iterations = 50000; + + std::cout << "\n--- Reserve Capacity Test ---" << std::endl; + + // FastFormatter with reserve + { + BenchmarkTimer timer("FastFormatter with reserve - Long text"); + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream oss(256); // Pre-reserve capacity + oss << "This is a longer text example that will test the reserve functionality: " + << i << ", value1: " << (i * 2) << ", value2: " << (i * 3.14) + << ", value3: " << std::hex << i << ", value4: " << std::dec << (i + 100) + << " - End of the long text"; + std::string result = oss.str(); + volatile auto len = result.length(); + (void)len; + } + } + + // FastFormatter without reserve (for comparison) + { + BenchmarkTimer timer("FastFormatter without reserve - Long text"); + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream oss; // No pre-reserve + oss << "This is a longer text example that will test the reserve functionality: " + << i << ", value1: " << (i * 2) << ", value2: " << (i * 3.14) + << ", value3: " << std::hex << i << ", value4: " << std::dec << (i + 100) + << " - End of the long text"; + std::string result = oss.str(); + volatile auto len = result.length(); + (void)len; + } + } + + // std::ostringstream (for comparison) + { + BenchmarkTimer timer("std::ostringstream - Long text"); + for (int i = 0; i < iterations; ++i) { + std::ostringstream oss; + oss << "This is a longer text example that will test the reserve functionality: " + << i << ", value1: " << (i * 2) << ", value2: " << (i * 3.14) + << ", value3: " << std::hex << i << ", value4: " << std::dec << (i + 100) + << " - End of the long text"; + std::string result = oss.str(); + volatile auto len = result.length(); + (void)len; + } + } + + // テストが正常に実行されたことを確認 + CHECK(true); +} diff --git a/tests/formatter/formatter_detailed_benchmark.cpp b/tests/formatter/formatter_detailed_benchmark.cpp new file mode 100644 index 0000000..cf64de7 --- /dev/null +++ b/tests/formatter/formatter_detailed_benchmark.cpp @@ -0,0 +1,121 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include + +#include +#include +#include +#include + +#include + +TEST_CASE("Detailed Benchmark - Construction Overhead") { + std::cout << "\n--- Construction Overhead Test ---" << std::endl; + const int iterations = 1000000; + + // Formatterオブジェクトの構築コスト + auto start = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + nodec::Formatter formatter; + volatile auto* ptr = &formatter; + (void)ptr; + } + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - start); + std::cout << "Formatter construction: " << duration.count() << " microseconds" << std::endl; + + // ostringstream構築コスト(比較用) + start = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + std::ostringstream oss; + volatile auto* ptr = &oss; + (void)ptr; + } + end = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration_cast(end - start); + std::cout << "ostringstream construction: " << duration.count() << " microseconds" << std::endl; + + CHECK(true); +} + +TEST_CASE("Detailed Benchmark - String Types") { + std::cout << "\n--- String Types Test ---" << std::endl; + const int iterations = 100000; + + // 異なる文字列型での性能テスト + std::string std_string = "Hello World"; + const char* c_string = "Hello World"; + + // std::string + auto start = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + std::string result = nodec::Formatter() << std_string << i; + volatile auto len = result.length(); + (void)len; + } + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - start); + std::cout << "std::string input: " << duration.count() << " microseconds" << std::endl; + + // const char* + start = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + std::string result = nodec::Formatter() << c_string << i; + volatile auto len = result.length(); + (void)len; + } + end = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration_cast(end - start); + std::cout << "const char* input: " << duration.count() << " microseconds" << std::endl; + + CHECK(true); +} + +TEST_CASE("Detailed Benchmark - Error Formatter") { + std::cout << "\n--- Error Formatter Test ---" << std::endl; + const int iterations = 50000; + + auto start = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + std::string result = nodec::ErrorFormatter(__FILE__, __LINE__) + << "Error occurred with value: " << i; + volatile auto len = result.length(); + (void)len; + } + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - start); + std::cout << "ErrorFormatter: " << duration.count() << " microseconds" << std::endl; + + CHECK(true); +} + +TEST_CASE("Detailed Benchmark - Multiple Instances") { + std::cout << "\n--- Multiple Instances Test ---" << std::endl; + // 注意: このテストは実際の並行性テストではなく、 + // 単一スレッドでの複数インスタンス作成テストです + + const int iterations = 10000; + std::vector results; + results.reserve(iterations * 3); + + auto start = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + // 複数のFormatterを同時に作成 + nodec::Formatter f1, f2, f3; + f1 << "Thread1: " << i; + f2 << "Thread2: " << (i * 2); + f3 << "Thread3: " << (i * 3); + + results.push_back(f1); + results.push_back(f2); + results.push_back(f3); + } + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - start); + std::cout << "Multiple instances: " << duration.count() << " microseconds" << std::endl; + + // 結果を使用して最適化を防ぐ + volatile auto total_size = results.size(); + (void)total_size; + + CHECK(true); +} diff --git a/tests/formatter/formatter_ostringstream_compat.cpp b/tests/formatter/formatter_ostringstream_compat.cpp new file mode 100644 index 0000000..2a246ae --- /dev/null +++ b/tests/formatter/formatter_ostringstream_compat.cpp @@ -0,0 +1,136 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include + +#include +#include +#include + +TEST_CASE("Formatter - Enhanced Manipulator Support") { + // setw テスト + std::string result1 = nodec::Formatter() << nodec::setw(10) << 42; + CHECK(result1 == " 42"); // 右揃え、幅10 + + // setfill テスト + std::string result2 = nodec::Formatter() << nodec::setfill('0') << nodec::setw(5) << 123; + CHECK(result2 == "00123"); + + // setprecision テスト + std::string result3 = nodec::Formatter() << std::fixed << nodec::setprecision(2) << 3.14159; + CHECK(result3 == "3.14"); + + // 組み合わせテスト + std::string result4 = nodec::Formatter() << nodec::setfill('*') << nodec::setw(8) << "Hi"; + CHECK(result4 == "******Hi"); +} + +TEST_CASE("Formatter - Number Base Support") { + // 16進数テスト(小文字) + std::string result1 = nodec::Formatter() << std::hex << 255; + CHECK(result1 == "ff"); + + // 16進数テスト(大文字) + std::string result2 = nodec::Formatter() << std::hex << std::uppercase << 255; + CHECK(result2 == "FF"); + + // 16進数 with showbase + std::string result3 = nodec::Formatter() << std::hex << std::showbase << 255; + CHECK(result3 == "0xff"); + + // 8進数テスト + std::string result4 = nodec::Formatter() << std::oct << 64; + CHECK(result4 == "100"); + + // 8進数 with showbase + std::string result5 = nodec::Formatter() << std::oct << std::showbase << 64; + CHECK(result5 == "0100"); + + // 10進数に戻す + std::string result6 = nodec::Formatter() << std::hex << 16 << ", " << std::dec << 16; + CHECK(result6 == "10, 16"); +} + +TEST_CASE("Formatter - Alignment Support") { + // 左揃え + std::string result1 = nodec::Formatter() << std::left << nodec::setw(8) << "Hi"; + CHECK(result1 == "Hi "); + + // 右揃え(デフォルト) + std::string result2 = nodec::Formatter() << std::right << nodec::setw(8) << "Hi"; + CHECK(result2 == " Hi"); + + // 内部揃え(符号付き数値) + std::string result3 = nodec::Formatter() << std::internal << nodec::setfill('0') << nodec::setw(8) << -42; + CHECK(result3 == "-0000042"); + + // 内部揃え(16進数) + std::string result4 = nodec::Formatter() << std::hex << std::showbase << std::internal + << nodec::setfill('0') << nodec::setw(8) << 255; + CHECK(result4 == "0x0000ff"); +} + +TEST_CASE("Formatter - Bool Support") { + // 数値としてのbool + std::string result1 = nodec::Formatter() << true << ", " << false; + CHECK(result1 == "1, 0"); + + // boolalpha + std::string result2 = nodec::Formatter() << std::boolalpha << true << ", " << false; + CHECK(result2 == "true, false"); + + // noboolalpha + std::string result3 = nodec::Formatter() << std::boolalpha << true << ", " + << std::noboolalpha << false; + CHECK(result3 == "true, 0"); +} + +TEST_CASE("Formatter - Float Formatting") { + // fixed フォーマット + std::string result1 = nodec::Formatter() << std::fixed << nodec::setprecision(3) << 3.14159; + CHECK(result1 == "3.142"); + + // scientific フォーマット + std::string result2 = nodec::Formatter() << std::scientific << nodec::setprecision(2) << 1234.5; + CHECK(result2 == "1.23e+03"); + + // uppercase scientific + std::string result3 = nodec::Formatter() << std::scientific << std::uppercase + << nodec::setprecision(2) << 1234.5; + CHECK(result3 == "1.23E+03"); +} + +TEST_CASE("Formatter - ostringstream Compatibility Check") { + // 同じフォーマットでostringreamと結果比較 + int value = 42; + + // Formatter + std::string fmt_result = nodec::Formatter() << nodec::setfill('0') << nodec::setw(6) << value; + + // ostringstream + std::ostringstream oss; + oss << std::setfill('0') << std::setw(6) << value; + std::string oss_result = oss.str(); + + CHECK(fmt_result == oss_result); + + // 16進数比較 + std::string fmt_hex = nodec::Formatter() << std::hex << std::uppercase << 255; + + std::ostringstream oss_hex; + oss_hex << std::hex << std::uppercase << 255; + std::string oss_hex_result = oss_hex.str(); + + CHECK(fmt_hex == oss_hex_result); +} + +TEST_CASE("Formatter - Complex Format Chains") { + // 複雑なフォーマットチェーン + std::string result = nodec::Formatter() + << "Value: " << std::hex << std::uppercase << std::showbase + << nodec::setfill('0') << nodec::setw(8) << 255 + << ", Float: " << std::dec << std::fixed << nodec::setprecision(2) << 3.14159 + << ", Bool: " << std::boolalpha << true; + + // 期待値:"Value: 00000XFF, Float: 3.14, Bool: true" + std::string expected = "Value: 00000XFF, Float: 3.14, Bool: true"; + CHECK(result == expected); +} diff --git a/tests/formatter/formatter_vector3_benchmark.cpp b/tests/formatter/formatter_vector3_benchmark.cpp new file mode 100644 index 0000000..e3a2bc7 --- /dev/null +++ b/tests/formatter/formatter_vector3_benchmark.cpp @@ -0,0 +1,243 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include + +#include +#include +#include +#include +#include + +#include +#include + +// ベンチマーク測定用のヘルパークラス +class BenchmarkTimer { +public: + BenchmarkTimer(const std::string& name) : name_(name) { + start_time_ = std::chrono::high_resolution_clock::now(); + } + + ~BenchmarkTimer() { + auto end_time = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end_time - start_time_); + std::cout << name_ << ": " << duration.count() << " microseconds" << std::endl; + } + +private: + std::string name_; + std::chrono::high_resolution_clock::time_point start_time_; +}; + +TEST_CASE("Benchmark - Vector3 Fallback Performance") { + const int iterations = 50000; + + std::cout << "\n--- Vector3 Fallback Performance Test ---" << std::endl; + + // テスト用のVector3データ + std::vector vectors; + vectors.reserve(iterations); + for (int i = 0; i < iterations; ++i) { + vectors.emplace_back(static_cast(i), static_cast(i * 2), static_cast(i * 3)); + } + + // nodec::Formatter(フォールバック使用) + { + BenchmarkTimer timer("nodec::Formatter - Vector3f fallback"); + std::vector results; + results.reserve(iterations); + + for (int i = 0; i < iterations; ++i) { + results.push_back(nodec::Formatter() << "Vec: " << vectors[i] << ", Index: " << i); + } + + // 結果を使用して最適化を防ぐ + volatile auto total_size = results.size(); + (void)total_size; + } + + // std::ostringstream(比較用) + { + BenchmarkTimer timer("std::ostringstream - Vector3f"); + std::vector results; + results.reserve(iterations); + + for (int i = 0; i < iterations; ++i) { + std::ostringstream oss; + oss << "Vec: " << vectors[i] << ", Index: " << i; + results.push_back(oss.str()); + } + + // 結果を使用して最適化を防ぐ + volatile auto total_size = results.size(); + (void)total_size; + } + + // std::string + 演算子(手動でVector3を文字列化) + { + BenchmarkTimer timer("std::string + operator - Manual Vector3f"); + std::vector results; + results.reserve(iterations); + + for (int i = 0; i < iterations; ++i) { + std::string result = std::string("Vec: ( ") + + std::to_string(vectors[i].x) + ", " + + std::to_string(vectors[i].y) + ", " + + std::to_string(vectors[i].z) + " ), Index: " + + std::to_string(i); + results.push_back(result); + } + + // 結果を使用して最適化を防ぐ + volatile auto total_size = results.size(); + (void)total_size; + } + + CHECK(true); +} + +TEST_CASE("Benchmark - Vector3 Types Comparison") { + const int iterations = 30000; + + std::cout << "\n--- Vector3 Types Performance Comparison ---" << std::endl; + + // Vector3f のベンチマーク + { + BenchmarkTimer timer("nodec::Formatter - Vector3f"); + nodec::Vector3f vec(1.5f, 2.7f, 3.9f); + + for (int i = 0; i < iterations; ++i) { + std::string result = nodec::Formatter() << "Float Vec: " << vec << " [" << i << "]"; + volatile auto len = result.length(); + (void)len; + } + } + + // Vector3i のベンチマーク + { + BenchmarkTimer timer("nodec::Formatter - Vector3i"); + nodec::Vector3i vec(15, 27, 39); + + for (int i = 0; i < iterations; ++i) { + std::string result = nodec::Formatter() << "Int Vec: " << vec << " [" << i << "]"; + volatile auto len = result.length(); + (void)len; + } + } + + // Vector3d のベンチマーク + { + BenchmarkTimer timer("nodec::Formatter - Vector3d"); + nodec::Vector3d vec(1.5, 2.7, 3.9); + + for (int i = 0; i < iterations; ++i) { + std::string result = nodec::Formatter() << "Double Vec: " << vec << " [" << i << "]"; + volatile auto len = result.length(); + (void)len; + } + } + + CHECK(true); +} + +TEST_CASE("Benchmark - Complex Vector3 Chains") { + const int iterations = 10000; + + std::cout << "\n--- Complex Vector3 Chain Test ---" << std::endl; + + // 複雑なVector3チェーンのベンチマーク + { + BenchmarkTimer timer("nodec::Formatter - Complex Vector3 chain"); + + for (int i = 0; i < iterations; ++i) { + nodec::Vector3f pos(static_cast(i), static_cast(i * 2), static_cast(i * 3)); + nodec::Vector3f vel(1.0f, 0.5f, -1.0f); + nodec::Vector3i id(i, i + 1, i + 2); + + std::string result = nodec::Formatter() + << "Entity[" << i << "] " + << "Pos: " << pos << ", " + << "Vel: " << vel << ", " + << "ID: " << id << ", " + << "Time: " << (i * 0.016f); + + volatile auto len = result.length(); + (void)len; + } + } + + // std::ostringstream での同等処理(比較用) + { + BenchmarkTimer timer("std::ostringstream - Complex Vector3 chain"); + + for (int i = 0; i < iterations; ++i) { + nodec::Vector3f pos(static_cast(i), static_cast(i * 2), static_cast(i * 3)); + nodec::Vector3f vel(1.0f, 0.5f, -1.0f); + nodec::Vector3i id(i, i + 1, i + 2); + + std::ostringstream oss; + oss << "Entity[" << i << "] " + << "Pos: " << pos << ", " + << "Vel: " << vel << ", " + << "ID: " << id << ", " + << "Time: " << (i * 0.016f); + + std::string result = oss.str(); + volatile auto len = result.length(); + (void)len; + } + } + + CHECK(true); +} + +TEST_CASE("Benchmark - Vector3 Memory Efficiency") { + const int iterations = 20000; + + std::cout << "\n--- Vector3 Memory Efficiency Test ---" << std::endl; + + // 大量のVector3文字列化でのメモリ効率テスト + { + BenchmarkTimer timer("nodec::Formatter - Vector3 memory test"); + std::vector results; + results.reserve(iterations); + + for (int i = 0; i < iterations; ++i) { + // 事前リザーブを使用 + nodec::Formatter formatter(64); // Vector3文字列は通常30-40文字程度 + + nodec::Vector3f vec( + static_cast(i * 1.1f), + static_cast(i * 2.2f), + static_cast(i * 3.3f) + ); + + formatter << "Vector[" << i << "]: " << vec << " magnitude"; + results.push_back(formatter.str()); + } + + volatile auto total_size = results.size(); + (void)total_size; + } + + // 事前リザーブなしでの比較 + { + BenchmarkTimer timer("nodec::Formatter - Vector3 without reserve"); + std::vector results; + results.reserve(iterations); + + for (int i = 0; i < iterations; ++i) { + nodec::Vector3f vec( + static_cast(i * 1.1f), + static_cast(i * 2.2f), + static_cast(i * 3.3f) + ); + + results.push_back(nodec::Formatter() << "Vector[" << i << "]: " << vec << " magnitude"); + } + + volatile auto total_size = results.size(); + (void)total_size; + } + + CHECK(true); +} diff --git a/tests/formatter/hex_debug.cpp b/tests/formatter/hex_debug.cpp new file mode 100644 index 0000000..764f88b --- /dev/null +++ b/tests/formatter/hex_debug.cpp @@ -0,0 +1,44 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +#include +#include +#include +#include + +TEST_CASE("Detailed hex format debugging") { + // Formatterでのテスト + nodec::Formatter fmt; + std::string result = fmt << std::hex << std::uppercase << std::showbase + << nodec::setfill('0') << nodec::setw(8) << 255; + std::cout << "Formatter result: '" << result << "'" << std::endl; + + // ostringstreamでのテスト + std::ostringstream oss; + oss << std::hex << std::uppercase << std::showbase << std::setfill('0') << std::setw(8) << 255; + std::string expected = oss.str(); + std::cout << "ostringstream result: '" << expected << "'" << std::endl; + + CHECK(result == expected); +} + +TEST_CASE("Step-by-step hex formatting") { + // ステップごとにフォーマットを確認 + nodec::Formatter fmt; + + // 1. 基本の16進数 + std::string result1 = nodec::Formatter() << std::hex << 255; + std::cout << "hex only: '" << result1 << "'" << std::endl; + + // 2. 大文字 + std::string result2 = nodec::Formatter() << std::hex << std::uppercase << 255; + std::cout << "hex + uppercase: '" << result2 << "'" << std::endl; + + // 3. showbaseを追加 + std::string result3 = nodec::Formatter() << std::hex << std::uppercase << std::showbase << 255; + std::cout << "hex + uppercase + showbase: '" << result3 << "'" << std::endl; + + // 4. setfillとsetwを追加 + std::string result4 = nodec::Formatter() << std::hex << std::uppercase << std::showbase + << nodec::setfill('0') << nodec::setw(8) << 255; + std::cout << "full format: '" << result4 << "'" << std::endl; +} diff --git a/tests/formatter/openmode_examples.cpp b/tests/formatter/openmode_examples.cpp new file mode 100644 index 0000000..f7bc6dd --- /dev/null +++ b/tests/formatter/openmode_examples.cpp @@ -0,0 +1,29 @@ +#include +#include + +int main() { + std::cout << "=== openmode の実用例 ===" << std::endl; + + // 1. 通常の出力(デフォルト) + std::ostringstream oss1; + oss1 << "Hello"; + oss1 << " World"; + std::cout << "通常: " << oss1.str() << std::endl; // "Hello World" + + // 2. 追記モード + std::ostringstream oss2("Initial: ", std::ios_base::app); + oss2 << "Added"; + std::cout << "追記: " << oss2.str() << std::endl; // "Initial: Added" + + // 3. 初期文字列を削除して開始 + std::ostringstream oss3("ToBeDeleted", std::ios_base::out | std::ios_base::trunc); + oss3 << "New content"; + std::cout << "削除後: " << oss3.str() << std::endl; // "New content" + + // 4. 初期文字列を保持(デフォルト動作) + std::ostringstream oss4("Keep: "); + oss4 << "Added"; + std::cout << "保持: " << oss4.str() << std::endl; // "Keep: Added" + + return 0; +} diff --git a/tests/formatter/optimized_ostringstream_test.cpp b/tests/formatter/optimized_ostringstream_test.cpp new file mode 100644 index 0000000..ac544b8 --- /dev/null +++ b/tests/formatter/optimized_ostringstream_test.cpp @@ -0,0 +1,206 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include + +#include +#include +#include +#include +#include + +TEST_CASE("Optimized ostringstream - Basic functionality") { + SUBCASE("Default construction and basic output") { + nodec::optimized_ostringstream oss; + oss << "Hello, World!"; + CHECK(oss.str() == "Hello, World!"); + } + + SUBCASE("Reserved construction") { + nodec::optimized_ostringstream oss(1024); + CHECK(oss.capacity() >= 1024); + + oss << "Reserved string"; + CHECK(oss.str() == "Reserved string"); + CHECK(oss.capacity() >= 1024); // 容量は保持される + } + + SUBCASE("Initial string with reserve") { + std::string initial = "Start: "; + nodec::optimized_ostringstream oss(initial, 512); + CHECK(oss.capacity() >= 512); + + oss << "Additional content"; + CHECK(oss.str() == "Start: Additional content"); + } +} + +TEST_CASE("Optimized ostringstream - Complete ostringstream compatibility") { + SUBCASE("Manipulators work exactly like ostringstream") { + // optimized_ostringstream + nodec::optimized_ostringstream oss; + oss << std::hex << std::uppercase << std::showbase + << std::setfill('0') << std::setw(8) << 255; + std::string result1 = oss.str(); + + // standard ostringstream + std::ostringstream std_oss; + std_oss << std::hex << std::uppercase << std::showbase + << std::setfill('0') << std::setw(8) << 255; + std::string result2 = std_oss.str(); + + CHECK(result1 == result2); + CHECK(result1 == "00000XFF"); // 期待される結果 + } + + SUBCASE("Float formatting") { + nodec::optimized_ostringstream oss; + oss << std::fixed << std::setprecision(2) << 3.14159; + + std::ostringstream std_oss; + std_oss << std::fixed << std::setprecision(2) << 3.14159; + + CHECK(oss.str() == std_oss.str()); + CHECK(oss.str() == "3.14"); + } + + SUBCASE("Boolean formatting") { + nodec::optimized_ostringstream oss; + oss << std::boolalpha << true << ", " << false; + + std::ostringstream std_oss; + std_oss << std::boolalpha << true << ", " << false; + + CHECK(oss.str() == std_oss.str()); + CHECK(oss.str() == "true, false"); + } +} + +TEST_CASE("Optimized ostringstream - Custom types support") { + SUBCASE("Vector3 support via ostream operator") { + nodec::Vector3f vec(1.0f, 2.0f, 3.0f); + + nodec::optimized_ostringstream oss; + oss << "Vector: " << vec; + + std::ostringstream std_oss; + std_oss << "Vector: " << vec; + + CHECK(oss.str() == std_oss.str()); + // Vector3のoperator<<が正しく呼ばれることを確認 + CHECK(oss.str().find("( 1, 2, 3 )") != std::string::npos); + } +} + +TEST_CASE("Optimized ostringstream - Performance features") { + SUBCASE("Reserve prevents reallocations") { + nodec::optimized_ostringstream oss(1000); + size_t initial_capacity = oss.capacity(); + + // 大量のデータを追加 + for (int i = 0; i < 100; ++i) { + oss << "Data chunk " << i << "; "; + } + + // 容量が維持されていることを確認(再配置が発生していない) + CHECK(oss.capacity() >= initial_capacity); + CHECK(oss.size() > 0); + } + + SUBCASE("Clear and reuse") { + nodec::optimized_ostringstream oss(512); + + oss << "First use"; + CHECK(oss.str() == "First use"); + + oss.clear_content(); + CHECK(oss.str().empty()); + CHECK(oss.capacity() >= 512); // 容量は保持 + + oss << "Second use"; + CHECK(oss.str() == "Second use"); + } +} + +TEST_CASE("Optimized ostringstream - Complex formatting chains") { + SUBCASE("Complex mixed formatting") { + nodec::optimized_ostringstream oss(256); + oss << "Value: " << std::hex << std::uppercase << std::showbase + << std::setfill('0') << std::setw(8) << 255 + << ", Float: " << std::dec << std::fixed << std::setprecision(2) << 3.14159 + << ", Bool: " << std::boolalpha << true; + + std::ostringstream std_oss; + std_oss << "Value: " << std::hex << std::uppercase << std::showbase + << std::setfill('0') << std::setw(8) << 255 + << ", Float: " << std::dec << std::fixed << std::setprecision(2) << 3.14159 + << ", Bool: " << std::boolalpha << true; + + CHECK(oss.str() == std_oss.str()); + CHECK(oss.str() == "Value: 00000XFF, Float: 3.14, Bool: true"); + } +} + +TEST_CASE("Optimized ostringstream - Move semantics") { + SUBCASE("Move construction") { + nodec::optimized_ostringstream oss1(256); + oss1 << "Original content"; + + auto oss2 = std::move(oss1); + CHECK(oss2.str() == "Original content"); + CHECK(oss2.capacity() >= 256); + } + + SUBCASE("Move assignment") { + nodec::optimized_ostringstream oss1(256); + oss1 << "Original content"; + + nodec::optimized_ostringstream oss2; + oss2 = std::move(oss1); + + CHECK(oss2.str() == "Original content"); + CHECK(oss2.capacity() >= 256); + } +} + +TEST_CASE("Optimized ostringstream - Conversion operators") { + SUBCASE("String conversion operator") { + nodec::optimized_ostringstream oss; + oss << "Test content"; + + std::string result = oss; // implicit conversion + CHECK(result == "Test content"); + + // 関数への渡し方も確認 + auto check_string = [](const std::string& s) { return s == "Test content"; }; + CHECK(check_string(oss)); + } +} + +// パフォーマンステスト(参考用) +TEST_CASE("Optimized ostringstream - Performance comparison") { + const int iterations = 1000; + const size_t reserve_size = 10000; + + SUBCASE("Reserved vs non-reserved performance indication") { + // このテストは実際のパフォーマンス測定ではなく、 + // APIが正しく動作することの確認 + + // 事前リザーブあり + nodec::optimized_ostringstream oss_reserved(reserve_size); + for (int i = 0; i < iterations; ++i) { + oss_reserved << "Item " << i << ": some content; "; + } + + // 事前リザーブなし + nodec::optimized_ostringstream oss_normal; + for (int i = 0; i < iterations; ++i) { + oss_normal << "Item " << i << ": some content; "; + } + + // 両方とも同じ結果を生成することを確認 + CHECK(oss_reserved.str() == oss_normal.str()); + CHECK(oss_reserved.capacity() >= reserve_size); + + // パフォーマンス上の利点は実際の使用で測定される + // ここでは機能の正確性のみを確認 + } +} diff --git a/tests/formatter/ostringstream_verification.cpp b/tests/formatter/ostringstream_verification.cpp new file mode 100644 index 0000000..20ab977 --- /dev/null +++ b/tests/formatter/ostringstream_verification.cpp @@ -0,0 +1,15 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +#include +#include +#include + +TEST_CASE("ostringstream behavior verification") { + std::ostringstream oss; + oss << std::hex << std::uppercase << std::showbase << std::setfill('0') << std::setw(8) << 255; + std::string result = oss.str(); + std::cout << "ostringstream result: " << result << std::endl; + + // 期待される動作をここで確認 + CHECK(result.length() > 0); +} diff --git a/tests/formatter/vector3_debug.cpp b/tests/formatter/vector3_debug.cpp new file mode 100644 index 0000000..1bc5167 --- /dev/null +++ b/tests/formatter/vector3_debug.cpp @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +int main() { + nodec::Vector3f vec(1.0f, 2.0f, 3.0f); + + std::cout << "=== Vector3 Output Test ===" << std::endl; + + // optimized_ostringstream + nodec::optimized_ostringstream oss; + oss << "Vector: " << vec; + std::cout << "optimized_ostringstream: '" << oss.str() << "'" << std::endl; + + // standard ostringstream + std::ostringstream std_oss; + std_oss << "Vector: " << vec; + std::cout << "standard ostringstream: '" << std_oss.str() << "'" << std::endl; + + // 直接Vector3出力 + std::cout << "Direct output: " << vec << std::endl; + + return 0; +} From 48fd7de2045ce83a7e45d6648e65026438840e41 Mon Sep 17 00:00:00 2001 From: IOE Date: Thu, 3 Jul 2025 09:32:48 +0900 Subject: [PATCH 2/6] test --- include/nodec/formatter.hpp | 1286 +++++++++--------- include/nodec/optimized_ostringstream.hpp | 157 +-- overflow_xsputn_timing_analysis.md | 189 +++ tests/CMakeLists.txt | 43 +- tests/formatter/eof_necessity_test.cpp | 245 ++++ tests/formatter/formatter_benchmark.cpp | 164 ++- tests/formatter/overflow_xsputn_timing.cpp | 217 +++ tests/formatter/smart_formatter_analysis.cpp | 220 +++ tests/formatter/traits_eof_analysis.cpp | 137 ++ tests/formatter/view_analysis.cpp | 98 ++ view_performance_analysis.md | 171 +++ 11 files changed, 2177 insertions(+), 750 deletions(-) create mode 100644 overflow_xsputn_timing_analysis.md create mode 100644 tests/formatter/eof_necessity_test.cpp create mode 100644 tests/formatter/overflow_xsputn_timing.cpp create mode 100644 tests/formatter/smart_formatter_analysis.cpp create mode 100644 tests/formatter/traits_eof_analysis.cpp create mode 100644 tests/formatter/view_analysis.cpp create mode 100644 view_performance_analysis.md diff --git a/include/nodec/formatter.hpp b/include/nodec/formatter.hpp index aff933a..5367b91 100644 --- a/include/nodec/formatter.hpp +++ b/include/nodec/formatter.hpp @@ -3,657 +3,678 @@ #include -#include -#include #include -#include #include +#include +#include +#include namespace nodec { -// カスタム操作子の定義 -struct SetWidth { - int width; - explicit SetWidth(int w) : width(w) {} -}; - -struct SetFill { - char fill_char; - explicit SetFill(char c) : fill_char(c) {} -}; - -struct SetPrecision { - int precision; - explicit SetPrecision(int p) : precision(p) {} -}; - -// カスタム操作子のヘルパー関数 -inline SetWidth setw(int width) { return SetWidth(width); } -inline SetFill setfill(char fill_char) { return SetFill(fill_char); } -inline SetPrecision setprecision(int precision) { return SetPrecision(precision); } - -/* - * The Text Formatter - * - * if you use C++20, consider to use std::format. - * * - * - * It is designed for easy use of text format even though under C++20. - * ostringstream compatible formatter with high performance. - * - * Implementation refs: - * * - * - */ - class Formatter { public: - enum class Alignment { - Left, - Right, - Internal - }; - - enum class FloatFormat { - Default, - Fixed, - Scientific - }; - - Formatter() noexcept = default; - ~Formatter() noexcept = default; - - // 事前リザーブ機能付きコンストラクタ - explicit Formatter(size_t reserve_size) noexcept { - buffer_.reserve(reserve_size); - } + Formatter() noexcept {} + ~Formatter() noexcept {} - // 基本型の効率的な処理 - Formatter &operator<<(const char* value) noexcept { - if (value) { - append_string_with_format(value); - } - return *this; - } - - Formatter &operator<<(const std::string& value) noexcept { - append_string_with_format(value); - return *this; - } - - Formatter &operator<<(char value) noexcept { - append_char_with_format(value); - return *this; - } - - // 整数型の効率的な処理 - Formatter &operator<<(int value) noexcept { - append_integer_with_format(value); - return *this; - } - - Formatter &operator<<(long value) noexcept { - append_integer_with_format(value); - return *this; - } - - Formatter &operator<<(long long value) noexcept { - append_integer_with_format(value); - return *this; - } - - Formatter &operator<<(unsigned int value) noexcept { - append_unsigned_integer_with_format(value); - return *this; - } - - Formatter &operator<<(unsigned long value) noexcept { - append_unsigned_integer_with_format(value); - return *this; - } - - Formatter &operator<<(unsigned long long value) noexcept { - append_unsigned_integer_with_format(value); - return *this; - } - - // 浮動小数点型の処理 - Formatter &operator<<(float value) noexcept { - append_float_with_format(static_cast(value)); - return *this; - } - - Formatter &operator<<(double value) noexcept { - append_float_with_format(value); - return *this; - } - - // ostream操作子のサポート(hex, dec等) - Formatter &operator<<(std::ios_base& (*manipulator)(std::ios_base&)) noexcept { - if (manipulator == std::hex) { - base_mode_ = 16; - } else if (manipulator == std::dec) { - base_mode_ = 10; - } else if (manipulator == std::oct) { - base_mode_ = 8; - } else if (manipulator == std::fixed) { - float_mode_ = FloatMode::Fixed; - } else if (manipulator == std::scientific) { - float_mode_ = FloatMode::Scientific; - } else if (manipulator == std::uppercase) { - uppercase_ = true; - } else if (manipulator == std::nouppercase) { - uppercase_ = false; - } else if (manipulator == std::showbase) { - showbase_ = true; - } else if (manipulator == std::noshowbase) { - showbase_ = false; - } else if (manipulator == std::boolalpha) { - boolalpha_ = true; - } else if (manipulator == std::noboolalpha) { - boolalpha_ = false; - } else if (manipulator == std::left) { - align_mode_ = AlignMode::Left; - } else if (manipulator == std::right) { - align_mode_ = AlignMode::Right; - } else if (manipulator == std::internal) { - align_mode_ = AlignMode::Internal; - } - return *this; - } - - // カスタム操作子のサポート - Formatter &operator<<(const SetWidth& sw) noexcept { - width_ = sw.width; - return *this; - } - - Formatter &operator<<(const SetFill& sf) noexcept { - fill_char_ = sf.fill_char; - return *this; - } - - Formatter &operator<<(const SetPrecision& sp) noexcept { - precision_ = sp.precision; - return *this; - } - - // bool型の特別処理 - Formatter &operator<<(bool value) noexcept { - if (boolalpha_) { - buffer_ += (value ? "true" : "false"); - } else { - buffer_ += (value ? '1' : '0'); - } - return *this; - } - - // その他の型(フォールバック)- ただし、専用マニピュレータは除外 template - typename std::enable_if< - !std::is_same::value && - !std::is_same::value && - !std::is_same::value, - Formatter& - >::type operator<<(const T &value) noexcept { - // 複雑な型はostringstreamにフォールバック - std::ostringstream oss; - oss << value; - buffer_ += oss.str(); + Formatter &operator<<(const T &value) noexcept { + stream_ << value; return *this; } operator std::string() const noexcept { - return buffer_; - } - - // 追加のユーティリティメソッド - void reserve(size_t size) noexcept { - buffer_.reserve(size); - } - - void clear() noexcept { - buffer_.clear(); - // フォーマット状態をリセット - base_mode_ = 10; - float_mode_ = FloatMode::Default; - precision_ = 6; - width_ = 0; - fill_char_ = ' '; - uppercase_ = false; - showbase_ = false; - boolalpha_ = false; - align_mode_ = AlignMode::Right; - } - - size_t size() const noexcept { - return buffer_.size(); - } - - const std::string& str() const noexcept { - return buffer_; - } - - // マニピュレータ用のpublicメソッド - void apply_setw(int w) noexcept { - width_ = w; - } - - void apply_setfill(char c) noexcept { - fill_char_ = c; - } - - void apply_setprecision(int p) noexcept { - precision_ = p; + return stream_.str(); } private: - enum class FloatMode { - Default, - Fixed, - Scientific - }; - - enum class AlignMode { - Left, - Right, - Internal - }; - - std::string buffer_; - - // フォーマット状態 - int base_mode_ = 10; // 10進数がデフォルト - FloatMode float_mode_ = FloatMode::Default; - int precision_ = 6; // デフォルト精度 - int width_ = 0; // フィールド幅 - char fill_char_ = ' '; // 埋め文字 - bool uppercase_ = false; // 大文字表示 - bool showbase_ = false; // プレフィックス表示 - bool boolalpha_ = false; // bool値をtrue/falseで表示 - AlignMode align_mode_ = AlignMode::Right; // 右揃えがデフォルト - - // マニピュレータ適用 - template - void apply_manipulator(const T& manipulator) noexcept { - // SFINAE を使ってsetw, setfill, setprecisionを識別 - // 実際の実装では型特性を使用するが、簡略化のため条件分岐で対応 - // この部分は実装が複雑になるため、後で詳細化 - } - - // 整数を効率的に文字列に変換 - template - void append_integer(IntType value) noexcept { - if (base_mode_ == 16) { - if (value < 0) { - buffer_ += '-'; - append_hex_with_format(static_cast(-value)); - } else { - append_hex_with_format(static_cast(value)); - } - } else if (base_mode_ == 8) { - if (value < 0) { - buffer_ += '-'; - append_oct_with_format(static_cast(-value)); - } else { - append_oct_with_format(static_cast(value)); - } - } else { - append_decimal_with_format(value); - } - } - - template - void append_unsigned_integer(UIntType value) noexcept { - if (base_mode_ == 16) { - append_hex_with_format(value); - } else if (base_mode_ == 8) { - append_oct_with_format(value); - } else { - append_decimal_with_format(value); - } - } - - // 10進数フォーマット - template - void append_decimal_with_format(IntType value) noexcept { - std::string result; - - if (value == 0) { - result = "0"; - } else { - bool negative = value < 0; - if (negative) { - value = -value; - } - - char temp[32]; - int pos = 0; - while (value > 0) { - temp[pos++] = '0' + (value % 10); - value /= 10; - } - - if (negative) { - result += '-'; - } - - for (int i = pos - 1; i >= 0; --i) { - result += temp[i]; - } - } - - apply_field_formatting(result); - } - - // 16進数フォーマット - void append_hex_with_format(unsigned long long value) noexcept { - std::string result; - - if (showbase_ && value != 0) { - result += "0x"; - } - - if (value == 0) { - result += '0'; - } else { - char temp[32]; - int pos = 0; - const char* hex_chars = uppercase_ ? "0123456789ABCDEF" : "0123456789abcdef"; - - while (value > 0) { - temp[pos++] = hex_chars[value % 16]; - value /= 16; - } - - for (int i = pos - 1; i >= 0; --i) { - result += temp[i]; - } - } - - apply_field_formatting(result); - } - - // 8進数フォーマット - void append_oct_with_format(unsigned long long value) noexcept { - std::string result; - - if (showbase_ && value != 0) { - result += '0'; - } - - if (value == 0) { - result += '0'; - } else { - char temp[32]; - int pos = 0; - - while (value > 0) { - temp[pos++] = '0' + (value % 8); - value /= 8; - } - - for (int i = pos - 1; i >= 0; --i) { - result += temp[i]; - } - } - - apply_field_formatting(result); - } - - // フィールドフォーマット適用 - void apply_field_formatting(const std::string& content) noexcept { - if (width_ <= static_cast(content.length())) { - // 幅指定なし、または内容が幅以上 - buffer_ += content; - width_ = 0; // 一度使用したらリセット - return; - } - - int padding = width_ - static_cast(content.length()); - - switch (align_mode_) { - case AlignMode::Left: - buffer_ += content; - buffer_.append(padding, fill_char_); - break; - - case AlignMode::Right: - buffer_.append(padding, fill_char_); - buffer_ += content; - break; - - case AlignMode::Internal: - // 符号と数値の間に埋め文字を挿入 - if (!content.empty() && (content[0] == '-' || content[0] == '+')) { - buffer_ += content[0]; - buffer_.append(padding, fill_char_); - buffer_ += content.substr(1); - } else if (content.length() >= 2 && content[0] == '0' && (content[1] == 'x' || content[1] == 'X')) { - buffer_ += content.substr(0, 2); - buffer_.append(padding, fill_char_); - buffer_ += content.substr(2); - } else { - buffer_.append(padding, fill_char_); - buffer_ += content; - } - break; - } - - width_ = 0; // 一度使用したらリセット - } - - // フォーマット付きの文字列・数値処理メソッド - void append_string_with_format(const std::string& str) noexcept { - if (width_ > 0 && static_cast(str.length()) < width_) { - apply_field_formatting(str); - } else { - buffer_ += str; - } - width_ = 0; // 使用後リセット - } - - void append_char_with_format(char c) noexcept { - if (width_ > 0 && width_ > 1) { - std::string str(1, c); - apply_field_formatting(str); - } else { - buffer_ += c; - } - width_ = 0; // 使用後リセット - } - - void append_integer_with_format(long long value) noexcept { - std::string num_str = format_integer(value); - if (width_ > 0 && static_cast(num_str.length()) < width_) { - apply_field_formatting(num_str); - } else { - buffer_ += num_str; - } - width_ = 0; // 使用後リセット - } - - void append_unsigned_integer_with_format(unsigned long long value) noexcept { - std::string num_str = format_unsigned_integer(value); - if (width_ > 0 && static_cast(num_str.length()) < width_) { - apply_field_formatting(num_str); - } else { - buffer_ += num_str; - } - width_ = 0; // 使用後リセット - } - - void append_float_with_format(double value) noexcept { - std::ostringstream oss; - oss.precision(precision_); - - // フロートモード設定 - switch (float_mode_) { - case FloatMode::Fixed: - oss << std::fixed; - break; - case FloatMode::Scientific: - oss << std::scientific; - break; - case FloatMode::Default: - // デフォルトフォーマット - break; - } - - // 大文字設定 - if (uppercase_) { - oss << std::uppercase; - } - - oss << value; - std::string result = oss.str(); - - if (width_ > 0 && static_cast(result.length()) < width_) { - apply_field_formatting(result); - } else { - buffer_ += result; - } - width_ = 0; // 使用後リセット - } - - // 数値フォーマット関数 - std::string format_integer(long long value) noexcept { - if (base_mode_ == 16) { - return format_hex_signed(value); - } else if (base_mode_ == 8) { - return format_oct_signed(value); - } else { - return format_decimal(value); - } - } - - std::string format_unsigned_integer(unsigned long long value) noexcept { - if (base_mode_ == 16) { - return format_hex(value); - } else if (base_mode_ == 8) { - return format_oct(value); - } else { - return format_unsigned_decimal(value); - } - } - - std::string format_hex_signed(long long value) noexcept { - if (value < 0) { - return "-" + format_hex(static_cast(-value)); - } else { - return format_hex(static_cast(value)); - } - } - - std::string format_oct_signed(long long value) noexcept { - if (value < 0) { - return "-" + format_oct(static_cast(-value)); - } else { - return format_oct(static_cast(value)); - } - } - - std::string format_decimal(long long value) noexcept { - if (value == 0) return "0"; - - std::string result; - bool negative = value < 0; - if (negative) value = -value; - - while (value > 0) { - result = char('0' + value % 10) + result; - value /= 10; - } - - if (negative) result = "-" + result; - return result; - } - - std::string format_unsigned_decimal(unsigned long long value) noexcept { - if (value == 0) return "0"; - - std::string result; - while (value > 0) { - result = char('0' + value % 10) + result; - value /= 10; - } - return result; - } - - std::string format_hex(unsigned long long value) noexcept { - if (value == 0) { - std::string result = "0"; - if (showbase_) result = "0x" + result; - return result; - } - - std::string result; - const char* hex_chars = uppercase_ ? "0123456789ABCDEF" : "0123456789abcdef"; - - while (value > 0) { - result = hex_chars[value % 16] + result; - value /= 16; - } - - if (showbase_) { - result = (uppercase_ ? "0X" : "0x") + result; - } - - return result; - } - - std::string format_oct(unsigned long long value) noexcept { - if (value == 0) { - std::string result = "0"; - if (showbase_) result = "0" + result; - return result; - } - - std::string result; - while (value > 0) { - result = char('0' + value % 8) + result; - value /= 8; - } - - if (showbase_) { - result = "0" + result; - } - - return result; - } - - void append_float(double value) noexcept { - std::ostringstream oss; - - // 精度設定 - oss.precision(precision_); - - // フロートモード設定 - switch (float_mode_) { - case FloatMode::Fixed: - oss << std::fixed; - break; - case FloatMode::Scientific: - oss << std::scientific; - break; - case FloatMode::Default: - // デフォルトフォーマット - break; - } - - // 大文字設定 - if (uppercase_) { - oss << std::uppercase; - } - - oss << value; - std::string result = oss.str(); - apply_field_formatting(result); - } + std::ostringstream stream_; NODEC_DISABLE_COPY(Formatter) }; +// // カスタム操作子の定義 +// struct SetWidth { +// int width; +// explicit SetWidth(int w) : width(w) {} +// }; + +// struct SetFill { +// char fill_char; +// explicit SetFill(char c) : fill_char(c) {} +// }; + +// struct SetPrecision { +// int precision; +// explicit SetPrecision(int p) : precision(p) {} +// }; + +// // カスタム操作子のヘルパー関数 +// inline SetWidth setw(int width) { return SetWidth(width); } +// inline SetFill setfill(char fill_char) { return SetFill(fill_char); } +// inline SetPrecision setprecision(int precision) { return SetPrecision(precision); } + +// /* +// * The Text Formatter +// * +// * if you use C++20, consider to use std::format. +// * * +// * +// * It is designed for easy use of text format even though under C++20. +// * ostringstream compatible formatter with high performance. +// * +// * Implementation refs: +// * * +// * +// */ + +// class Formatter { +// public: +// enum class Alignment { +// Left, +// Right, +// Internal +// }; + +// enum class FloatFormat { +// Default, +// Fixed, +// Scientific +// }; + +// Formatter() noexcept = default; +// ~Formatter() noexcept = default; + +// // 事前リザーブ機能付きコンストラクタ +// explicit Formatter(size_t reserve_size) noexcept { +// buffer_.reserve(reserve_size); +// } + +// // 基本型の効率的な処理 +// Formatter &operator<<(const char* value) noexcept { +// if (value) { +// append_string_with_format(value); +// } +// return *this; +// } + +// Formatter &operator<<(const std::string& value) noexcept { +// append_string_with_format(value); +// return *this; +// } + +// Formatter &operator<<(char value) noexcept { +// append_char_with_format(value); +// return *this; +// } + +// // 整数型の効率的な処理 +// Formatter &operator<<(int value) noexcept { +// append_integer_with_format(value); +// return *this; +// } + +// Formatter &operator<<(long value) noexcept { +// append_integer_with_format(value); +// return *this; +// } + +// Formatter &operator<<(long long value) noexcept { +// append_integer_with_format(value); +// return *this; +// } + +// Formatter &operator<<(unsigned int value) noexcept { +// append_unsigned_integer_with_format(value); +// return *this; +// } + +// Formatter &operator<<(unsigned long value) noexcept { +// append_unsigned_integer_with_format(value); +// return *this; +// } + +// Formatter &operator<<(unsigned long long value) noexcept { +// append_unsigned_integer_with_format(value); +// return *this; +// } + +// // 浮動小数点型の処理 +// Formatter &operator<<(float value) noexcept { +// append_float_with_format(static_cast(value)); +// return *this; +// } + +// Formatter &operator<<(double value) noexcept { +// append_float_with_format(value); +// return *this; +// } + +// // ostream操作子のサポート(hex, dec等) +// Formatter &operator<<(std::ios_base& (*manipulator)(std::ios_base&)) noexcept { +// if (manipulator == std::hex) { +// base_mode_ = 16; +// } else if (manipulator == std::dec) { +// base_mode_ = 10; +// } else if (manipulator == std::oct) { +// base_mode_ = 8; +// } else if (manipulator == std::fixed) { +// float_mode_ = FloatMode::Fixed; +// } else if (manipulator == std::scientific) { +// float_mode_ = FloatMode::Scientific; +// } else if (manipulator == std::uppercase) { +// uppercase_ = true; +// } else if (manipulator == std::nouppercase) { +// uppercase_ = false; +// } else if (manipulator == std::showbase) { +// showbase_ = true; +// } else if (manipulator == std::noshowbase) { +// showbase_ = false; +// } else if (manipulator == std::boolalpha) { +// boolalpha_ = true; +// } else if (manipulator == std::noboolalpha) { +// boolalpha_ = false; +// } else if (manipulator == std::left) { +// align_mode_ = AlignMode::Left; +// } else if (manipulator == std::right) { +// align_mode_ = AlignMode::Right; +// } else if (manipulator == std::internal) { +// align_mode_ = AlignMode::Internal; +// } +// return *this; +// } + +// // カスタム操作子のサポート +// Formatter &operator<<(const SetWidth& sw) noexcept { +// width_ = sw.width; +// return *this; +// } + +// Formatter &operator<<(const SetFill& sf) noexcept { +// fill_char_ = sf.fill_char; +// return *this; +// } + +// Formatter &operator<<(const SetPrecision& sp) noexcept { +// precision_ = sp.precision; +// return *this; +// } + +// // bool型の特別処理 +// Formatter &operator<<(bool value) noexcept { +// if (boolalpha_) { +// buffer_ += (value ? "true" : "false"); +// } else { +// buffer_ += (value ? '1' : '0'); +// } +// return *this; +// } + +// // その他の型(フォールバック)- ただし、専用マニピュレータは除外 +// template +// typename std::enable_if< +// !std::is_same::value && +// !std::is_same::value && +// !std::is_same::value, +// Formatter& +// >::type operator<<(const T &value) noexcept { +// // 複雑な型はostringstreamにフォールバック +// std::ostringstream oss; +// oss << value; +// buffer_ += oss.str(); +// return *this; +// } + +// operator std::string() const noexcept { +// return buffer_; +// } + +// // 追加のユーティリティメソッド +// void reserve(size_t size) noexcept { +// buffer_.reserve(size); +// } + +// void clear() noexcept { +// buffer_.clear(); +// // フォーマット状態をリセット +// base_mode_ = 10; +// float_mode_ = FloatMode::Default; +// precision_ = 6; +// width_ = 0; +// fill_char_ = ' '; +// uppercase_ = false; +// showbase_ = false; +// boolalpha_ = false; +// align_mode_ = AlignMode::Right; +// } + +// size_t size() const noexcept { +// return buffer_.size(); +// } + +// const std::string& str() const noexcept { +// return buffer_; +// } + +// // マニピュレータ用のpublicメソッド +// void apply_setw(int w) noexcept { +// width_ = w; +// } + +// void apply_setfill(char c) noexcept { +// fill_char_ = c; +// } + +// void apply_setprecision(int p) noexcept { +// precision_ = p; +// } + +// private: +// enum class FloatMode { +// Default, +// Fixed, +// Scientific +// }; + +// enum class AlignMode { +// Left, +// Right, +// Internal +// }; + +// std::string buffer_; + +// // フォーマット状態 +// int base_mode_ = 10; // 10進数がデフォルト +// FloatMode float_mode_ = FloatMode::Default; +// int precision_ = 6; // デフォルト精度 +// int width_ = 0; // フィールド幅 +// char fill_char_ = ' '; // 埋め文字 +// bool uppercase_ = false; // 大文字表示 +// bool showbase_ = false; // プレフィックス表示 +// bool boolalpha_ = false; // bool値をtrue/falseで表示 +// AlignMode align_mode_ = AlignMode::Right; // 右揃えがデフォルト + +// // マニピュレータ適用 +// template +// void apply_manipulator(const T& manipulator) noexcept { +// // SFINAE を使ってsetw, setfill, setprecisionを識別 +// // 実際の実装では型特性を使用するが、簡略化のため条件分岐で対応 +// // この部分は実装が複雑になるため、後で詳細化 +// } + +// // 整数を効率的に文字列に変換 +// template +// void append_integer(IntType value) noexcept { +// if (base_mode_ == 16) { +// if (value < 0) { +// buffer_ += '-'; +// append_hex_with_format(static_cast(-value)); +// } else { +// append_hex_with_format(static_cast(value)); +// } +// } else if (base_mode_ == 8) { +// if (value < 0) { +// buffer_ += '-'; +// append_oct_with_format(static_cast(-value)); +// } else { +// append_oct_with_format(static_cast(value)); +// } +// } else { +// append_decimal_with_format(value); +// } +// } + +// template +// void append_unsigned_integer(UIntType value) noexcept { +// if (base_mode_ == 16) { +// append_hex_with_format(value); +// } else if (base_mode_ == 8) { +// append_oct_with_format(value); +// } else { +// append_decimal_with_format(value); +// } +// } + +// // 10進数フォーマット +// template +// void append_decimal_with_format(IntType value) noexcept { +// std::string result; + +// if (value == 0) { +// result = "0"; +// } else { +// bool negative = value < 0; +// if (negative) { +// value = -value; +// } + +// char temp[32]; +// int pos = 0; +// while (value > 0) { +// temp[pos++] = '0' + (value % 10); +// value /= 10; +// } + +// if (negative) { +// result += '-'; +// } + +// for (int i = pos - 1; i >= 0; --i) { +// result += temp[i]; +// } +// } + +// apply_field_formatting(result); +// } + +// // 16進数フォーマット +// void append_hex_with_format(unsigned long long value) noexcept { +// std::string result; + +// if (showbase_ && value != 0) { +// result += "0x"; +// } + +// if (value == 0) { +// result += '0'; +// } else { +// char temp[32]; +// int pos = 0; +// const char* hex_chars = uppercase_ ? "0123456789ABCDEF" : "0123456789abcdef"; + +// while (value > 0) { +// temp[pos++] = hex_chars[value % 16]; +// value /= 16; +// } + +// for (int i = pos - 1; i >= 0; --i) { +// result += temp[i]; +// } +// } + +// apply_field_formatting(result); +// } + +// // 8進数フォーマット +// void append_oct_with_format(unsigned long long value) noexcept { +// std::string result; + +// if (showbase_ && value != 0) { +// result += '0'; +// } + +// if (value == 0) { +// result += '0'; +// } else { +// char temp[32]; +// int pos = 0; + +// while (value > 0) { +// temp[pos++] = '0' + (value % 8); +// value /= 8; +// } + +// for (int i = pos - 1; i >= 0; --i) { +// result += temp[i]; +// } +// } + +// apply_field_formatting(result); +// } + +// // フィールドフォーマット適用 +// void apply_field_formatting(const std::string& content) noexcept { +// if (width_ <= static_cast(content.length())) { +// // 幅指定なし、または内容が幅以上 +// buffer_ += content; +// width_ = 0; // 一度使用したらリセット +// return; +// } + +// int padding = width_ - static_cast(content.length()); + +// switch (align_mode_) { +// case AlignMode::Left: +// buffer_ += content; +// buffer_.append(padding, fill_char_); +// break; + +// case AlignMode::Right: +// buffer_.append(padding, fill_char_); +// buffer_ += content; +// break; + +// case AlignMode::Internal: +// // 符号と数値の間に埋め文字を挿入 +// if (!content.empty() && (content[0] == '-' || content[0] == '+')) { +// buffer_ += content[0]; +// buffer_.append(padding, fill_char_); +// buffer_ += content.substr(1); +// } else if (content.length() >= 2 && content[0] == '0' && (content[1] == 'x' || content[1] == 'X')) { +// buffer_ += content.substr(0, 2); +// buffer_.append(padding, fill_char_); +// buffer_ += content.substr(2); +// } else { +// buffer_.append(padding, fill_char_); +// buffer_ += content; +// } +// break; +// } + +// width_ = 0; // 一度使用したらリセット +// } + +// // フォーマット付きの文字列・数値処理メソッド +// void append_string_with_format(const std::string& str) noexcept { +// if (width_ > 0 && static_cast(str.length()) < width_) { +// apply_field_formatting(str); +// } else { +// buffer_ += str; +// } +// width_ = 0; // 使用後リセット +// } + +// void append_char_with_format(char c) noexcept { +// if (width_ > 0 && width_ > 1) { +// std::string str(1, c); +// apply_field_formatting(str); +// } else { +// buffer_ += c; +// } +// width_ = 0; // 使用後リセット +// } + +// void append_integer_with_format(long long value) noexcept { +// std::string num_str = format_integer(value); +// if (width_ > 0 && static_cast(num_str.length()) < width_) { +// apply_field_formatting(num_str); +// } else { +// buffer_ += num_str; +// } +// width_ = 0; // 使用後リセット +// } + +// void append_unsigned_integer_with_format(unsigned long long value) noexcept { +// std::string num_str = format_unsigned_integer(value); +// if (width_ > 0 && static_cast(num_str.length()) < width_) { +// apply_field_formatting(num_str); +// } else { +// buffer_ += num_str; +// } +// width_ = 0; // 使用後リセット +// } + +// void append_float_with_format(double value) noexcept { +// std::ostringstream oss; +// oss.precision(precision_); + +// // フロートモード設定 +// switch (float_mode_) { +// case FloatMode::Fixed: +// oss << std::fixed; +// break; +// case FloatMode::Scientific: +// oss << std::scientific; +// break; +// case FloatMode::Default: +// // デフォルトフォーマット +// break; +// } + +// // 大文字設定 +// if (uppercase_) { +// oss << std::uppercase; +// } + +// oss << value; +// std::string result = oss.str(); + +// if (width_ > 0 && static_cast(result.length()) < width_) { +// apply_field_formatting(result); +// } else { +// buffer_ += result; +// } +// width_ = 0; // 使用後リセット +// } + +// // 数値フォーマット関数 +// std::string format_integer(long long value) noexcept { +// if (base_mode_ == 16) { +// return format_hex_signed(value); +// } else if (base_mode_ == 8) { +// return format_oct_signed(value); +// } else { +// return format_decimal(value); +// } +// } + +// std::string format_unsigned_integer(unsigned long long value) noexcept { +// if (base_mode_ == 16) { +// return format_hex(value); +// } else if (base_mode_ == 8) { +// return format_oct(value); +// } else { +// return format_unsigned_decimal(value); +// } +// } + +// std::string format_hex_signed(long long value) noexcept { +// if (value < 0) { +// return "-" + format_hex(static_cast(-value)); +// } else { +// return format_hex(static_cast(value)); +// } +// } + +// std::string format_oct_signed(long long value) noexcept { +// if (value < 0) { +// return "-" + format_oct(static_cast(-value)); +// } else { +// return format_oct(static_cast(value)); +// } +// } + +// std::string format_decimal(long long value) noexcept { +// if (value == 0) return "0"; + +// std::string result; +// bool negative = value < 0; +// if (negative) value = -value; + +// while (value > 0) { +// result = char('0' + value % 10) + result; +// value /= 10; +// } + +// if (negative) result = "-" + result; +// return result; +// } + +// std::string format_unsigned_decimal(unsigned long long value) noexcept { +// if (value == 0) return "0"; + +// std::string result; +// while (value > 0) { +// result = char('0' + value % 10) + result; +// value /= 10; +// } +// return result; +// } + +// std::string format_hex(unsigned long long value) noexcept { +// if (value == 0) { +// std::string result = "0"; +// if (showbase_) result = "0x" + result; +// return result; +// } + +// std::string result; +// const char* hex_chars = uppercase_ ? "0123456789ABCDEF" : "0123456789abcdef"; + +// while (value > 0) { +// result = hex_chars[value % 16] + result; +// value /= 16; +// } + +// if (showbase_) { +// result = (uppercase_ ? "0X" : "0x") + result; +// } + +// return result; +// } + +// std::string format_oct(unsigned long long value) noexcept { +// if (value == 0) { +// std::string result = "0"; +// if (showbase_) result = "0" + result; +// return result; +// } + +// std::string result; +// while (value > 0) { +// result = char('0' + value % 8) + result; +// value /= 8; +// } + +// if (showbase_) { +// result = "0" + result; +// } + +// return result; +// } + +// void append_float(double value) noexcept { +// std::ostringstream oss; + +// // 精度設定 +// oss.precision(precision_); + +// // フロートモード設定 +// switch (float_mode_) { +// case FloatMode::Fixed: +// oss << std::fixed; +// break; +// case FloatMode::Scientific: +// oss << std::scientific; +// break; +// case FloatMode::Default: +// // デフォルトフォーマット +// break; +// } + +// // 大文字設定 +// if (uppercase_) { +// oss << std::uppercase; +// } + +// oss << value; +// std::string result = oss.str(); +// apply_field_formatting(result); +// } + +// NODEC_DISABLE_COPY(Formatter) +// }; + template class ErrorFormatter { public: @@ -669,7 +690,8 @@ class ErrorFormatter { } operator std::string() noexcept { - stream_ << "\n" << std::dec + stream_ << "\n" + << std::dec << "[File] " << file_ << "\n" << "[Line] " << line_; return stream_.str(); diff --git a/include/nodec/optimized_ostringstream.hpp b/include/nodec/optimized_ostringstream.hpp index c631a93..bdeb29a 100644 --- a/include/nodec/optimized_ostringstream.hpp +++ b/include/nodec/optimized_ostringstream.hpp @@ -1,10 +1,9 @@ #ifndef NODEC__OPTIMIZED_OSTRINGSTREAM_HPP_ #define NODEC__OPTIMIZED_OSTRINGSTREAM_HPP_ -#include -#include #include #include +#include namespace nodec { @@ -17,6 +16,7 @@ class basic_reserved_stringbuf : public std::basic_streambuf { public: using base_type = std::basic_streambuf; using string_type = std::basic_string; + using string_view_type = std::basic_string_view; using size_type = typename string_type::size_type; using int_type = typename Traits::int_type; using char_type = CharT; @@ -25,40 +25,32 @@ class basic_reserved_stringbuf : public std::basic_streambuf { using off_type = typename Traits::off_type; // デフォルトコンストラクタ - basic_reserved_stringbuf() - : mode_(std::ios_base::out), buffer_(), reserved_capacity_(0) { - init_put_area(); - } + basic_reserved_stringbuf() = default; // 事前リザーブ付きコンストラクタ - explicit basic_reserved_stringbuf(size_type reserve_size) - : mode_(std::ios_base::out), buffer_(), reserved_capacity_(reserve_size) { - buffer_.reserve(reserve_size); - init_put_area(); + explicit basic_reserved_stringbuf(size_type reserve_size) { + if (reserve_size > 0) { + ensure_capacity(reserve_size); + } } // 初期文字列 + 事前リザーブ付きコンストラクタ basic_reserved_stringbuf(const string_type &initial_str, size_type reserve_size) - : mode_(std::ios_base::out), buffer_(initial_str), reserved_capacity_(reserve_size) { - ensure_capacity(std::max(reserve_size, initial_str.size())); - init_put_area(); + : buffer_(initial_str) { + if (reserve_size > initial_str.size()) { + ensure_capacity(reserve_size); + } } // ムーブコンストラクタ basic_reserved_stringbuf(basic_reserved_stringbuf &&other) noexcept - : base_type(std::move(other)), mode_(other.mode_), buffer_(std::move(other.buffer_)), - reserved_capacity_(other.reserved_capacity_) { - init_put_area(); - } + : base_type(std::move(other)), buffer_(std::move(other.buffer_)) {} // ムーブ代入演算子 basic_reserved_stringbuf &operator=(basic_reserved_stringbuf &&other) noexcept { if (this != &other) { base_type::operator=(std::move(other)); - mode_ = other.mode_; buffer_ = std::move(other.buffer_); - reserved_capacity_ = other.reserved_capacity_; - init_put_area(); } return *this; } @@ -71,26 +63,33 @@ class basic_reserved_stringbuf : public std::basic_streambuf { return buffer_; } + // 文字列ビュー取得(コピーなし) + string_view_type view() const { + return string_view_type(buffer_.data(), buffer_.size()); + } + // 文字列設定 void str(const string_type &s) { buffer_ = s; - init_put_area(); } void str(string_type &&s) { buffer_ = std::move(s); - init_put_area(); } // 事前リザーブ機能 void reserve(size_type new_capacity) { - reserved_capacity_ = new_capacity; ensure_capacity(new_capacity); } - // 容量・サイズ情報(リザーブされた容量も考慮) + // 内容をクリア(容量は保持) + void clear() { + buffer_.clear(); + } + + // 容量・サイズ情報 size_type capacity() const { - return std::max(buffer_.capacity(), reserved_capacity_); + return buffer_.capacity(); } size_type size() const { @@ -98,107 +97,53 @@ class basic_reserved_stringbuf : public std::basic_streambuf { } protected: - // オーバーフロー処理(高度に最適化) + // より効率的なオーバーフロー処理 int_type overflow(int_type c = Traits::eof()) override { - if (!(mode_ & std::ios_base::out)) { - return Traits::eof(); - } - if (!Traits::eq_int_type(c, Traits::eof())) { - // 文字を直接buffer_に追加 - try { + // バッファに余裕があるかチェック + if (buffer_.size() < buffer_.capacity()) { + buffer_.push_back(Traits::to_char_type(c)); + } else { + // 容量不足時は少し多めに確保 + buffer_.reserve(buffer_.capacity() == 0 ? 16 : buffer_.capacity() * 2); buffer_.push_back(Traits::to_char_type(c)); - return c; - } catch (...) { - return Traits::eof(); } + return c; } return Traits::not_eof(c); } - // バルク書き込み最適化 + // バルク書き込み最適化(より積極的な事前確保) std::streamsize xsputn(const char_type *s, std::streamsize count) override { - if (!(mode_ & std::ios_base::out) || count <= 0) { + if (count <= 0) { return 0; } - - try { - buffer_.append(s, static_cast(count)); - return count; - } catch (...) { - return 0; + + // 必要な容量を事前に確保 + size_type needed = buffer_.size() + static_cast(count); + if (needed > buffer_.capacity()) { + buffer_.reserve(needed); } + + buffer_.append(s, static_cast(count)); + return count; } - // 同期処理 + // 同期処理(何もしない - 直接書き込み済み) int sync() override { - // 既にbuffer_に直接書き込んでいるので何もしない return 0; } - // シーク処理 - pos_type seekoff(off_type off, std::ios_base::seekdir way, - std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override { - if (!(mode_ & which)) { - return pos_type(off_type(-1)); - } - - off_type newoff; - switch (way) { - case std::ios_base::beg: - newoff = off; - break; - case std::ios_base::cur: - newoff = static_cast(buffer_.size()) + off; - break; - case std::ios_base::end: - newoff = static_cast(buffer_.size()) + off; - break; - default: - return pos_type(off_type(-1)); - } - - if (newoff < 0 || static_cast(newoff) > buffer_.size()) { - return pos_type(off_type(-1)); - } - - buffer_.resize(static_cast(newoff)); - return pos_type(newoff); - } - - pos_type seekpos(pos_type pos, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override { - return seekoff(off_type(pos), std::ios_base::beg, which); - } - private: - std::ios_base::openmode mode_; string_type buffer_; - size_type reserved_capacity_; - // 容量確保の内部メソッド(SSOを考慮) + // 効率的な容量確保(シンプル版) void ensure_capacity(size_type capacity) { - if (capacity > 0) { - // 実際の容量を確保(SSOを回避) - if (capacity > 15) { // SSOの閾値を超える場合 - string_type temp(capacity, CharT{}); - temp.clear(); - if (temp.capacity() >= capacity) { - // 既存のコンテンツをコピー - temp = buffer_; - buffer_ = std::move(temp); - } - } + if (capacity > buffer_.capacity()) { + buffer_.reserve(capacity); } } - // Put areaの初期化(ダミー - 実際の書き込みはoverflow/xsputnで処理) - void init_put_area() { - // streambufの要求を満たすためのダミー設定 - // 実際の書き込みはoverflowとxsputnで直接buffer_に行う - char_type dummy{}; - base_type::setp(&dummy, &dummy); - } - // コピーは禁止、ムーブのみ許可 basic_reserved_stringbuf(const basic_reserved_stringbuf &) = delete; basic_reserved_stringbuf &operator=(const basic_reserved_stringbuf &) = delete; @@ -216,6 +161,7 @@ class basic_optimized_ostringstream : public std::basic_ostream { public: using base_type = std::basic_ostream; using string_type = std::basic_string; + using string_view_type = std::basic_string_view; using size_type = typename string_type::size_type; using stringbuf_type = basic_reserved_stringbuf; @@ -264,6 +210,11 @@ class basic_optimized_ostringstream : public std::basic_ostream { return buffer_.str(); } + // 文字列ビュー取得(コピーなし - 高性能) + string_view_type view() const { + return buffer_.view(); + } + // 文字列設定 void str(const string_type &s) { buffer_.str(s); @@ -287,9 +238,9 @@ class basic_optimized_ostringstream : public std::basic_ostream { return buffer_.size(); } - // clear機能(内容をクリアして再利用) + // clear機能(内容をクリアして再利用 - 容量は保持) void clear_content() { - buffer_.str(string_type()); + buffer_.clear(); // stringbufの内容をクリア(容量保持) base_type::clear(); // ストリームの状態もクリア } diff --git a/overflow_xsputn_timing_analysis.md b/overflow_xsputn_timing_analysis.md new file mode 100644 index 0000000..8f44598 --- /dev/null +++ b/overflow_xsputn_timing_analysis.md @@ -0,0 +1,189 @@ +# C++ ostream の overflow() と xsputn() 関数呼び出しタイミング解説 + +## 🎯 重要な発見:実行結果からの分析 + +実際のテスト結果から、以下の重要なパターンが明確に分かりました: + +## 📞 関数呼び出しのタイミング + +### 1. `overflow()` - 単一文字の処理 +**呼び出される場面:** +- **単一文字**の書き込み(`stream << 'A'`) +- **数値の各桁**の書き込み(`stream << 123` → '1', '2', '3' それぞれに呼び出し) +- **マニピュレータによる空白文字**の書き込み(`std::setw(10)`による空白) + +```cpp +// 例:これらの場合にoverflow()が呼ばれる +stream << 'A'; // 1回のoverflow()呼び出し +stream << 123; // 3回のoverflow()呼び出し('1', '2', '3') +stream << 3.14; // 4回のoverflow()呼び出し('3', '.', '1', '4') +``` + +**特徴:** +- 1文字ずつの処理なので**効率が悪い** +- バッファサイズが0のままなのは、**各文字が個別のstreamインスタンス**で処理されているため + +### 2. `xsputn()` - バルク文字列の処理 +**呼び出される場面:** +- **文字列リテラル**の書き込み(`stream << "Hello"`) +- **長い文字列**の一括書き込み +- **std::string**の書き込み + +```cpp +// 例:これらの場合にxsputn()が呼ばれる +stream << "Hello"; // 1回のxsputn(5文字) +stream << some_string; // 1回のxsputn(文字列長) +``` + +**特徴:** +- **効率的**:複数文字を一度に処理 +- **自動的に容量確保**:必要に応じてバッファを拡張 + +## 📊 パフォーマンスの違い + +### 💥 非効率的なパターン(overflow()多発) +``` +📞 overflow() called with char: 'A' | Buffer size: 0 | Capacity: 15 +📞 overflow() called with char: 'B' | Buffer size: 0 | Capacity: 15 +📞 overflow() called with char: 'C' | Buffer size: 0 | Capacity: 15 +...(1000回の個別呼び出し) +``` +- **1000文字で1000回の関数呼び出し** +- 各文字ごとにオーバーヘッド発生 + +### ⚡ 効率的なパターン(xsputn()使用) +``` +📞 xsputn() called with 1000 chars: "ABCDEFGHIJKLMNOPQRST..." + | Buffer size before: 0 | Capacity: 15 + → After xsputn: Buffer size: 1000 | Capacity: 1007 +``` +- **1000文字で1回の関数呼び出し** +- 一括処理で大幅な性能向上 + +## 🔧 実装における重要な洞察 + +### 現在のoptimized_ostringstreamの実装分析 + +```cpp +// overflow() - 単一文字処理 +int_type overflow(int_type c = Traits::eof()) override { + if (!Traits::eq_int_type(c, Traits::eof())) { + if (buffer_.size() < buffer_.capacity()) { + buffer_.push_back(Traits::to_char_type(c)); + } else { + // 容量不足時は倍に拡張 + buffer_.reserve(buffer_.capacity() == 0 ? 16 : buffer_.capacity() * 2); + buffer_.push_back(Traits::to_char_type(c)); + } + return c; + } + return Traits::not_eof(c); +} + +// xsputn() - バルク文字列処理 +std::streamsize xsputn(const char_type *s, std::streamsize count) override { + if (count <= 0) return 0; + + // 必要な容量を事前に確保 + size_type needed = buffer_.size() + static_cast(count); + if (needed > buffer_.capacity()) { + buffer_.reserve(needed); + } + + buffer_.append(s, static_cast(count)); + return count; +} +``` + +## 🎭 特異なケースの解説 + +### 数値書き込みの挙動 +実行結果で確認できたように: +```cpp +stream << 123; // overflow()が3回呼ばれる +``` + +これは、数値が内部で以下のように処理されるためです: +1. `123` → 文字列 `"123"` に変換 +2. **しかし1文字ずつoverflow()で処理**される(実装依存) + +### マニピュレータとの組み合わせ +```cpp +stream << std::setw(10) << "test"; +``` +この場合: +1. 幅設定により**6個の空白文字**が`overflow()`で処理 +2. `"test"`が`xsputn()`で処理 + +## 🚀 最適化への提案 + +### 1. バッファリング戦略の改善 +```cpp +// 数値処理の最適化案 +template +auto operator<<(T&& value) -> decltype(*this) { + if constexpr (std::is_arithmetic_v>) { + // 数値を一時バッファで文字列化してからxsputn()を使用 + thread_local std::array temp_buffer; + auto result = std::to_chars(temp_buffer.data(), + temp_buffer.data() + temp_buffer.size(), + value); + this->rdbuf()->xsputn(temp_buffer.data(), result.ptr - temp_buffer.data()); + return *this; + } else { + return base_type::operator<<(std::forward(value)); + } +} +``` + +### 2. スマートなバッファ拡張 +```cpp +void smart_reserve(size_type additional_size) { + size_type current_size = buffer_.size(); + size_type needed = current_size + additional_size; + + if (needed > buffer_.capacity()) { + // より効率的な拡張戦略 + size_type new_capacity = std::max(needed, buffer_.capacity() * 2); + buffer_.reserve(new_capacity); + } +} +``` + +## 📈 実用的な使い方の指針 + +### ✅ 効率的な書き込みパターン +```cpp +// 良い例:文字列を一括処理 +stream << "Long string content"; + +// 良い例:事前に文字列を構築 +std::string result = "Value: " + std::to_string(value) + " units"; +stream << result; + +// 良い例:StringBuilder パターン +std::ostringstream temp; +temp << "Multiple" << " " << "parts"; +stream << temp.str(); // 一括で書き込み +``` + +### ❌ 避けるべきパターン +```cpp +// 悪い例:文字を個別に書き込み +for (char c : "Hello") { + stream << c; // overflow()が5回呼ばれる +} + +// 悪い例:細かい分割 +stream << "A" << "B" << "C"; // xsputn()が3回呼ばれる +``` + +## 🎯 まとめ + +1. **文字列は可能な限りまとめて書き込む** +2. **数値は内部で1文字ずつ処理される**ことを理解する +3. **xsputn()の活用**が性能向上の鍵 +4. **事前バッファ確保**でメモリ再配置を最小化 +5. **マニピュレータ使用時**の空白文字処理コストを考慮 + +この理解により、より効率的なstreamバッファ実装と使用方法を選択できるようになります。 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6bc4585..fced087 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,14 +15,14 @@ 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__formatter" formatter/formatter.cpp) add_basic_test("nodec__formatter_benchmark" formatter/formatter_benchmark.cpp) -add_basic_test("nodec__formatter_detailed_benchmark" formatter/formatter_detailed_benchmark.cpp) -add_basic_test("nodec__formatter_vector3_benchmark" formatter/formatter_vector3_benchmark.cpp) -add_basic_test("nodec__formatter_ostringstream_compat" formatter/formatter_ostringstream_compat.cpp) +# add_basic_test("nodec__formatter_detailed_benchmark" formatter/formatter_detailed_benchmark.cpp) +# add_basic_test("nodec__formatter_vector3_benchmark" formatter/formatter_vector3_benchmark.cpp) +# add_basic_test("nodec__formatter_ostringstream_compat" formatter/formatter_ostringstream_compat.cpp) add_basic_test("nodec__ostringstream_verification" formatter/ostringstream_verification.cpp) -add_basic_test("nodec__hex_debug" formatter/hex_debug.cpp) -add_basic_test("nodec__complex_format_debug" formatter/complex_format_debug.cpp) +# add_basic_test("nodec__hex_debug" formatter/hex_debug.cpp) +# add_basic_test("nodec__complex_format_debug" formatter/complex_format_debug.cpp) add_basic_test("nodec__optimized_ostringstream_test" formatter/optimized_ostringstream_test.cpp) add_basic_test("nodec__gfx" gfx/gfx.cpp) add_basic_test("nodec__logging__logging" logging/logging.cpp) @@ -42,17 +42,32 @@ add_basic_test("nodec__unicode" unicode/unicode.cpp) add_basic_test("nodec__utility" utility/utility.cpp) add_basic_test("nodec__vector" vector/vector.cpp) -# Debug test for optimized ostringstream -add_basic_test("nodec__debug_test" formatter/debug_test.cpp) +# # Debug test for optimized ostringstream +# add_basic_test("nodec__debug_test" formatter/debug_test.cpp) -# Constructor debug test -add_basic_test("nodec__constructor_debug" formatter/constructor_debug.cpp) +# # Constructor debug test +# add_basic_test("nodec__constructor_debug" formatter/constructor_debug.cpp) -# OpenMode examples test -add_basic_test("nodec__openmode_examples" formatter/openmode_examples.cpp) +# # OpenMode examples test +# add_basic_test("nodec__openmode_examples" formatter/openmode_examples.cpp) -# Vector3 debug test -add_basic_test("nodec__vector3_debug" formatter/vector3_debug.cpp) +# # Vector3 debug test +# add_basic_test("nodec__vector3_debug" formatter/vector3_debug.cpp) + +# # View analysis test +# add_basic_test("nodec__view_analysis" formatter/view_analysis.cpp) + +# # Smart formatter analysis test +# add_basic_test("nodec__smart_formatter_analysis" formatter/smart_formatter_analysis.cpp) + +# # Overflow and xsputn timing analysis +# add_basic_test("nodec__overflow_xsputn_timing" formatter/overflow_xsputn_timing.cpp) + +# # Traits and EOF analysis +# add_basic_test("nodec__traits_eof_analysis" formatter/traits_eof_analysis.cpp) + +# # EOF necessity test +# add_basic_test("nodec__eof_necessity_test" formatter/eof_necessity_test.cpp) target_compile_definitions( nodec__unicode PRIVATE diff --git a/tests/formatter/eof_necessity_test.cpp b/tests/formatter/eof_necessity_test.cpp new file mode 100644 index 0000000..e136c22 --- /dev/null +++ b/tests/formatter/eof_necessity_test.cpp @@ -0,0 +1,245 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +#include +#include +#include +#include + +// EOFチェックを省略したバージョン +template> +class no_eof_check_stringbuf : public std::basic_streambuf { +public: + using base_type = std::basic_streambuf; + using string_type = std::basic_string; + using size_type = typename string_type::size_type; + using int_type = typename Traits::int_type; + using char_type = CharT; + + // EOFチェックを完全に省略したoverflow() + int_type overflow(int_type c = Traits::eof()) override { + // 警告:EOFチェックなし! + if (buffer_.size() < buffer_.capacity()) { + buffer_.push_back(Traits::to_char_type(c)); + } else { + buffer_.reserve(buffer_.capacity() == 0 ? 16 : buffer_.capacity() * 2); + buffer_.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; + size_type needed = buffer_.size() + static_cast(count); + if (needed > buffer_.capacity()) { + buffer_.reserve(needed); + } + buffer_.append(s, static_cast(count)); + return count; + } + + string_type str() const { return buffer_; } + void clear() { buffer_.clear(); } + +private: + string_type buffer_; +}; + +// EOFチェックありとなしの統計を取るバージョン +template> +class statistics_stringbuf : public std::basic_streambuf { +public: + using base_type = std::basic_streambuf; + using string_type = std::basic_string; + using size_type = typename string_type::size_type; + using int_type = typename Traits::int_type; + using char_type = CharT; + + int_type overflow(int_type c = Traits::eof()) override { + overflow_calls_++; + + if (!Traits::eq_int_type(c, Traits::eof())) { + valid_char_calls_++; + if (buffer_.size() < buffer_.capacity()) { + buffer_.push_back(Traits::to_char_type(c)); + } else { + buffer_.reserve(buffer_.capacity() == 0 ? 16 : buffer_.capacity() * 2); + buffer_.push_back(Traits::to_char_type(c)); + } + return c; + } else { + eof_calls_++; + return Traits::not_eof(c); + } + } + + std::streamsize xsputn(const char_type *s, std::streamsize count) override { + xsputn_calls_++; + if (count <= 0) return 0; + size_type needed = buffer_.size() + static_cast(count); + if (needed > buffer_.capacity()) { + buffer_.reserve(needed); + } + buffer_.append(s, static_cast(count)); + return count; + } + + string_type str() const { return buffer_; } + + void print_statistics() const { + std::cout << "=== 統計情報 ===" << std::endl; + std::cout << "overflow() 総呼び出し回数: " << overflow_calls_ << std::endl; + std::cout << " - 有効な文字: " << valid_char_calls_ << std::endl; + std::cout << " - EOF: " << eof_calls_ << std::endl; + std::cout << "xsputn() 呼び出し回数: " << xsputn_calls_ << std::endl; + } + + void reset_statistics() { + overflow_calls_ = valid_char_calls_ = eof_calls_ = xsputn_calls_ = 0; + } + +private: + string_type buffer_; + mutable size_t overflow_calls_ = 0; + mutable size_t valid_char_calls_ = 0; + mutable size_t eof_calls_ = 0; + mutable size_t xsputn_calls_ = 0; +}; + +TEST_CASE("EOFチェックの必要性を検証") { + std::cout << "\n=== EOFチェックの必要性検証 ===" << std::endl; + + statistics_stringbuf stats_buf; + std::ostream stats_stream(&stats_buf); + + std::cout << "\n1. 通常の文字列操作テスト:" << std::endl; + stats_buf.reset_statistics(); + + stats_stream << "Hello"; + stats_stream << " "; + stats_stream << "World"; + stats_stream << 123; + stats_stream << 3.14; + + stats_buf.print_statistics(); + std::cout << "結果: \"" << stats_buf.str() << "\"" << std::endl; + + std::cout << "\n2. 大量文字処理テスト:" << std::endl; + stats_buf.reset_statistics(); + + for (int i = 0; i < 1000; ++i) { + stats_stream << static_cast('A' + (i % 26)); + } + + stats_buf.print_statistics(); + + std::cout << "\n3. 混合処理テスト:" << std::endl; + stats_buf.reset_statistics(); + + for (int i = 0; i < 100; ++i) { + stats_stream << "Item" << i << ": " << (i * 1.5) << " "; + } + + stats_buf.print_statistics(); +} + +TEST_CASE("EOFチェックなしでの動作確認") { + std::cout << "\n=== EOFチェック省略版の動作確認 ===" << std::endl; + + try { + no_eof_check_stringbuf no_check_buf; + std::ostream no_check_stream(&no_check_buf); + + std::cout << "EOFチェックなしでの書き込みテスト:" << std::endl; + + no_check_stream << "Test"; + no_check_stream << 123; + no_check_stream << " works!"; + + std::cout << "結果: \"" << no_check_buf.str() << "\"" << std::endl; + std::cout << "→ 正常に動作(EOFは渡されていない)" << std::endl; + + } catch (const std::exception& e) { + std::cout << "例外発生: " << e.what() << std::endl; + } +} + +TEST_CASE("手動でEOFを渡した場合の動作") { + std::cout << "\n=== 手動EOF送信テスト ===" << std::endl; + + statistics_stringbuf stats_buf; + + std::cout << "1. EOFチェックありの場合:" << std::endl; + auto eof_val = std::char_traits::eof(); + auto result_with_check = stats_buf.overflow(eof_val); + stats_buf.print_statistics(); + std::cout << "EOF処理結果: " << result_with_check << std::endl; + + std::cout << "\n2. 通常文字との比較:" << std::endl; + stats_buf.reset_statistics(); + auto normal_result = stats_buf.overflow(std::char_traits::to_int_type('A')); + stats_buf.print_statistics(); + std::cout << "通常文字処理結果: " << normal_result << std::endl; + std::cout << "バッファ内容: \"" << stats_buf.str() << "\"" << std::endl; +} + +TEST_CASE("C++標準準拠とパフォーマンスの比較") { + std::cout << "\n=== 標準準拠 vs パフォーマンス ===" << std::endl; + + const int iterations = 100000; + + // EOFチェックありのバージョン + { + auto start = std::chrono::high_resolution_clock::now(); + + nodec::optimized_ostringstream oss_with_check; + for (int i = 0; i < iterations; ++i) { + oss_with_check << static_cast('A' + (i % 26)); + } + + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - start); + + std::cout << "EOFチェックあり: " << duration.count() << " μs" << std::endl; + std::cout << "結果長: " << oss_with_check.size() << std::endl; + } + + // EOFチェックなしのバージョン + { + auto start = std::chrono::high_resolution_clock::now(); + + no_eof_check_stringbuf buf_no_check; + std::ostream stream_no_check(&buf_no_check); + + for (int i = 0; i < iterations; ++i) { + stream_no_check << static_cast('A' + (i % 26)); + } + + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - start); + + std::cout << "EOFチェックなし: " << duration.count() << " μs" << std::endl; + std::cout << "結果長: " << buf_no_check.str().size() << std::endl; + } +} + +TEST_CASE("実用的な推奨事項") { + std::cout << "\n=== 実用的な推奨事項 ===" << std::endl; + + std::cout << "1. C++標準への準拠:" << std::endl; + std::cout << " - overflow()の標準的な実装ではEOFチェックが期待される" << std::endl; + std::cout << " - 他のstreambuf実装との互換性確保" << std::endl; + + std::cout << "\n2. 防御的プログラミング:" << std::endl; + std::cout << " - 予期しないEOF値から保護" << std::endl; + std::cout << " - 将来の仕様変更への対応" << std::endl; + + std::cout << "\n3. パフォーマンスへの影響:" << std::endl; + std::cout << " - 1回の比較操作による影響は微小" << std::endl; + std::cout << " - 現代のCPUでは分岐予測により最適化される" << std::endl; + + std::cout << "\n4. 推奨:" << std::endl; + std::cout << " EOFチェックは『保持する』ことを推奨" << std::endl; + std::cout << " 理由: 安全性 > 微小なパフォーマンス差" << std::endl; + + REQUIRE(true); +} diff --git a/tests/formatter/formatter_benchmark.cpp b/tests/formatter/formatter_benchmark.cpp index c32a1d0..291d8e8 100644 --- a/tests/formatter/formatter_benchmark.cpp +++ b/tests/formatter/formatter_benchmark.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -70,6 +71,19 @@ TEST_CASE("Benchmark - Simple Concatenation") { } } + // FastFormatter view() - コピーなし + { + BenchmarkTimer timer("FastFormatter view() - Simple concatenation"); + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream oss; + oss << "Hello" << " " << "World" << i; + std::string_view result = oss.view(); // No copy + // 結果を使用して最適化を防ぐ + volatile auto len = result.length(); + (void)len; + } + } + // std::string + 演算子のベンチマーク(比較用) { BenchmarkTimer timer("std::string + operator - Simple concatenation"); @@ -114,6 +128,18 @@ TEST_CASE("Benchmark - Numeric Formatting") { } } + // FastFormatter view() - コピーなし + { + BenchmarkTimer timer("FastFormatter view() - Numeric formatting"); + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream oss; + oss << "Number: " << i << ", Float: " << (i * 3.14) << ", Hex: " << std::hex << i; + std::string_view result = oss.view(); // No copy + volatile auto len = result.length(); + (void)len; + } + } + // std::ostringstreamのベンチマーク(比較用) { BenchmarkTimer timer("std::ostringstream - Numeric formatting"); @@ -169,6 +195,24 @@ TEST_CASE("Benchmark - Long Chain") { } } + // FastFormatter view() - コピーなし + { + BenchmarkTimer timer("FastFormatter view() - Long chain"); + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream oss; + oss << "Start: " << i + << ", Value1: " << (i * 2) + << ", Value2: " << (i * 3.5) + << ", Value3: " << (i % 100) + << ", Value4: " << std::hex << i + << ", Value5: " << std::dec << (i + 1000) + << ", End"; + std::string_view result = oss.view(); // No copy + volatile auto len = result.length(); + (void)len; + } + } + // std::ostringstreamのベンチマーク(比較用) { BenchmarkTimer timer("std::ostringstream - Long chain"); @@ -224,7 +268,8 @@ TEST_CASE("Benchmark - Memory Usage") { // 結果を使用して最適化を防ぐ volatile auto total_size = results.size(); - (void)total_size; } + (void)total_size; + } // nodec::optimized_ostringstream (FastFormatter)のベンチマーク { @@ -242,6 +287,30 @@ TEST_CASE("Benchmark - Memory Usage") { volatile auto total_size = results.size(); (void)total_size; } + + // FastFormatter view() - string_viewのベクタ(参考用) + { + BenchmarkTimer timer("FastFormatter view() - Multiple instances (view only)"); + std::vector streams; + streams.reserve(iterations); + + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream oss; + oss << "Test " << i << " with value " << (i * 2.5); + streams.push_back(std::move(oss)); + } + + // string_viewとして結果を取得(実際の使用例) + for (const auto& oss : streams) { + std::string_view result = oss.view(); + volatile auto len = result.length(); + (void)len; + } + + // 結果を使用して最適化を防ぐ + volatile auto total_size = streams.size(); + (void)total_size; + } { BenchmarkTimer timer("std::ostringstream - Multiple instances"); @@ -285,6 +354,21 @@ TEST_CASE("Benchmark - Reserve Capacity") { } } + // FastFormatter with reserve + view() + { + BenchmarkTimer timer("FastFormatter view() with reserve - Long text"); + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream oss(256); // Pre-reserve capacity + oss << "This is a longer text example that will test the reserve functionality: " + << i << ", value1: " << (i * 2) << ", value2: " << (i * 3.14) + << ", value3: " << std::hex << i << ", value4: " << std::dec << (i + 100) + << " - End of the long text"; + std::string_view result = oss.view(); // No copy + volatile auto len = result.length(); + (void)len; + } + } + // FastFormatter without reserve (for comparison) { BenchmarkTimer timer("FastFormatter without reserve - Long text"); @@ -300,6 +384,21 @@ TEST_CASE("Benchmark - Reserve Capacity") { } } + // FastFormatter without reserve + view() + { + BenchmarkTimer timer("FastFormatter view() without reserve - Long text"); + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream oss; // No pre-reserve + oss << "This is a longer text example that will test the reserve functionality: " + << i << ", value1: " << (i * 2) << ", value2: " << (i * 3.14) + << ", value3: " << std::hex << i << ", value4: " << std::dec << (i + 100) + << " - End of the long text"; + std::string_view result = oss.view(); // No copy + volatile auto len = result.length(); + (void)len; + } + } + // std::ostringstream (for comparison) { BenchmarkTimer timer("std::ostringstream - Long text"); @@ -318,3 +417,66 @@ TEST_CASE("Benchmark - Reserve Capacity") { // テストが正常に実行されたことを確認 CHECK(true); } + +TEST_CASE("Benchmark - String View Performance") { + const int iterations = 100000; + + std::cout << "\n--- String View Performance Test ---" << std::endl; + + // FastFormatter view() vs str() comparison + { + BenchmarkTimer timer("FastFormatter view() - No copy"); + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream oss; + oss << "Test string " << i << " with some content"; + std::string_view result = oss.view(); // No copy + // 結果を使用して最適化を防ぐ + volatile auto len = result.length(); + (void)len; + } + } + + { + BenchmarkTimer timer("FastFormatter str() - With copy"); + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream oss; + oss << "Test string " << i << " with some content"; + std::string result = oss.str(); // Copy + // 結果を使用して最適化を防ぐ + volatile auto len = result.length(); + (void)len; + } + } + + // Long string performance comparison + { + BenchmarkTimer timer("FastFormatter view() - Long strings"); + for (int i = 0; i < iterations / 10; ++i) { + nodec::optimized_ostringstream oss(1024); + oss << "This is a much longer test string that contains more data to demonstrate " + << "the performance benefits of using string_view instead of copying the entire string. " + << "Iteration number: " << i << ", additional data: " << (i * 3.14159) + << ", more content to make this string significantly longer and test memory efficiency."; + std::string_view result = oss.view(); // No copy + volatile auto len = result.length(); + (void)len; + } + } + + { + BenchmarkTimer timer("FastFormatter str() - Long strings"); + for (int i = 0; i < iterations / 10; ++i) { + nodec::optimized_ostringstream oss(1024); + oss << "This is a much longer test string that contains more data to demonstrate " + << "the performance benefits of using string_view instead of copying the entire string. " + << "Iteration number: " << i << ", additional data: " << (i * 3.14159) + << ", more content to make this string significantly longer and test memory efficiency."; + std::string result = oss.str(); // Copy + volatile auto len = result.length(); + (void)len; + } + } + + // テストが正常に実行されたことを確認 + CHECK(true); +} diff --git a/tests/formatter/overflow_xsputn_timing.cpp b/tests/formatter/overflow_xsputn_timing.cpp new file mode 100644 index 0000000..4c3f603 --- /dev/null +++ b/tests/formatter/overflow_xsputn_timing.cpp @@ -0,0 +1,217 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +#include +#include +#include +#include +#include + +// デバッグ用のstreambuf - 関数呼び出しを追跡 +template> +class debug_stringbuf : public nodec::basic_reserved_stringbuf { +public: + using base_type = nodec::basic_reserved_stringbuf; + using int_type = typename Traits::int_type; + using char_type = CharT; + using string_type = std::basic_string; + using size_type = typename string_type::size_type; + + debug_stringbuf() = default; + explicit debug_stringbuf(size_type reserve_size) : base_type(reserve_size) {} + + // overflow()呼び出しを追跡 + int_type overflow(int_type c = Traits::eof()) override { + std::cout << "📞 overflow() called with char: "; + if (!Traits::eq_int_type(c, Traits::eof())) { + char ch = Traits::to_char_type(c); + if (ch >= 32 && ch <= 126) { // 印刷可能文字 + std::cout << "'" << ch << "'"; + } else { + std::cout << "(non-printable:" << static_cast(ch) << ")"; + } + } else { + std::cout << "EOF"; + } + std::cout << " | Buffer size: " << this->size() + << " | Capacity: " << this->capacity() << std::endl; + + return base_type::overflow(c); + } + + // xsputn()呼び出しを追跡 + std::streamsize xsputn(const char_type *s, std::streamsize count) override { + std::cout << "📞 xsputn() called with " << count << " chars: \""; + // 最初の20文字まで表示 + for (std::streamsize i = 0; i < std::min(count, std::streamsize(20)); ++i) { + char ch = s[i]; + if (ch >= 32 && ch <= 126) { + std::cout << ch; + } else { + std::cout << "\\x" << std::hex << static_cast(ch) << std::dec; + } + } + if (count > 20) std::cout << "..."; + std::cout << "\" | Buffer size before: " << this->size() + << " | Capacity: " << this->capacity() << std::endl; + + auto result = base_type::xsputn(s, count); + + std::cout << " → After xsputn: Buffer size: " << this->size() + << " | Capacity: " << this->capacity() << std::endl; + + return result; + } + + // sync()呼び出しを追跡 + int sync() override { + std::cout << "📞 sync() called | Buffer size: " << this->size() << std::endl; + return base_type::sync(); + } +}; + +// デバッグ用のostringstream +template> +class debug_ostringstream : public std::basic_ostream { +public: + using base_type = std::basic_ostream; + using stringbuf_type = debug_stringbuf; + using string_type = std::basic_string; + using size_type = typename string_type::size_type; + + debug_ostringstream() : base_type(&buffer_), buffer_() {} + explicit debug_ostringstream(size_type reserve_size) + : base_type(&buffer_), buffer_(reserve_size) {} + + stringbuf_type* rdbuf() const { + return const_cast(&buffer_); + } + + string_type str() const { + return buffer_.str(); + } + +private: + stringbuf_type buffer_; +}; + +TEST_CASE("overflow() と xsputn() の呼び出しタイミング解説") { + std::cout << "\n=== overflow() と xsputn() 呼び出しタイミングの詳細解説 ===" << std::endl; + + std::cout << "\n【1. 単一文字の書き込み - overflow()が呼ばれる】" << std::endl; + { + debug_ostringstream dss; + std::cout << "初期状態 - Buffer size: " << dss.rdbuf()->size() + << ", Capacity: " << dss.rdbuf()->capacity() << std::endl; + + std::cout << "\n単一文字 'A' を書き込み:" << std::endl; + dss << 'A'; // 単一文字 → overflow()呼び出し + + std::cout << "\n単一文字 'B' を書き込み:" << std::endl; + dss << 'B'; // 単一文字 → overflow()呼び出し + } + + std::cout << "\n【2. 文字列の書き込み - xsputn()が呼ばれる】" << std::endl; + { + debug_ostringstream dss; + std::cout << "\n文字列 \"Hello\" を書き込み:" << std::endl; + dss << "Hello"; // 文字列 → xsputn()呼び出し + + std::cout << "\n文字列 \" World\" を書き込み:" << std::endl; + dss << " World"; // 文字列 → xsputn()呼び出し + } + + std::cout << "\n【3. 数値の書き込み - 内部で文字列変換後にxsputn()】" << std::endl; + { + debug_ostringstream dss; + std::cout << "\n整数 123 を書き込み:" << std::endl; + dss << 123; // 数値 → 内部でstr変換 → xsputn()呼び出し + + std::cout << "\n浮動小数点 3.14 を書き込み:" << std::endl; + dss << 3.14; // 浮動小数点 → 内部でstr変換 → xsputn()呼び出し + } + + std::cout << "\n【4. マニピュレータとの組み合わせ】" << std::endl; + { + debug_ostringstream dss; + std::cout << "\nstd::hex マニピュレータと数値:" << std::endl; + dss << std::hex << 255; // マニピュレータ設定後、数値変換 → xsputn() + + std::cout << "\nstd::setw(10) と文字列:" << std::endl; + dss << std::setw(10) << "test"; // 幅設定後、文字列 → xsputn() + } + + std::cout << "\n【5. バッファ容量不足時の動作】" << std::endl; + { + debug_ostringstream dss(5); // 小さな初期容量 + std::cout << "\n初期容量5で開始、長い文字列を書き込み:" << std::endl; + dss << "This is a very long string that exceeds initial capacity"; + // 容量不足 → xsputn()内でreserve()が呼ばれる + } + + std::cout << "\n【6. flush() と sync() の関係】" << std::endl; + { + debug_ostringstream dss; + dss << "Some content"; + std::cout << "\nflush() を明示的に呼び出し:" << std::endl; + dss.flush(); // flush() → sync()呼び出し + } + + REQUIRE(true); +} + +TEST_CASE("std::ostringstreamとの比較") { + std::cout << "\n=== std::ostringstream vs debug_ostringstream 動作比較 ===" << std::endl; + + std::cout << "\n【通常のstd::ostringstream】" << std::endl; + { + std::ostringstream oss; + oss << "Hello" << " " << 123 << " " << 'X'; + std::cout << "結果: \"" << oss.str() << "\"" << std::endl; + std::cout << "※内部関数呼び出しは見えない" << std::endl; + } + + std::cout << "\n【debug_ostringstream(関数呼び出し可視化)】" << std::endl; + { + debug_ostringstream dss; + dss << "Hello" << " " << 123 << " " << 'X'; + std::cout << "結果: \"" << dss.str() << "\"" << std::endl; + std::cout << "※上記のように内部関数呼び出しが見える" << std::endl; + } + + REQUIRE(true); +} + +TEST_CASE("パフォーマンス影響の実演") { + std::cout << "\n=== パフォーマンス影響の実演 ===" << std::endl; + + const int iterations = 1000; + + std::cout << "\n【文字を1個ずつ書き込み - overflow()多発】" << std::endl; + { + debug_ostringstream dss; + std::cout << "最初の5回の文字書き込み:" << std::endl; + for (int i = 0; i < 5; ++i) { + dss << static_cast('A' + i); + } + std::cout << "... (残り" << (iterations - 5) << "回は省略)" << std::endl; + + // 残りは静かに実行 + for (int i = 5; i < iterations; ++i) { + debug_ostringstream quiet_dss; + quiet_dss << static_cast('A' + (i % 26)); + } + } + + std::cout << "\n【文字列をまとめて書き込み - xsputn()効率的】" << std::endl; + { + debug_ostringstream dss; + std::cout << "1回の文字列書き込み:" << std::endl; + std::string long_str; + for (int i = 0; i < iterations; ++i) { + long_str += static_cast('A' + (i % 26)); + } + dss << long_str; // 一度にまとめて書き込み + } + + REQUIRE(true); +} diff --git a/tests/formatter/smart_formatter_analysis.cpp b/tests/formatter/smart_formatter_analysis.cpp new file mode 100644 index 0000000..1773247 --- /dev/null +++ b/tests/formatter/smart_formatter_analysis.cpp @@ -0,0 +1,220 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +#include +#include +#include +#include +#include + +using namespace std::chrono; + +// 改良版: 文字列長に応じて自動選択するバージョン +template> +class smart_optimized_ostringstream : public nodec::basic_optimized_ostringstream { +public: + using base_type = nodec::basic_optimized_ostringstream; + using string_type = typename base_type::string_type; + using string_view_type = typename base_type::string_view_type; + + // 基底クラスのコンストラクタを継承 + using base_type::base_type; + + // 文字列長に応じて最適な方法を自動選択 + auto get_optimal() const { + const auto size = this->rdbuf()->view().size(); + if (size > 100) { // 閾値: 100文字 + return this->view(); + } else { + return string_view_type(this->str()); // str()をstring_viewでラップ + } + } + + // コンパイル時選択版 + template + auto get_string() const { + if constexpr (PreferView) { + return this->view(); + } else { + return this->str(); + } + } +}; + +using SmartFormatter = smart_optimized_ostringstream<>; + +TEST_CASE("Smart Formatter Performance Test") { + const int iterations = 100000; + + std::cout << "=== Smart Formatter Analysis ===" << std::endl; + + // 短い文字列テスト + { + auto start = high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + SmartFormatter sf; + sf << "Test" << i; + auto result = sf.get_optimal(); // 自動選択 + (void)result.size(); // 使用することで最適化を防ぐ + } + auto end = high_resolution_clock::now(); + auto duration = duration_cast(end - start); + std::cout << "Smart Formatter (short strings): " << duration.count() << " microseconds" << std::endl; + } + + // 長い文字列テスト + { + auto start = high_resolution_clock::now(); + for (int i = 0; i < iterations / 10; ++i) { // 回数を減らして調整 + SmartFormatter sf; + sf << "This is a very long string that exceeds the threshold of 100 characters. "; + sf << "It contains multiple parts to make it definitely longer than the threshold. "; + sf << "Test number: " << i << " with additional padding to ensure length."; + auto result = sf.get_optimal(); // 自動選択 + (void)result.size(); + } + auto end = high_resolution_clock::now(); + auto duration = duration_cast(end - start); + std::cout << "Smart Formatter (long strings): " << duration.count() << " microseconds" << std::endl; + } + + // 比較: 従来のview()固定 + { + auto start = high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream sf; + sf << "Test" << i; + auto result = sf.view(); + (void)result.size(); + } + auto end = high_resolution_clock::now(); + auto duration = duration_cast(end - start); + std::cout << "Regular view() (short strings): " << duration.count() << " microseconds" << std::endl; + } + + // 比較: 従来のstr()固定 + { + auto start = high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream sf; + sf << "Test" << i; + auto result = sf.str(); + (void)result.size(); + } + auto end = high_resolution_clock::now(); + auto duration = duration_cast(end - start); + std::cout << "Regular str() (short strings): " << duration.count() << " microseconds" << std::endl; + } + + std::cout << std::endl; + + // テンプレート版のテスト + std::cout << "=== Template-based Selection ===" << std::endl; + + { + auto start = high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + SmartFormatter sf; + sf << "Test" << i; + auto result = sf.get_string(); // str() 相当 + (void)result.size(); + } + auto end = high_resolution_clock::now(); + auto duration = duration_cast(end - start); + std::cout << "Template-based str(): " << duration.count() << " microseconds" << std::endl; + } + + { + auto start = high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + SmartFormatter sf; + sf << "Test" << i; + auto result = sf.get_string(); // view() 相当 + (void)result.size(); + } + auto end = high_resolution_clock::now(); + auto duration = duration_cast(end - start); + std::cout << "Template-based view(): " << duration.count() << " microseconds" << std::endl; + } + + REQUIRE(true); // テストが正常に実行されたことを示す +} + +TEST_CASE("Memory Overhead Analysis") { + std::cout << "=== Memory Overhead Analysis ===" << std::endl; + + // メモリ使用量の比較 + std::cout << "sizeof(std::string): " << sizeof(std::string) << " bytes" << std::endl; + std::cout << "sizeof(std::string_view): " << sizeof(std::string_view) << " bytes" << std::endl; + std::cout << "sizeof(nodec::optimized_ostringstream): " << sizeof(nodec::optimized_ostringstream) << " bytes" << std::endl; + std::cout << "sizeof(SmartFormatter): " << sizeof(SmartFormatter) << " bytes" << std::endl; + + // 実際の文字列でのメモリ使用量テスト + { + std::string short_str = "Test123"; + std::string_view short_view = short_str; + + std::cout << "Short string - std::string size: " << short_str.size() + << ", capacity: " << short_str.capacity() << std::endl; + std::cout << "Short string - std::string_view size: " << short_view.size() << std::endl; + } + + { + std::string long_str(200, 'A'); // 200文字の長い文字列 + std::string_view long_view = long_str; + + std::cout << "Long string - std::string size: " << long_str.size() + << ", capacity: " << long_str.capacity() << std::endl; + std::cout << "Long string - std::string_view size: " << long_view.size() << std::endl; + } + + REQUIRE(true); +} + +TEST_CASE("Function Call Overhead Analysis") { + std::cout << "=== Function Call Overhead Analysis ===" << std::endl; + + const int iterations = 1000000; + nodec::optimized_ostringstream ss; + ss << "Test string for overhead analysis"; + + // str() 呼び出しオーバーヘッド + { + auto start = high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + auto result = ss.str(); + volatile auto len = result.size(); + (void)len; + } + auto end = high_resolution_clock::now(); + auto duration = duration_cast(end - start); + std::cout << "str() call overhead: " << duration.count() / iterations << " ns per call" << std::endl; + } + + // view() 呼び出しオーバーヘッド + { + auto start = high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + auto result = ss.view(); + volatile auto len = result.size(); + (void)len; + } + auto end = high_resolution_clock::now(); + auto duration = duration_cast(end - start); + std::cout << "view() call overhead: " << duration.count() / iterations << " ns per call" << std::endl; + } + + // 直接バッファアクセス(参考) + { + auto start = high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + auto buffer = ss.rdbuf()->str(); + volatile auto len = buffer.size(); + (void)len; + } + auto end = high_resolution_clock::now(); + auto duration = duration_cast(end - start); + std::cout << "Direct buffer access: " << duration.count() / iterations << " ns per call" << std::endl; + } + + REQUIRE(true); +} diff --git a/tests/formatter/traits_eof_analysis.cpp b/tests/formatter/traits_eof_analysis.cpp new file mode 100644 index 0000000..e49fec9 --- /dev/null +++ b/tests/formatter/traits_eof_analysis.cpp @@ -0,0 +1,137 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include +#include +#include +#include + +// Traitsの動作を詳しく調べるテスト +TEST_CASE("Traits::eq_int_type と EOF 処理の詳細解説") { + std::cout << "\n=== Traits と EOF 処理の詳細分析 ===" << std::endl; + + using Traits = std::char_traits; + using int_type = Traits::int_type; + + // EOFの値を確認 + int_type eof_value = Traits::eof(); + std::cout << "EOF値: " << eof_value << std::endl; + std::cout << "EOF値 (16進): 0x" << std::hex << eof_value << std::dec << std::endl; + + // 通常の文字値との比較 + char normal_char = 'A'; + int_type normal_int_type = Traits::to_int_type(normal_char); + + std::cout << "\n--- 通常文字の処理 ---" << std::endl; + std::cout << "文字 'A' の int_type値: " << normal_int_type << std::endl; + std::cout << "文字 'A' (16進): 0x" << std::hex << normal_int_type << std::dec << std::endl; + + // eq_int_type による比較 + bool is_eof_normal = Traits::eq_int_type(normal_int_type, eof_value); + bool is_not_eof_normal = !Traits::eq_int_type(normal_int_type, eof_value); + + std::cout << "Traits::eq_int_type('A', EOF): " << std::boolalpha << is_eof_normal << std::endl; + std::cout << "!Traits::eq_int_type('A', EOF): " << std::boolalpha << is_not_eof_normal << std::endl; + + // EOFとの比較 + bool is_eof_eof = Traits::eq_int_type(eof_value, eof_value); + bool is_not_eof_eof = !Traits::eq_int_type(eof_value, eof_value); + + std::cout << "\n--- EOF値の処理 ---" << std::endl; + std::cout << "Traits::eq_int_type(EOF, EOF): " << std::boolalpha << is_eof_eof << std::endl; + std::cout << "!Traits::eq_int_type(EOF, EOF): " << std::boolalpha << is_not_eof_eof << std::endl; + + // 実際の overflow() 内での処理シミュレーション + std::cout << "\n--- overflow() 内での処理シミュレーション ---" << std::endl; + + auto simulate_overflow = [](int_type c) { + std::cout << "入力値: " << c << " (0x" << std::hex << c << std::dec << ")" << std::endl; + + if (!Traits::eq_int_type(c, Traits::eof())) { + std::cout << "→ 有効な文字として処理される" << std::endl; + char actual_char = Traits::to_char_type(c); + std::cout << "→ 変換された文字: '" << actual_char << "'" << std::endl; + return c; // 成功 + } else { + std::cout << "→ EOF として認識され、処理をスキップ" << std::endl; + return Traits::not_eof(c); // EOFでない値を返す + } + }; + + std::cout << "\n1. 通常文字 'B' の場合:" << std::endl; + simulate_overflow(Traits::to_int_type('B')); + + std::cout << "\n2. EOF値の場合:" << std::endl; + simulate_overflow(Traits::eof()); + + std::cout << "\n3. 数値 255 (0xFF) の場合:" << std::endl; + simulate_overflow(255); + + REQUIRE(true); +} + +TEST_CASE("型安全性の重要性") { + std::cout << "\n=== 型安全性の重要性 ===" << std::endl; + + using Traits = std::char_traits; + + // 危険な比較 vs 安全な比較 + int_type c = Traits::to_int_type('A'); + int_type eof_val = Traits::eof(); + + std::cout << "文字 'A' の値: " << c << std::endl; + std::cout << "EOF の値: " << eof_val << std::endl; + + // 直接比較(推奨されない) + bool unsafe_comparison = (c == eof_val); + std::cout << "直接比較 (c == eof): " << std::boolalpha << unsafe_comparison << std::endl; + + // 安全な比較(推奨) + bool safe_comparison = Traits::eq_int_type(c, eof_val); + std::cout << "安全な比較 Traits::eq_int_type(c, eof): " << std::boolalpha << safe_comparison << std::endl; + + // なぜ安全な比較が重要か + std::cout << "\n--- 型変換の問題 ---" << std::endl; + + // 文字型の範囲 + std::cout << "char の最小値: " << static_cast(std::numeric_limits::min()) << std::endl; + std::cout << "char の最大値: " << static_cast(std::numeric_limits::max()) << std::endl; + std::cout << "int_type のサイズ: " << sizeof(Traits::int_type) << " bytes" << std::endl; + std::cout << "char のサイズ: " << sizeof(char) << " bytes" << std::endl; + + REQUIRE(true); +} + +TEST_CASE("実際のoverflow()動作確認") { + std::cout << "\n=== 実際の overflow() 動作確認 ===" << std::endl; + + // カスタムデバッグstreambuf + class debug_stringbuf : public nodec::basic_reserved_stringbuf { + public: + using base_type = nodec::basic_reserved_stringbuf; + using int_type = std::char_traits::int_type; + + int_type overflow(int_type c) override { + std::cout << "overflow() called with: " << c; + + if (!std::char_traits::eq_int_type(c, std::char_traits::eof())) { + std::cout << " → 有効な文字 '" << std::char_traits::to_char_type(c) << "'" << std::endl; + return base_type::overflow(c); + } else { + std::cout << " → EOF(処理スキップ)" << std::endl; + return std::char_traits::not_eof(c); + } + } + }; + + debug_stringbuf buf; + std::ostream stream(&buf); + + std::cout << "文字 'X' を書き込み:" << std::endl; + stream << 'X'; + + std::cout << "文字 'Y' を書き込み:" << std::endl; + stream << 'Y'; + + std::cout << "結果: \"" << buf.str() << "\"" << std::endl; + + REQUIRE(true); +} diff --git a/tests/formatter/view_analysis.cpp b/tests/formatter/view_analysis.cpp new file mode 100644 index 0000000..d6bad58 --- /dev/null +++ b/tests/formatter/view_analysis.cpp @@ -0,0 +1,98 @@ +#include +#include +#include +#include + +class PreciseBenchmark { +public: + PreciseBenchmark(const std::string& name) : name_(name) { + start_ = std::chrono::high_resolution_clock::now(); + } + + ~PreciseBenchmark() { + auto end = std::chrono::high_resolution_clock::now(); + auto ns = std::chrono::duration_cast(end - start_); + std::cout << std::setw(40) << name_ << ": " << std::setw(10) << ns.count() << " ns" << std::endl; + } + +private: + std::string name_; + std::chrono::high_resolution_clock::time_point start_; +}; + +int main() { + const int iterations = 1000000; + + std::cout << "=== View() vs str() Detailed Analysis ===" << std::endl; + std::cout << std::setw(40) << "Test Case" << ": " << std::setw(10) << "Time (ns)" << std::endl; + std::cout << std::string(55, '-') << std::endl; + + // 1. シンプルな文字列での比較 + { + PreciseBenchmark bench("Simple str() - 1M iterations"); + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream oss; + oss << "Test " << i; + std::string result = oss.str(); + volatile auto len = result.length(); + (void)len; + } + } + + { + PreciseBenchmark bench("Simple view() - 1M iterations"); + for (int i = 0; i < iterations; ++i) { + nodec::optimized_ostringstream oss; + oss << "Test " << i; + std::string_view result = oss.view(); + volatile auto len = result.length(); + (void)len; + } + } + + // 2. 単一操作での比較 + std::cout << std::endl; + nodec::optimized_ostringstream test_oss; + test_oss << "This is a test string with some content: " << 12345 << ", float: " << 3.14159; + + { + PreciseBenchmark bench("Single str() call - 1M times"); + for (int i = 0; i < iterations; ++i) { + std::string result = test_oss.str(); + volatile auto len = result.length(); + (void)len; + } + } + + { + PreciseBenchmark bench("Single view() call - 1M times"); + for (int i = 0; i < iterations; ++i) { + std::string_view result = test_oss.view(); + volatile auto len = result.length(); + (void)len; + } + } + + // 3. コンストラクション コストの比較 + std::cout << std::endl; + { + PreciseBenchmark bench("string construction only"); + for (int i = 0; i < iterations; ++i) { + std::string result("Test string content for analysis"); + volatile auto len = result.length(); + (void)len; + } + } + + { + PreciseBenchmark bench("string_view construction only"); + std::string source("Test string content for analysis"); + for (int i = 0; i < iterations; ++i) { + std::string_view result(source); + volatile auto len = result.length(); + (void)len; + } + } + + return 0; +} diff --git a/view_performance_analysis.md b/view_performance_analysis.md new file mode 100644 index 0000000..0557b9d --- /dev/null +++ b/view_performance_analysis.md @@ -0,0 +1,171 @@ +# FastFormatter view() vs str() Performance Analysis + +## 実行結果の要約 + +### Release Build (O2最適化) +``` +--- Simple Concatenation Test --- +FastFormatter - Simple concatenation: 64771 microseconds +FastFormatter view() - Simple concatenation: 59674 microseconds (✓ view() が 8% 高速) + +--- Numeric Formatting Test --- +FastFormatter - Numeric formatting: 161807 microseconds +FastFormatter view() - Numeric formatting: 162424 microseconds (≈ ほぼ同等) + +--- Long Chain Test --- +FastFormatter - Long chain: 25528 microseconds +FastFormatter view() - Long chain: 27137 microseconds (✗ view() が 6% 遅い) + +--- Memory Usage Test --- +FastFormatter - Multiple instances: 64753 microseconds +FastFormatter view() - Multiple instances: 73830 microseconds (✗ view() が 14% 遅い) + +--- Reserve Capacity Test --- +FastFormatter view() with reserve - Long text: 114391 microseconds +FastFormatter without reserve - Long text: 115304 microseconds (✓ view() が僅かに高速) + +--- String View Performance Test --- +FastFormatter view() - No copy: 75927 microseconds +FastFormatter str() - With copy: 67877 microseconds (✗ view() が 12% 遅い) +FastFormatter view() - Long strings: 14468 microseconds +FastFormatter str() - Long strings: 22152 microseconds (✓ view() が 35% 高速) +``` + +### Debug Build (最適化なし) +``` +--- Simple Concatenation Test --- +FastFormatter - Simple concatenation: 524202 microseconds +FastFormatter view() - Simple concatenation: 457610 microseconds (✓ view() が 13% 高速) + +--- Numeric Formatting Test --- +FastFormatter - Numeric formatting: 1553837 microseconds +FastFormatter view() - Numeric formatting: 1198604 microseconds (✓ view() が 23% 高速) + +--- Memory Usage Test --- +FastFormatter - Multiple instances: 623764 microseconds +FastFormatter view() - Multiple instances: 507135 microseconds (✓ view() が 19% 高速) + +--- String View Performance Test --- +FastFormatter view() - No copy: 963670 microseconds +FastFormatter str() - With copy: 955954 microseconds (≈ ほぼ同等) +FastFormatter view() - Long strings: 104077 microseconds +FastFormatter str() - Long strings: 118974 microseconds (✓ view() が 13% 高速) +``` + +### マイクロベンチマーク結果 (Release) +``` +Simple str() - 1M iterations: 813138500 ns +Simple view() - 1M iterations: 755528800 ns (✓ view() が 7% 高速) +Single str() call - 1M times: 70939900 ns +Single view() call - 1M times: 925100 ns (✓ view() が 98.7% 高速) +``` + +## 分析結果 + +### 1. view()が高速な場合 +- **長い文字列の処理**: 文字列が長いほど、コピーコストが増大するため、view()の優位性が明確に現れる +- **単純な呼び出し**: `view()`自体は`string_view`の軽量な構築なので、純粋な呼び出し時間は非常に短い +- **Debug builds**: 最適化されていない環境では、コピーコストが顕著に現れるため、view()が多くの場合で高速 + +### 2. view()が遅くなる場合 +- **短い文字列での複雑な処理**: 短い文字列では、コピーコストが小さく、その他の処理オーバーヘッドが相対的に大きくなる +- **Release builds での最適化の影響**: + +#### 2.1 コンパイラ最適化の影響 +```cpp +// str()の場合 +string_type str() const { + return buffer_; // RVO (Return Value Optimization) やNRVO適用可能 +} + +// view()の場合 +string_view_type view() const { + return string_view_type(buffer_.data(), buffer_.size()); // 関数呼び出しが必要 +} +``` + +#### 2.2 メモリアクセスパターンの違い +- `str()`: 文字列コピーが発生するが、連続的なメモリアクセスで、CPU キャッシュに優しい +- `view()`: `buffer_.data()` と `buffer_.size()` の2つの関数呼び出しが必要 + +#### 2.3 標準ライブラリの最適化 +- `std::string` のコピーコンストラクタは高度に最適化されており、短い文字列に対してはSSO (Small String Optimization) が有効 +- `std::string_view` の構築も軽量だが、関数呼び出しオーバーヘッドが存在 + +### 3. ベンチマーク結果の解釈 + +#### 3.1 "Long Chain Test" で view() が遅い理由 +```cpp +// 複数の短い文字列を連結する場合 +ss << "Item" << i << ": " << (i * 2.5) << " units"; +``` +- 各操作で生成される文字列が比較的短い +- 繰り返し処理でのオーバーヘッドが累積 +- Release最適化により、str()のコピーが高速化 + +#### 3.2 "Memory Usage Test" で view() が遅い理由 +```cpp +// 多数のインスタンスを作成・破棄する場合 +for (int i = 0; i < iterations; ++i) { + FastFormatter ff; + ff << "Test" << i; + auto result = ff.view(); // または ff.str() +} +``` +- インスタンス作成・破棄のオーバーヘッドが支配的 +- view() の軽量性が相対的に目立たない +- Release最適化により、全体の処理が高速化され、差が縮まる + +## 結論と推奨事項 + +### view() を使うべき場合 +1. **長い文字列を扱う場合** (目安: 100文字以上) +2. **Debug builds での開発時** +3. **文字列内容を変更しない読み取り専用の用途** +4. **メモリ使用量を抑えたい場合** + +### str() を使うべき場合 +1. **短い文字列を扱う場合** (目安: 100文字未満) +2. **Release builds での高性能が要求される場合** +3. **文字列を他の関数に渡す必要がある場合** +4. **既存のコードとの互換性が重要な場合** + +### 最適化のための提案 + +#### 1. Conditional Return (条件分岐によるリターン) +```cpp +// 文字列長に応じて自動選択 +auto get_result() const { + if (buffer_.size() > 100) { + return view(); // 長い文字列はview() + } else { + return str(); // 短い文字列はstr() + } +} +``` + +#### 2. Template Specialization +```cpp +template +auto get_string() const { + if constexpr (PreferView) { + return view(); + } else { + return str(); + } +} +``` + +#### 3. Benchmark-driven Selection +実際の使用パターンに基づいて、アプリケーション固有の最適化を行う。 + +## 技術的な洞察 + +この分析により、以下の重要な洞察が得られました: + +1. **コンパイラ最適化の影響は非常に大きい**: Release builds では予想外の結果が生じる +2. **文字列長がパフォーマンスの決定要因**: 閾値を超えるとview()が明確に有利 +3. **コンテキストが重要**: 単純な処理 vs 複雑な処理で結果が異なる +4. **標準ライブラリの最適化は侮れない**: 特にSSO (Small String Optimization) の効果 + +これらの結果は、実際のアプリケーションでどちらを選択するかを決定する際の重要な指針となります。 From 2b31fc93fd51afbf14b8e436741002b3441da9fc Mon Sep 17 00:00:00 2001 From: IOE Date: Sat, 12 Jul 2025 20:08:37 +0900 Subject: [PATCH 3/6] update --- include/nodec/formatter.hpp | 752 +++--------------- include/nodec/optimized_ostringstream.hpp | 269 ------- overflow_xsputn_timing_analysis.md | 189 ----- test_ostringstream.cpp | 10 - tests/CMakeLists.txt | 37 +- tests/formatter/formatter.cpp | 214 ++--- tests/formatter/formatter_benchmark.cpp | 402 ++-------- .../formatter_detailed_benchmark.cpp | 121 --- .../formatter_ostringstream_compat.cpp | 214 +++-- .../formatter/formatter_vector3_benchmark.cpp | 243 ------ tests/formatter/hex_debug.cpp | 44 - tests/formatter/openmode_examples.cpp | 29 - .../optimized_ostringstream_test.cpp | 206 ----- .../formatter/ostringstream_verification.cpp | 15 - tests/formatter/overflow_xsputn_timing.cpp | 217 ----- tests/formatter/smart_formatter_analysis.cpp | 220 ----- tests/formatter/traits_eof_analysis.cpp | 137 ---- tests/formatter/vector3_debug.cpp | 25 - tests/formatter/view_analysis.cpp | 98 --- view_performance_analysis.md | 171 ---- 20 files changed, 414 insertions(+), 3199 deletions(-) delete mode 100644 include/nodec/optimized_ostringstream.hpp delete mode 100644 overflow_xsputn_timing_analysis.md delete mode 100644 test_ostringstream.cpp delete mode 100644 tests/formatter/formatter_detailed_benchmark.cpp delete mode 100644 tests/formatter/formatter_vector3_benchmark.cpp delete mode 100644 tests/formatter/hex_debug.cpp delete mode 100644 tests/formatter/openmode_examples.cpp delete mode 100644 tests/formatter/optimized_ostringstream_test.cpp delete mode 100644 tests/formatter/ostringstream_verification.cpp delete mode 100644 tests/formatter/overflow_xsputn_timing.cpp delete mode 100644 tests/formatter/smart_formatter_analysis.cpp delete mode 100644 tests/formatter/traits_eof_analysis.cpp delete mode 100644 tests/formatter/vector3_debug.cpp delete mode 100644 tests/formatter/view_analysis.cpp delete mode 100644 view_performance_analysis.md diff --git a/include/nodec/formatter.hpp b/include/nodec/formatter.hpp index 5367b91..d5f59db 100644 --- a/include/nodec/formatter.hpp +++ b/include/nodec/formatter.hpp @@ -6,18 +6,20 @@ #include #include #include +#include +#include #include #include namespace nodec { -class Formatter { +class FormatterLegacy { public: - Formatter() noexcept {} - ~Formatter() noexcept {} + FormatterLegacy() noexcept {} + ~FormatterLegacy() noexcept {} template - Formatter &operator<<(const T &value) noexcept { + FormatterLegacy &operator<<(const T &value) noexcept { stream_ << value; return *this; } @@ -29,651 +31,107 @@ class Formatter { private: std::ostringstream stream_; - NODEC_DISABLE_COPY(Formatter) + NODEC_DISABLE_COPY(FormatterLegacy) }; -// // カスタム操作子の定義 -// struct SetWidth { -// int width; -// explicit SetWidth(int w) : width(w) {} -// }; - -// struct SetFill { -// char fill_char; -// explicit SetFill(char c) : fill_char(c) {} -// }; - -// struct SetPrecision { -// int precision; -// explicit SetPrecision(int p) : precision(p) {} -// }; - -// // カスタム操作子のヘルパー関数 -// inline SetWidth setw(int width) { return SetWidth(width); } -// inline SetFill setfill(char fill_char) { return SetFill(fill_char); } -// inline SetPrecision setprecision(int precision) { return SetPrecision(precision); } - -// /* -// * The Text Formatter -// * -// * if you use C++20, consider to use std::format. -// * * -// * -// * It is designed for easy use of text format even though under C++20. -// * ostringstream compatible formatter with high performance. -// * -// * Implementation refs: -// * * -// * -// */ - -// class Formatter { -// public: -// enum class Alignment { -// Left, -// Right, -// Internal -// }; - -// enum class FloatFormat { -// Default, -// Fixed, -// Scientific -// }; - -// Formatter() noexcept = default; -// ~Formatter() noexcept = default; - -// // 事前リザーブ機能付きコンストラクタ -// explicit Formatter(size_t reserve_size) noexcept { -// buffer_.reserve(reserve_size); -// } - -// // 基本型の効率的な処理 -// Formatter &operator<<(const char* value) noexcept { -// if (value) { -// append_string_with_format(value); -// } -// return *this; -// } - -// Formatter &operator<<(const std::string& value) noexcept { -// append_string_with_format(value); -// return *this; -// } - -// Formatter &operator<<(char value) noexcept { -// append_char_with_format(value); -// return *this; -// } - -// // 整数型の効率的な処理 -// Formatter &operator<<(int value) noexcept { -// append_integer_with_format(value); -// return *this; -// } - -// Formatter &operator<<(long value) noexcept { -// append_integer_with_format(value); -// return *this; -// } - -// Formatter &operator<<(long long value) noexcept { -// append_integer_with_format(value); -// return *this; -// } - -// Formatter &operator<<(unsigned int value) noexcept { -// append_unsigned_integer_with_format(value); -// return *this; -// } - -// Formatter &operator<<(unsigned long value) noexcept { -// append_unsigned_integer_with_format(value); -// return *this; -// } - -// Formatter &operator<<(unsigned long long value) noexcept { -// append_unsigned_integer_with_format(value); -// return *this; -// } - -// // 浮動小数点型の処理 -// Formatter &operator<<(float value) noexcept { -// append_float_with_format(static_cast(value)); -// return *this; -// } - -// Formatter &operator<<(double value) noexcept { -// append_float_with_format(value); -// return *this; -// } - -// // ostream操作子のサポート(hex, dec等) -// Formatter &operator<<(std::ios_base& (*manipulator)(std::ios_base&)) noexcept { -// if (manipulator == std::hex) { -// base_mode_ = 16; -// } else if (manipulator == std::dec) { -// base_mode_ = 10; -// } else if (manipulator == std::oct) { -// base_mode_ = 8; -// } else if (manipulator == std::fixed) { -// float_mode_ = FloatMode::Fixed; -// } else if (manipulator == std::scientific) { -// float_mode_ = FloatMode::Scientific; -// } else if (manipulator == std::uppercase) { -// uppercase_ = true; -// } else if (manipulator == std::nouppercase) { -// uppercase_ = false; -// } else if (manipulator == std::showbase) { -// showbase_ = true; -// } else if (manipulator == std::noshowbase) { -// showbase_ = false; -// } else if (manipulator == std::boolalpha) { -// boolalpha_ = true; -// } else if (manipulator == std::noboolalpha) { -// boolalpha_ = false; -// } else if (manipulator == std::left) { -// align_mode_ = AlignMode::Left; -// } else if (manipulator == std::right) { -// align_mode_ = AlignMode::Right; -// } else if (manipulator == std::internal) { -// align_mode_ = AlignMode::Internal; -// } -// return *this; -// } - -// // カスタム操作子のサポート -// Formatter &operator<<(const SetWidth& sw) noexcept { -// width_ = sw.width; -// return *this; -// } - -// Formatter &operator<<(const SetFill& sf) noexcept { -// fill_char_ = sf.fill_char; -// return *this; -// } - -// Formatter &operator<<(const SetPrecision& sp) noexcept { -// precision_ = sp.precision; -// return *this; -// } - -// // bool型の特別処理 -// Formatter &operator<<(bool value) noexcept { -// if (boolalpha_) { -// buffer_ += (value ? "true" : "false"); -// } else { -// buffer_ += (value ? '1' : '0'); -// } -// return *this; -// } - -// // その他の型(フォールバック)- ただし、専用マニピュレータは除外 -// template -// typename std::enable_if< -// !std::is_same::value && -// !std::is_same::value && -// !std::is_same::value, -// Formatter& -// >::type operator<<(const T &value) noexcept { -// // 複雑な型はostringstreamにフォールバック -// std::ostringstream oss; -// oss << value; -// buffer_ += oss.str(); -// return *this; -// } - -// operator std::string() const noexcept { -// return buffer_; -// } - -// // 追加のユーティリティメソッド -// void reserve(size_t size) noexcept { -// buffer_.reserve(size); -// } - -// void clear() noexcept { -// buffer_.clear(); -// // フォーマット状態をリセット -// base_mode_ = 10; -// float_mode_ = FloatMode::Default; -// precision_ = 6; -// width_ = 0; -// fill_char_ = ' '; -// uppercase_ = false; -// showbase_ = false; -// boolalpha_ = false; -// align_mode_ = AlignMode::Right; -// } - -// size_t size() const noexcept { -// return buffer_.size(); -// } - -// const std::string& str() const noexcept { -// return buffer_; -// } - -// // マニピュレータ用のpublicメソッド -// void apply_setw(int w) noexcept { -// width_ = w; -// } - -// void apply_setfill(char c) noexcept { -// fill_char_ = c; -// } - -// void apply_setprecision(int p) noexcept { -// precision_ = p; -// } - -// private: -// enum class FloatMode { -// Default, -// Fixed, -// Scientific -// }; - -// enum class AlignMode { -// Left, -// Right, -// Internal -// }; - -// std::string buffer_; - -// // フォーマット状態 -// int base_mode_ = 10; // 10進数がデフォルト -// FloatMode float_mode_ = FloatMode::Default; -// int precision_ = 6; // デフォルト精度 -// int width_ = 0; // フィールド幅 -// char fill_char_ = ' '; // 埋め文字 -// bool uppercase_ = false; // 大文字表示 -// bool showbase_ = false; // プレフィックス表示 -// bool boolalpha_ = false; // bool値をtrue/falseで表示 -// AlignMode align_mode_ = AlignMode::Right; // 右揃えがデフォルト - -// // マニピュレータ適用 -// template -// void apply_manipulator(const T& manipulator) noexcept { -// // SFINAE を使ってsetw, setfill, setprecisionを識別 -// // 実際の実装では型特性を使用するが、簡略化のため条件分岐で対応 -// // この部分は実装が複雑になるため、後で詳細化 -// } - -// // 整数を効率的に文字列に変換 -// template -// void append_integer(IntType value) noexcept { -// if (base_mode_ == 16) { -// if (value < 0) { -// buffer_ += '-'; -// append_hex_with_format(static_cast(-value)); -// } else { -// append_hex_with_format(static_cast(value)); -// } -// } else if (base_mode_ == 8) { -// if (value < 0) { -// buffer_ += '-'; -// append_oct_with_format(static_cast(-value)); -// } else { -// append_oct_with_format(static_cast(value)); -// } -// } else { -// append_decimal_with_format(value); -// } -// } - -// template -// void append_unsigned_integer(UIntType value) noexcept { -// if (base_mode_ == 16) { -// append_hex_with_format(value); -// } else if (base_mode_ == 8) { -// append_oct_with_format(value); -// } else { -// append_decimal_with_format(value); -// } -// } - -// // 10進数フォーマット -// template -// void append_decimal_with_format(IntType value) noexcept { -// std::string result; - -// if (value == 0) { -// result = "0"; -// } else { -// bool negative = value < 0; -// if (negative) { -// value = -value; -// } - -// char temp[32]; -// int pos = 0; -// while (value > 0) { -// temp[pos++] = '0' + (value % 10); -// value /= 10; -// } - -// if (negative) { -// result += '-'; -// } - -// for (int i = pos - 1; i >= 0; --i) { -// result += temp[i]; -// } -// } - -// apply_field_formatting(result); -// } - -// // 16進数フォーマット -// void append_hex_with_format(unsigned long long value) noexcept { -// std::string result; - -// if (showbase_ && value != 0) { -// result += "0x"; -// } - -// if (value == 0) { -// result += '0'; -// } else { -// char temp[32]; -// int pos = 0; -// const char* hex_chars = uppercase_ ? "0123456789ABCDEF" : "0123456789abcdef"; - -// while (value > 0) { -// temp[pos++] = hex_chars[value % 16]; -// value /= 16; -// } - -// for (int i = pos - 1; i >= 0; --i) { -// result += temp[i]; -// } -// } - -// apply_field_formatting(result); -// } - -// // 8進数フォーマット -// void append_oct_with_format(unsigned long long value) noexcept { -// std::string result; - -// if (showbase_ && value != 0) { -// result += '0'; -// } - -// if (value == 0) { -// result += '0'; -// } else { -// char temp[32]; -// int pos = 0; - -// while (value > 0) { -// temp[pos++] = '0' + (value % 8); -// value /= 8; -// } - -// for (int i = pos - 1; i >= 0; --i) { -// result += temp[i]; -// } -// } - -// apply_field_formatting(result); -// } - -// // フィールドフォーマット適用 -// void apply_field_formatting(const std::string& content) noexcept { -// if (width_ <= static_cast(content.length())) { -// // 幅指定なし、または内容が幅以上 -// buffer_ += content; -// width_ = 0; // 一度使用したらリセット -// return; -// } - -// int padding = width_ - static_cast(content.length()); - -// switch (align_mode_) { -// case AlignMode::Left: -// buffer_ += content; -// buffer_.append(padding, fill_char_); -// break; - -// case AlignMode::Right: -// buffer_.append(padding, fill_char_); -// buffer_ += content; -// break; - -// case AlignMode::Internal: -// // 符号と数値の間に埋め文字を挿入 -// if (!content.empty() && (content[0] == '-' || content[0] == '+')) { -// buffer_ += content[0]; -// buffer_.append(padding, fill_char_); -// buffer_ += content.substr(1); -// } else if (content.length() >= 2 && content[0] == '0' && (content[1] == 'x' || content[1] == 'X')) { -// buffer_ += content.substr(0, 2); -// buffer_.append(padding, fill_char_); -// buffer_ += content.substr(2); -// } else { -// buffer_.append(padding, fill_char_); -// buffer_ += content; -// } -// break; -// } - -// width_ = 0; // 一度使用したらリセット -// } - -// // フォーマット付きの文字列・数値処理メソッド -// void append_string_with_format(const std::string& str) noexcept { -// if (width_ > 0 && static_cast(str.length()) < width_) { -// apply_field_formatting(str); -// } else { -// buffer_ += str; -// } -// width_ = 0; // 使用後リセット -// } - -// void append_char_with_format(char c) noexcept { -// if (width_ > 0 && width_ > 1) { -// std::string str(1, c); -// apply_field_formatting(str); -// } else { -// buffer_ += c; -// } -// width_ = 0; // 使用後リセット -// } - -// void append_integer_with_format(long long value) noexcept { -// std::string num_str = format_integer(value); -// if (width_ > 0 && static_cast(num_str.length()) < width_) { -// apply_field_formatting(num_str); -// } else { -// buffer_ += num_str; -// } -// width_ = 0; // 使用後リセット -// } - -// void append_unsigned_integer_with_format(unsigned long long value) noexcept { -// std::string num_str = format_unsigned_integer(value); -// if (width_ > 0 && static_cast(num_str.length()) < width_) { -// apply_field_formatting(num_str); -// } else { -// buffer_ += num_str; -// } -// width_ = 0; // 使用後リセット -// } - -// void append_float_with_format(double value) noexcept { -// std::ostringstream oss; -// oss.precision(precision_); - -// // フロートモード設定 -// switch (float_mode_) { -// case FloatMode::Fixed: -// oss << std::fixed; -// break; -// case FloatMode::Scientific: -// oss << std::scientific; -// break; -// case FloatMode::Default: -// // デフォルトフォーマット -// break; -// } - -// // 大文字設定 -// if (uppercase_) { -// oss << std::uppercase; -// } - -// oss << value; -// std::string result = oss.str(); - -// if (width_ > 0 && static_cast(result.length()) < width_) { -// apply_field_formatting(result); -// } else { -// buffer_ += result; -// } -// width_ = 0; // 使用後リセット -// } - -// // 数値フォーマット関数 -// std::string format_integer(long long value) noexcept { -// if (base_mode_ == 16) { -// return format_hex_signed(value); -// } else if (base_mode_ == 8) { -// return format_oct_signed(value); -// } else { -// return format_decimal(value); -// } -// } - -// std::string format_unsigned_integer(unsigned long long value) noexcept { -// if (base_mode_ == 16) { -// return format_hex(value); -// } else if (base_mode_ == 8) { -// return format_oct(value); -// } else { -// return format_unsigned_decimal(value); -// } -// } - -// std::string format_hex_signed(long long value) noexcept { -// if (value < 0) { -// return "-" + format_hex(static_cast(-value)); -// } else { -// return format_hex(static_cast(value)); -// } -// } - -// std::string format_oct_signed(long long value) noexcept { -// if (value < 0) { -// return "-" + format_oct(static_cast(-value)); -// } else { -// return format_oct(static_cast(value)); -// } -// } - -// std::string format_decimal(long long value) noexcept { -// if (value == 0) return "0"; - -// std::string result; -// bool negative = value < 0; -// if (negative) value = -value; - -// while (value > 0) { -// result = char('0' + value % 10) + result; -// value /= 10; -// } - -// if (negative) result = "-" + result; -// return result; -// } - -// std::string format_unsigned_decimal(unsigned long long value) noexcept { -// if (value == 0) return "0"; - -// std::string result; -// while (value > 0) { -// result = char('0' + value % 10) + result; -// value /= 10; -// } -// return result; -// } - -// std::string format_hex(unsigned long long value) noexcept { -// if (value == 0) { -// std::string result = "0"; -// if (showbase_) result = "0x" + result; -// return result; -// } - -// std::string result; -// const char* hex_chars = uppercase_ ? "0123456789ABCDEF" : "0123456789abcdef"; - -// while (value > 0) { -// result = hex_chars[value % 16] + result; -// value /= 16; -// } - -// if (showbase_) { -// result = (uppercase_ ? "0X" : "0x") + result; -// } - -// return result; -// } - -// std::string format_oct(unsigned long long value) noexcept { -// if (value == 0) { -// std::string result = "0"; -// if (showbase_) result = "0" + result; -// return result; -// } - -// std::string result; -// while (value > 0) { -// result = char('0' + value % 8) + result; -// value /= 8; -// } - -// if (showbase_) { -// result = "0" + result; -// } - -// return result; -// } - -// void append_float(double value) noexcept { -// std::ostringstream oss; - -// // 精度設定 -// oss.precision(precision_); - -// // フロートモード設定 -// switch (float_mode_) { -// case FloatMode::Fixed: -// oss << std::fixed; -// break; -// case FloatMode::Scientific: -// oss << std::scientific; -// break; -// case FloatMode::Default: -// // デフォルトフォーマット -// break; -// } - -// // 大文字設定 -// if (uppercase_) { -// oss << std::uppercase; -// } - -// oss << value; -// std::string result = oss.str(); -// apply_field_formatting(result); -// } - -// NODEC_DISABLE_COPY(Formatter) -// }; +template> +class BasicStringBuilderStream { +private: + class BasicStringBuilderStreamBuf : public std::basic_streambuf { + public: + using base_type = std::basic_streambuf; + using string_type = std::basic_string; + using string_view_type = std::basic_string_view; + 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(count)); + return count; + } + + private: + string_type &dest_; + }; + +public: + using string_type = std::basic_string; + using string_view_type = std::basic_string_view; + using size_type = typename string_type::size_type; + using stringbuf_type = BasicStringBuilderStreamBuf; + + BasicStringBuilderStream(string_type &dest): base_stream_(&buffer_), buffer_(dest) {} + + string_type str() const { + return buffer_.str(); + } + + BasicStringBuilderStream &operator<<(const char *str) { + if (str == nullptr) { + return *this; + } + + // 文字列の長さを取得 + std::streamsize count = static_cast(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 + BasicStringBuilderStream &operator<<(const T &value) { + base_stream_ << value; + return *this; + } + +private: + stringbuf_type buffer_; + std::basic_ostream base_stream_; +}; + +using StringBuilderStream = BasicStringBuilderStream; template class ErrorFormatter { diff --git a/include/nodec/optimized_ostringstream.hpp b/include/nodec/optimized_ostringstream.hpp deleted file mode 100644 index bdeb29a..0000000 --- a/include/nodec/optimized_ostringstream.hpp +++ /dev/null @@ -1,269 +0,0 @@ -#ifndef NODEC__OPTIMIZED_OSTRINGSTREAM_HPP_ -#define NODEC__OPTIMIZED_OSTRINGSTREAM_HPP_ - -#include -#include -#include - -namespace nodec { - -/** - * 高度に最適化されたstreambuf - 確実な事前リザーブ機能付き - * std::stringbufとは異なり、真の高性能を追求したシンプルな実装 - */ -template> -class basic_reserved_stringbuf : public std::basic_streambuf { -public: - using base_type = std::basic_streambuf; - using string_type = std::basic_string; - using string_view_type = std::basic_string_view; - 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; - - // デフォルトコンストラクタ - basic_reserved_stringbuf() = default; - - // 事前リザーブ付きコンストラクタ - explicit basic_reserved_stringbuf(size_type reserve_size) { - if (reserve_size > 0) { - ensure_capacity(reserve_size); - } - } - - // 初期文字列 + 事前リザーブ付きコンストラクタ - basic_reserved_stringbuf(const string_type &initial_str, size_type reserve_size) - : buffer_(initial_str) { - if (reserve_size > initial_str.size()) { - ensure_capacity(reserve_size); - } - } - - // ムーブコンストラクタ - basic_reserved_stringbuf(basic_reserved_stringbuf &&other) noexcept - : base_type(std::move(other)), buffer_(std::move(other.buffer_)) {} - - // ムーブ代入演算子 - basic_reserved_stringbuf &operator=(basic_reserved_stringbuf &&other) noexcept { - if (this != &other) { - base_type::operator=(std::move(other)); - buffer_ = std::move(other.buffer_); - } - return *this; - } - - // デストラクタ - ~basic_reserved_stringbuf() = default; - - // 文字列取得 - string_type str() const { - return buffer_; - } - - // 文字列ビュー取得(コピーなし) - string_view_type view() const { - return string_view_type(buffer_.data(), buffer_.size()); - } - - // 文字列設定 - void str(const string_type &s) { - buffer_ = s; - } - - void str(string_type &&s) { - buffer_ = std::move(s); - } - - // 事前リザーブ機能 - void reserve(size_type new_capacity) { - ensure_capacity(new_capacity); - } - - // 内容をクリア(容量は保持) - void clear() { - buffer_.clear(); - } - - // 容量・サイズ情報 - size_type capacity() const { - return buffer_.capacity(); - } - - size_type size() const { - return buffer_.size(); - } - -protected: - // より効率的なオーバーフロー処理 - int_type overflow(int_type c = Traits::eof()) override { - if (!Traits::eq_int_type(c, Traits::eof())) { - // バッファに余裕があるかチェック - if (buffer_.size() < buffer_.capacity()) { - buffer_.push_back(Traits::to_char_type(c)); - } else { - // 容量不足時は少し多めに確保 - buffer_.reserve(buffer_.capacity() == 0 ? 16 : buffer_.capacity() * 2); - buffer_.push_back(Traits::to_char_type(c)); - } - return c; - } - return Traits::not_eof(c); - } - - // バルク書き込み最適化(より積極的な事前確保) - std::streamsize xsputn(const char_type *s, std::streamsize count) override { - if (count <= 0) { - return 0; - } - - // 必要な容量を事前に確保 - size_type needed = buffer_.size() + static_cast(count); - if (needed > buffer_.capacity()) { - buffer_.reserve(needed); - } - - buffer_.append(s, static_cast(count)); - return count; - } - - // 同期処理(何もしない - 直接書き込み済み) - int sync() override { - return 0; - } - -private: - string_type buffer_; - - // 効率的な容量確保(シンプル版) - void ensure_capacity(size_type capacity) { - if (capacity > buffer_.capacity()) { - buffer_.reserve(capacity); - } - } - - // コピーは禁止、ムーブのみ許可 - basic_reserved_stringbuf(const basic_reserved_stringbuf &) = delete; - basic_reserved_stringbuf &operator=(const basic_reserved_stringbuf &) = delete; -}; - -using reserved_stringbuf = basic_reserved_stringbuf; -using reserved_wstringbuf = basic_reserved_stringbuf; - -/** - * 事前リザーブ機能付きostringstream - * 完全なstd::ostringstreamとの互換性を保ちつつ、高性能を実現 - */ -template> -class basic_optimized_ostringstream : public std::basic_ostream { -public: - using base_type = std::basic_ostream; - using string_type = std::basic_string; - using string_view_type = std::basic_string_view; - using size_type = typename string_type::size_type; - using stringbuf_type = basic_reserved_stringbuf; - - // デフォルトコンストラクタ - basic_optimized_ostringstream() - : base_type(&buffer_), buffer_() {} - - // 事前リザーブ付きコンストラクタ(曖昧さ回避のため型を明確化) - explicit basic_optimized_ostringstream(size_type reserve_size) - : base_type(&buffer_), buffer_(reserve_size) {} - - // 初期文字列付きコンストラクタ - explicit basic_optimized_ostringstream(const string_type &initial_str) - : base_type(&buffer_), buffer_(initial_str, 0) {} - - // 初期文字列 + 事前リザーブ付きコンストラクタ - basic_optimized_ostringstream(const string_type &initial_str, size_type reserve_size) - : base_type(&buffer_), buffer_(initial_str, reserve_size) {} - - // ムーブコンストラクタ - basic_optimized_ostringstream(basic_optimized_ostringstream &&other) noexcept - : base_type(std::move(other)), buffer_(std::move(other.buffer_)) { - base_type::set_rdbuf(&buffer_); - } - - // ムーブ代入演算子 - basic_optimized_ostringstream &operator=(basic_optimized_ostringstream &&other) noexcept { - if (this != &other) { - base_type::operator=(std::move(other)); - buffer_ = std::move(other.buffer_); - base_type::set_rdbuf(&buffer_); - } - return *this; - } - - // デストラクタ - ~basic_optimized_ostringstream() = default; - - // rdbufアクセス - stringbuf_type *rdbuf() const { - return const_cast(&buffer_); - } - - // 文字列取得 - string_type str() const { - return buffer_.str(); - } - - // 文字列ビュー取得(コピーなし - 高性能) - string_view_type view() const { - return buffer_.view(); - } - - // 文字列設定 - void str(const string_type &s) { - buffer_.str(s); - } - - void str(string_type &&s) { - buffer_.str(std::move(s)); - } - - // 事前リザーブ機能 - void reserve(size_type new_capacity) { - buffer_.reserve(new_capacity); - } - - // 容量・サイズ情報 - size_type capacity() const { - return buffer_.capacity(); - } - - size_type size() const { - return buffer_.size(); - } - - // clear機能(内容をクリアして再利用 - 容量は保持) - void clear_content() { - buffer_.clear(); // stringbufの内容をクリア(容量保持) - base_type::clear(); // ストリームの状態もクリア - } - - // 文字列変換演算子(利便性のため) - operator string_type() const { - return str(); - } - -private: - stringbuf_type buffer_; - - // コピーは禁止、ムーブのみ許可 - basic_optimized_ostringstream(const basic_optimized_ostringstream &) = delete; - basic_optimized_ostringstream &operator=(const basic_optimized_ostringstream &) = delete; -}; - -// 型エイリアス -using optimized_ostringstream = basic_optimized_ostringstream; -using optimized_wostringstream = basic_optimized_ostringstream; - -// 利便性のための名前空間エイリアス(既存のFormatterとの互換性) -using FastFormatter = optimized_ostringstream; - -} // namespace nodec - -#endif // NODEC__OPTIMIZED_OSTRINGSTREAM_HPP_ diff --git a/overflow_xsputn_timing_analysis.md b/overflow_xsputn_timing_analysis.md deleted file mode 100644 index 8f44598..0000000 --- a/overflow_xsputn_timing_analysis.md +++ /dev/null @@ -1,189 +0,0 @@ -# C++ ostream の overflow() と xsputn() 関数呼び出しタイミング解説 - -## 🎯 重要な発見:実行結果からの分析 - -実際のテスト結果から、以下の重要なパターンが明確に分かりました: - -## 📞 関数呼び出しのタイミング - -### 1. `overflow()` - 単一文字の処理 -**呼び出される場面:** -- **単一文字**の書き込み(`stream << 'A'`) -- **数値の各桁**の書き込み(`stream << 123` → '1', '2', '3' それぞれに呼び出し) -- **マニピュレータによる空白文字**の書き込み(`std::setw(10)`による空白) - -```cpp -// 例:これらの場合にoverflow()が呼ばれる -stream << 'A'; // 1回のoverflow()呼び出し -stream << 123; // 3回のoverflow()呼び出し('1', '2', '3') -stream << 3.14; // 4回のoverflow()呼び出し('3', '.', '1', '4') -``` - -**特徴:** -- 1文字ずつの処理なので**効率が悪い** -- バッファサイズが0のままなのは、**各文字が個別のstreamインスタンス**で処理されているため - -### 2. `xsputn()` - バルク文字列の処理 -**呼び出される場面:** -- **文字列リテラル**の書き込み(`stream << "Hello"`) -- **長い文字列**の一括書き込み -- **std::string**の書き込み - -```cpp -// 例:これらの場合にxsputn()が呼ばれる -stream << "Hello"; // 1回のxsputn(5文字) -stream << some_string; // 1回のxsputn(文字列長) -``` - -**特徴:** -- **効率的**:複数文字を一度に処理 -- **自動的に容量確保**:必要に応じてバッファを拡張 - -## 📊 パフォーマンスの違い - -### 💥 非効率的なパターン(overflow()多発) -``` -📞 overflow() called with char: 'A' | Buffer size: 0 | Capacity: 15 -📞 overflow() called with char: 'B' | Buffer size: 0 | Capacity: 15 -📞 overflow() called with char: 'C' | Buffer size: 0 | Capacity: 15 -...(1000回の個別呼び出し) -``` -- **1000文字で1000回の関数呼び出し** -- 各文字ごとにオーバーヘッド発生 - -### ⚡ 効率的なパターン(xsputn()使用) -``` -📞 xsputn() called with 1000 chars: "ABCDEFGHIJKLMNOPQRST..." - | Buffer size before: 0 | Capacity: 15 - → After xsputn: Buffer size: 1000 | Capacity: 1007 -``` -- **1000文字で1回の関数呼び出し** -- 一括処理で大幅な性能向上 - -## 🔧 実装における重要な洞察 - -### 現在のoptimized_ostringstreamの実装分析 - -```cpp -// overflow() - 単一文字処理 -int_type overflow(int_type c = Traits::eof()) override { - if (!Traits::eq_int_type(c, Traits::eof())) { - if (buffer_.size() < buffer_.capacity()) { - buffer_.push_back(Traits::to_char_type(c)); - } else { - // 容量不足時は倍に拡張 - buffer_.reserve(buffer_.capacity() == 0 ? 16 : buffer_.capacity() * 2); - buffer_.push_back(Traits::to_char_type(c)); - } - return c; - } - return Traits::not_eof(c); -} - -// xsputn() - バルク文字列処理 -std::streamsize xsputn(const char_type *s, std::streamsize count) override { - if (count <= 0) return 0; - - // 必要な容量を事前に確保 - size_type needed = buffer_.size() + static_cast(count); - if (needed > buffer_.capacity()) { - buffer_.reserve(needed); - } - - buffer_.append(s, static_cast(count)); - return count; -} -``` - -## 🎭 特異なケースの解説 - -### 数値書き込みの挙動 -実行結果で確認できたように: -```cpp -stream << 123; // overflow()が3回呼ばれる -``` - -これは、数値が内部で以下のように処理されるためです: -1. `123` → 文字列 `"123"` に変換 -2. **しかし1文字ずつoverflow()で処理**される(実装依存) - -### マニピュレータとの組み合わせ -```cpp -stream << std::setw(10) << "test"; -``` -この場合: -1. 幅設定により**6個の空白文字**が`overflow()`で処理 -2. `"test"`が`xsputn()`で処理 - -## 🚀 最適化への提案 - -### 1. バッファリング戦略の改善 -```cpp -// 数値処理の最適化案 -template -auto operator<<(T&& value) -> decltype(*this) { - if constexpr (std::is_arithmetic_v>) { - // 数値を一時バッファで文字列化してからxsputn()を使用 - thread_local std::array temp_buffer; - auto result = std::to_chars(temp_buffer.data(), - temp_buffer.data() + temp_buffer.size(), - value); - this->rdbuf()->xsputn(temp_buffer.data(), result.ptr - temp_buffer.data()); - return *this; - } else { - return base_type::operator<<(std::forward(value)); - } -} -``` - -### 2. スマートなバッファ拡張 -```cpp -void smart_reserve(size_type additional_size) { - size_type current_size = buffer_.size(); - size_type needed = current_size + additional_size; - - if (needed > buffer_.capacity()) { - // より効率的な拡張戦略 - size_type new_capacity = std::max(needed, buffer_.capacity() * 2); - buffer_.reserve(new_capacity); - } -} -``` - -## 📈 実用的な使い方の指針 - -### ✅ 効率的な書き込みパターン -```cpp -// 良い例:文字列を一括処理 -stream << "Long string content"; - -// 良い例:事前に文字列を構築 -std::string result = "Value: " + std::to_string(value) + " units"; -stream << result; - -// 良い例:StringBuilder パターン -std::ostringstream temp; -temp << "Multiple" << " " << "parts"; -stream << temp.str(); // 一括で書き込み -``` - -### ❌ 避けるべきパターン -```cpp -// 悪い例:文字を個別に書き込み -for (char c : "Hello") { - stream << c; // overflow()が5回呼ばれる -} - -// 悪い例:細かい分割 -stream << "A" << "B" << "C"; // xsputn()が3回呼ばれる -``` - -## 🎯 まとめ - -1. **文字列は可能な限りまとめて書き込む** -2. **数値は内部で1文字ずつ処理される**ことを理解する -3. **xsputn()の活用**が性能向上の鍵 -4. **事前バッファ確保**でメモリ再配置を最小化 -5. **マニピュレータ使用時**の空白文字処理コストを考慮 - -この理解により、より効率的なstreamバッファ実装と使用方法を選択できるようになります。 diff --git a/test_ostringstream.cpp b/test_ostringstream.cpp deleted file mode 100644 index 8017606..0000000 --- a/test_ostringstream.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include -#include - -int main() { - std::ostringstream oss; - oss << std::hex << std::uppercase << std::showbase << std::setfill('0') << std::setw(8) << 255; - std::cout << "ostringstream result: " << oss.str() << std::endl; - return 0; -} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fced087..10b0163 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,15 +15,9 @@ 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__formatter" formatter/formatter.cpp) add_basic_test("nodec__formatter_benchmark" formatter/formatter_benchmark.cpp) -# add_basic_test("nodec__formatter_detailed_benchmark" formatter/formatter_detailed_benchmark.cpp) -# add_basic_test("nodec__formatter_vector3_benchmark" formatter/formatter_vector3_benchmark.cpp) -# add_basic_test("nodec__formatter_ostringstream_compat" formatter/formatter_ostringstream_compat.cpp) -add_basic_test("nodec__ostringstream_verification" formatter/ostringstream_verification.cpp) -# add_basic_test("nodec__hex_debug" formatter/hex_debug.cpp) -# add_basic_test("nodec__complex_format_debug" formatter/complex_format_debug.cpp) -add_basic_test("nodec__optimized_ostringstream_test" formatter/optimized_ostringstream_test.cpp) +add_basic_test("nodec__formatter_ostringstream_compat" formatter/formatter_ostringstream_compat.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) @@ -42,33 +36,6 @@ add_basic_test("nodec__unicode" unicode/unicode.cpp) add_basic_test("nodec__utility" utility/utility.cpp) add_basic_test("nodec__vector" vector/vector.cpp) -# # Debug test for optimized ostringstream -# add_basic_test("nodec__debug_test" formatter/debug_test.cpp) - -# # Constructor debug test -# add_basic_test("nodec__constructor_debug" formatter/constructor_debug.cpp) - -# # OpenMode examples test -# add_basic_test("nodec__openmode_examples" formatter/openmode_examples.cpp) - -# # Vector3 debug test -# add_basic_test("nodec__vector3_debug" formatter/vector3_debug.cpp) - -# # View analysis test -# add_basic_test("nodec__view_analysis" formatter/view_analysis.cpp) - -# # Smart formatter analysis test -# add_basic_test("nodec__smart_formatter_analysis" formatter/smart_formatter_analysis.cpp) - -# # Overflow and xsputn timing analysis -# add_basic_test("nodec__overflow_xsputn_timing" formatter/overflow_xsputn_timing.cpp) - -# # Traits and EOF analysis -# add_basic_test("nodec__traits_eof_analysis" formatter/traits_eof_analysis.cpp) - -# # EOF necessity test -# add_basic_test("nodec__eof_necessity_test" formatter/eof_necessity_test.cpp) - target_compile_definitions( nodec__unicode PRIVATE TEST_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/unicode/data" diff --git a/tests/formatter/formatter.cpp b/tests/formatter/formatter.cpp index 21adf14..896d3f1 100644 --- a/tests/formatter/formatter.cpp +++ b/tests/formatter/formatter.cpp @@ -6,112 +6,124 @@ TEST_CASE("Testing Formatter - Basic Types") { // 文字列連結 - std::string result1 = nodec::Formatter() << "Hello" << " " << "World"; - CHECK(result1 == "Hello World"); + { + std::string result; + nodec::StringBuilderStream(result) << "Hello" << " " << "World"; + CHECK(result == "Hello World"); + } // 整数 - std::string result2 = nodec::Formatter() << "Number: " << 42; - CHECK(result2 == "Number: 42"); + { + std::string result; + nodec::StringBuilderStream(result) << "Number: " << 42; + CHECK(result == "Number: 42"); + } // 浮動小数点 - std::string result3 = nodec::Formatter() << "Float: " << 3.14; - CHECK(result3 == "Float: 3.14"); + { + std::string result; + nodec::StringBuilderStream(result) << "Float: " << 3.14; + CHECK(result == "Float: 3.14"); + } // 文字 - std::string result4 = nodec::Formatter() << 'A' << 'B' << 'C'; - CHECK(result4 == "ABC"); + { + std::string result; + nodec::StringBuilderStream(result) << 'A' << 'B' << 'C'; + CHECK(result == "ABC"); + } } -TEST_CASE("Testing Formatter - Numeric Types") { - // 様々な整数型 - std::string result1 = nodec::Formatter() << static_cast(123); - CHECK(result1 == "123"); - - std::string result2 = nodec::Formatter() << static_cast(456L); - CHECK(result2 == "456"); - - std::string result3 = nodec::Formatter() << static_cast(789LL); - CHECK(result3 == "789"); - - // 負の数 - std::string result4 = nodec::Formatter() << -42; - CHECK(result4 == "-42"); - - // ゼロ - std::string result5 = nodec::Formatter() << 0; - CHECK(result5 == "0"); -} - -TEST_CASE("Testing Formatter - Hex Mode") { - // 16進数モード - std::string result1 = nodec::Formatter() << "Hex: " << std::hex << 255; - CHECK(result1 == "Hex: ff"); - - // 10進数に戻す - std::string result2 = nodec::Formatter() << std::hex << 16 << ", " << std::dec << 16; - CHECK(result2 == "10, 16"); - - // 複数の16進数 - std::string result3 = nodec::Formatter() << std::hex << 10 << " " << 15 << " " << 255; - CHECK(result3 == "a f ff"); -} - -TEST_CASE("Testing Formatter - Reserve Functionality") { - // 事前リザーブ - nodec::Formatter formatter(100); - formatter << "This is a test string that might be long"; - std::string result = formatter.str(); - CHECK(result == "This is a test string that might be long"); - - // クリア機能 - formatter.clear(); - formatter << "New content"; - CHECK(formatter.str() == "New content"); -} - -TEST_CASE("Testing Formatter - Utility Methods") { - nodec::Formatter formatter; - formatter << "Hello"; - - // サイズチェック - CHECK(formatter.size() == 5); - - // 追加リザーブ - formatter.reserve(100); - formatter << " World"; - CHECK(formatter.str() == "Hello World"); - - // クリア - formatter.clear(); - CHECK(formatter.size() == 0); - CHECK(formatter.str() == ""); -} - -TEST_CASE("Testing Formatter - Vector3 Fallback") { - // Vector3の文字列化テスト(ostream演算子フォールバック) - nodec::Vector3f vec3f(1.5f, 2.7f, 3.9f); - std::string result1 = nodec::Formatter() << "Vector3f: " << vec3f; - CHECK(result1 == "Vector3f: ( 1.5, 2.7, 3.9 )"); - - // Vector3iの文字列化テスト - nodec::Vector3i vec3i(10, 20, 30); - std::string result2 = nodec::Formatter() << "Position: " << vec3i << ", End"; - CHECK(result2 == "Position: ( 10, 20, 30 ), End"); - - // Vector3dの文字列化テスト - nodec::Vector3d vec3d(1.23456, 7.89012, 3.45678); - std::string result3 = nodec::Formatter() << vec3d; - CHECK(result3 == "( 1.23456, 7.89012, 3.45678 )"); - - // 複数のVector3を連結 - nodec::Vector3f pos(1.0f, 2.0f, 3.0f); - nodec::Vector3f dir(0.0f, 1.0f, 0.0f); - std::string result4 = nodec::Formatter() << "Pos: " << pos << ", Dir: " << dir; - CHECK(result4 == "Pos: ( 1, 2, 3 ), Dir: ( 0, 1, 0 )"); - - // ostringstream との比較確認 - std::ostringstream oss; - oss << "Vector3f: " << vec3f; - std::string oss_result = oss.str(); - CHECK(result1 == oss_result); // 同じ結果であることを確認 -} \ No newline at end of file +// TEST_CASE("Testing Formatter - Numeric Types") { +// // 様々な整数型 +// std::string result1 = nodec::Formatter() << static_cast(123); +// CHECK(result1 == "123"); + +// std::string result2 = nodec::Formatter() << static_cast(456L); +// CHECK(result2 == "456"); + +// std::string result3 = nodec::Formatter() << static_cast(789LL); +// CHECK(result3 == "789"); + +// // 負の数 +// std::string result4 = nodec::Formatter() << -42; +// CHECK(result4 == "-42"); + +// // ゼロ +// std::string result5 = nodec::Formatter() << 0; +// CHECK(result5 == "0"); +// } + +// TEST_CASE("Testing Formatter - Hex Mode") { +// // 16進数モード +// std::string result1 = nodec::Formatter() << "Hex: " << std::hex << 255; +// CHECK(result1 == "Hex: ff"); + +// // 10進数に戻す +// std::string result2 = nodec::Formatter() << std::hex << 16 << ", " << std::dec << 16; +// CHECK(result2 == "10, 16"); + +// // 複数の16進数 +// std::string result3 = nodec::Formatter() << std::hex << 10 << " " << 15 << " " << 255; +// CHECK(result3 == "a f ff"); +// } + +// // TEST_CASE("Testing Formatter - Reserve Functionality") { +// // // 事前リザーブ +// // nodec::Formatter formatter(100); +// // formatter << "This is a test string that might be long"; +// // std::string result = formatter.str(); +// // CHECK(result == "This is a test string that might be long"); + +// // // クリア機能 +// // formatter.clear(); +// // formatter << "New content"; +// // CHECK(formatter.str() == "New content"); +// // } + +// // TEST_CASE("Testing Formatter - Utility Methods") { +// // nodec::Formatter formatter; +// // formatter << "Hello"; + +// // // サイズチェック +// // CHECK(formatter.size() == 5); + +// // // 追加リザーブ +// // formatter.reserve(100); +// // formatter << " World"; +// // CHECK(formatter.str() == "Hello World"); + +// // // クリア +// // formatter.clear(); +// // CHECK(formatter.size() == 0); +// // CHECK(formatter.str() == ""); +// // } + +// TEST_CASE("Testing Formatter - Vector3 Fallback") { +// // Vector3の文字列化テスト(ostream演算子フォールバック) +// nodec::Vector3f vec3f(1.5f, 2.7f, 3.9f); +// std::string result1 = nodec::Formatter() << "Vector3f: " << vec3f; +// CHECK(result1 == "Vector3f: ( 1.5, 2.7, 3.9 )"); + +// // Vector3iの文字列化テスト +// nodec::Vector3i vec3i(10, 20, 30); +// std::string result2 = nodec::Formatter() << "Position: " << vec3i << ", End"; +// CHECK(result2 == "Position: ( 10, 20, 30 ), End"); + +// // Vector3dの文字列化テスト +// nodec::Vector3d vec3d(1.23456, 7.89012, 3.45678); +// std::string result3 = nodec::Formatter() << vec3d; +// CHECK(result3 == "( 1.23456, 7.89012, 3.45678 )"); + +// // 複数のVector3を連結 +// nodec::Vector3f pos(1.0f, 2.0f, 3.0f); +// nodec::Vector3f dir(0.0f, 1.0f, 0.0f); +// std::string result4 = nodec::Formatter() << "Pos: " << pos << ", Dir: " << dir; +// CHECK(result4 == "Pos: ( 1, 2, 3 ), Dir: ( 0, 1, 0 )"); + +// // ostringstream との比較確認 +// std::ostringstream oss; +// oss << "Vector3f: " << vec3f; +// std::string oss_result = oss.str(); +// CHECK(result1 == oss_result); // 同じ結果であることを確認 +// } \ No newline at end of file diff --git a/tests/formatter/formatter_benchmark.cpp b/tests/formatter/formatter_benchmark.cpp index 291d8e8..1ad1e21 100644 --- a/tests/formatter/formatter_benchmark.cpp +++ b/tests/formatter/formatter_benchmark.cpp @@ -3,21 +3,21 @@ #include #include -#include #include #include #include +#include +#include #include -#include // ベンチマーク測定用のヘルパークラス class BenchmarkTimer { public: - BenchmarkTimer(const std::string& name) : name_(name) { + BenchmarkTimer(const std::string &name): name_(name) { start_time_ = std::chrono::high_resolution_clock::now(); } - + ~BenchmarkTimer() { auto end_time = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end_time - start_time_); @@ -31,20 +31,20 @@ class BenchmarkTimer { TEST_CASE("Benchmark - Simple Concatenation") { const int iterations = 100000; - + std::cout << "\n--- Simple Concatenation Test ---" << std::endl; - - // nodec::Formatterのベンチマーク + + // nodec::FormatterLegacyのベンチマーク { - BenchmarkTimer timer("nodec::Formatter - Simple concatenation"); + BenchmarkTimer timer("nodec::FormatterLegacy - Simple concatenation"); for (int i = 0; i < iterations; ++i) { - std::string result = nodec::Formatter() << "Hello" << " " << "World" << i; + std::string result = nodec::FormatterLegacy() << "Hello" << " " << "World" << i; // 結果を使用して最適化を防ぐ volatile auto len = result.length(); (void)len; } } - + // std::ostringstreamのベンチマーク(比較用) { BenchmarkTimer timer("std::ostringstream - Simple concatenation"); @@ -57,33 +57,21 @@ TEST_CASE("Benchmark - Simple Concatenation") { (void)len; } } - - // nodec::optimized_ostringstream (FastFormatter)のベンチマーク - { - BenchmarkTimer timer("FastFormatter - Simple concatenation"); - for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream oss; - oss << "Hello" << " " << "World" << i; - std::string result = oss.str(); - // 結果を使用して最適化を防ぐ - volatile auto len = result.length(); - (void)len; - } - } - - // FastFormatter view() - コピーなし + + // nodec::StringBuilderStreamのベンチマーク { - BenchmarkTimer timer("FastFormatter view() - Simple concatenation"); + BenchmarkTimer timer("StringBuilderStream - Simple concatenation"); for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream oss; - oss << "Hello" << " " << "World" << i; - std::string_view result = oss.view(); // No copy + std::string result; + nodec::StringBuilderStream builder(result); + + builder << "Hello" << " " << "World" << i; // 結果を使用して最適化を防ぐ volatile auto len = result.length(); (void)len; } } - + // std::string + 演算子のベンチマーク(比較用) { BenchmarkTimer timer("std::string + operator - Simple concatenation"); @@ -94,52 +82,40 @@ TEST_CASE("Benchmark - Simple Concatenation") { (void)len; } } - + // テストが正常に実行されたことを確認 CHECK(true); } TEST_CASE("Benchmark - Numeric Formatting") { const int iterations = 100000; - + std::cout << "\n--- Numeric Formatting Test ---" << std::endl; - - // nodec::Formatterのベンチマーク - { - BenchmarkTimer timer("nodec::Formatter - Numeric formatting"); - for (int i = 0; i < iterations; ++i) { - std::string result = nodec::Formatter() << "Number: " << i - << ", Float: " << (i * 3.14) - << ", Hex: " << std::hex << i; - volatile auto len = result.length(); - (void)len; - } - } - - // nodec::optimized_ostringstream (FastFormatter)のベンチマーク + + // nodec::FormatterLegacyのベンチマーク { - BenchmarkTimer timer("FastFormatter - Numeric formatting"); + BenchmarkTimer timer("nodec::FormatterLegacy - Numeric formatting"); for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream oss; - oss << "Number: " << i << ", Float: " << (i * 3.14) << ", Hex: " << std::hex << i; - std::string result = oss.str(); + std::string result = nodec::FormatterLegacy() << "Number: " << i + << ", Float: " << (i * 3.14) + << ", Hex: " << std::hex << i; volatile auto len = result.length(); (void)len; } } - - // FastFormatter view() - コピーなし + + // nodec::FormatterStreamのベンチマーク { - BenchmarkTimer timer("FastFormatter view() - Numeric formatting"); + BenchmarkTimer timer("StringBuilderStream - Numeric formatting"); for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream oss; - oss << "Number: " << i << ", Float: " << (i * 3.14) << ", Hex: " << std::hex << i; - std::string_view result = oss.view(); // No copy + std::string result; + nodec::StringBuilderStream builder(result); + builder << "Number: " << i << ", Float: " << (i * 3.14) << ", Hex: " << std::hex << i; volatile auto len = result.length(); (void)len; } } - + // std::ostringstreamのベンチマーク(比較用) { BenchmarkTimer timer("std::ostringstream - Numeric formatting"); @@ -151,68 +127,50 @@ TEST_CASE("Benchmark - Numeric Formatting") { (void)len; } } - + // テストが正常に実行されたことを確認 CHECK(true); } TEST_CASE("Benchmark - Long Chain") { const int iterations = 10000; - + std::cout << "\n--- Long Chain Test ---" << std::endl; - - // nodec::Formatterのベンチマーク - 長いチェーン - { - BenchmarkTimer timer("nodec::Formatter - Long chain"); - for (int i = 0; i < iterations; ++i) { - std::string result = nodec::Formatter() << "Start: " << i - << ", Value1: " << (i * 2) - << ", Value2: " << (i * 3.5) - << ", Value3: " << (i % 100) - << ", Value4: " << std::hex << i - << ", Value5: " << std::dec << (i + 1000) - << ", End"; - volatile auto len = result.length(); - (void)len; - } - } - - // nodec::optimized_ostringstream (FastFormatter)のベンチマーク + + // nodec::FormatterLegacyのベンチマーク - 長いチェーン { - BenchmarkTimer timer("FastFormatter - Long chain"); + BenchmarkTimer timer("nodec::FormatterLegacy - Long chain"); for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream oss; - oss << "Start: " << i - << ", Value1: " << (i * 2) - << ", Value2: " << (i * 3.5) - << ", Value3: " << (i % 100) - << ", Value4: " << std::hex << i - << ", Value5: " << std::dec << (i + 1000) - << ", End"; - std::string result = oss.str(); + std::string result = nodec::FormatterLegacy() << "Start: " << i + << ", Value1: " << (i * 2) + << ", Value2: " << (i * 3.5) + << ", Value3: " << (i % 100) + << ", Value4: " << std::hex << i + << ", Value5: " << std::dec << (i + 1000) + << ", End"; volatile auto len = result.length(); (void)len; } } - - // FastFormatter view() - コピーなし + + // StringBuilderStreamのベンチマーク { - BenchmarkTimer timer("FastFormatter view() - Long chain"); + BenchmarkTimer timer("StringBuilderStream - Long chain"); for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream oss; - oss << "Start: " << i - << ", Value1: " << (i * 2) - << ", Value2: " << (i * 3.5) - << ", Value3: " << (i % 100) - << ", Value4: " << std::hex << i - << ", Value5: " << std::dec << (i + 1000) - << ", End"; - std::string_view result = oss.view(); // No copy + std::string result; + nodec::StringBuilderStream builder(result); + builder << "Start: " << i + << ", Value1: " << (i * 2) + << ", Value2: " << (i * 3.5) + << ", Value3: " << (i % 100) + << ", Value4: " << std::hex << i + << ", Value5: " << std::dec << (i + 1000) + << ", End"; volatile auto len = result.length(); (void)len; } } - + // std::ostringstreamのベンチマーク(比較用) { BenchmarkTimer timer("std::ostringstream - Long chain"); @@ -230,253 +188,23 @@ TEST_CASE("Benchmark - Long Chain") { (void)len; } } - + // std::string + 演算子のベンチマーク(比較用) { BenchmarkTimer timer("std::string + operator - Long chain"); for (int i = 0; i < iterations; ++i) { std::string result = std::string("Start: ") + std::to_string(i) - + ", Value1: " + std::to_string(i * 2) - + ", Value2: " + std::to_string(i * 3.5) - + ", Value3: " + std::to_string(i % 100) - + ", Value4: " + std::to_string(i) // 16進数は複雑なので10進数で代用 - + ", Value5: " + std::to_string(i + 1000) - + ", End"; + + ", Value1: " + std::to_string(i * 2) + + ", Value2: " + std::to_string(i * 3.5) + + ", Value3: " + std::to_string(i % 100) + + ", Value4: " + std::to_string(i) // 16進数は複雑なので10進数で代用 + + ", Value5: " + std::to_string(i + 1000) + + ", End"; volatile auto len = result.length(); (void)len; } } - - // テストが正常に実行されたことを確認 - CHECK(true); -} -TEST_CASE("Benchmark - Memory Usage") { - const int iterations = 50000; - - std::cout << "\n--- Memory Usage Test ---" << std::endl; - - // 大量のFormatterオブジェクトを作成してメモリ使用量をテスト - { - BenchmarkTimer timer("nodec::Formatter - Multiple instances"); - std::vector results; - results.reserve(iterations); - - for (int i = 0; i < iterations; ++i) { - results.push_back(nodec::Formatter() << "Test " << i << " with value " << (i * 2.5)); - } - - // 結果を使用して最適化を防ぐ - volatile auto total_size = results.size(); - (void)total_size; - } - - // nodec::optimized_ostringstream (FastFormatter)のベンチマーク - { - BenchmarkTimer timer("FastFormatter - Multiple instances"); - std::vector results; - results.reserve(iterations); - - for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream oss; - oss << "Test " << i << " with value " << (i * 2.5); - results.push_back(oss.str()); - } - - // 結果を使用して最適化を防ぐ - volatile auto total_size = results.size(); - (void)total_size; - } - - // FastFormatter view() - string_viewのベクタ(参考用) - { - BenchmarkTimer timer("FastFormatter view() - Multiple instances (view only)"); - std::vector streams; - streams.reserve(iterations); - - for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream oss; - oss << "Test " << i << " with value " << (i * 2.5); - streams.push_back(std::move(oss)); - } - - // string_viewとして結果を取得(実際の使用例) - for (const auto& oss : streams) { - std::string_view result = oss.view(); - volatile auto len = result.length(); - (void)len; - } - - // 結果を使用して最適化を防ぐ - volatile auto total_size = streams.size(); - (void)total_size; - } - - { - BenchmarkTimer timer("std::ostringstream - Multiple instances"); - std::vector results; - results.reserve(iterations); - - for (int i = 0; i < iterations; ++i) { - std::ostringstream oss; - oss << "Test " << i << " with value " << (i * 2.5); - results.push_back(oss.str()); - } - - // 結果を使用して最適化を防ぐ - volatile auto total_size = results.size(); - (void)total_size; - } - - // テストが正常に実行されたことを確認 - CHECK(true); -} - -// doctestが自動的にテストを実行するため、main関数は不要 - -TEST_CASE("Benchmark - Reserve Capacity") { - const int iterations = 50000; - - std::cout << "\n--- Reserve Capacity Test ---" << std::endl; - - // FastFormatter with reserve - { - BenchmarkTimer timer("FastFormatter with reserve - Long text"); - for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream oss(256); // Pre-reserve capacity - oss << "This is a longer text example that will test the reserve functionality: " - << i << ", value1: " << (i * 2) << ", value2: " << (i * 3.14) - << ", value3: " << std::hex << i << ", value4: " << std::dec << (i + 100) - << " - End of the long text"; - std::string result = oss.str(); - volatile auto len = result.length(); - (void)len; - } - } - - // FastFormatter with reserve + view() - { - BenchmarkTimer timer("FastFormatter view() with reserve - Long text"); - for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream oss(256); // Pre-reserve capacity - oss << "This is a longer text example that will test the reserve functionality: " - << i << ", value1: " << (i * 2) << ", value2: " << (i * 3.14) - << ", value3: " << std::hex << i << ", value4: " << std::dec << (i + 100) - << " - End of the long text"; - std::string_view result = oss.view(); // No copy - volatile auto len = result.length(); - (void)len; - } - } - - // FastFormatter without reserve (for comparison) - { - BenchmarkTimer timer("FastFormatter without reserve - Long text"); - for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream oss; // No pre-reserve - oss << "This is a longer text example that will test the reserve functionality: " - << i << ", value1: " << (i * 2) << ", value2: " << (i * 3.14) - << ", value3: " << std::hex << i << ", value4: " << std::dec << (i + 100) - << " - End of the long text"; - std::string result = oss.str(); - volatile auto len = result.length(); - (void)len; - } - } - - // FastFormatter without reserve + view() - { - BenchmarkTimer timer("FastFormatter view() without reserve - Long text"); - for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream oss; // No pre-reserve - oss << "This is a longer text example that will test the reserve functionality: " - << i << ", value1: " << (i * 2) << ", value2: " << (i * 3.14) - << ", value3: " << std::hex << i << ", value4: " << std::dec << (i + 100) - << " - End of the long text"; - std::string_view result = oss.view(); // No copy - volatile auto len = result.length(); - (void)len; - } - } - - // std::ostringstream (for comparison) - { - BenchmarkTimer timer("std::ostringstream - Long text"); - for (int i = 0; i < iterations; ++i) { - std::ostringstream oss; - oss << "This is a longer text example that will test the reserve functionality: " - << i << ", value1: " << (i * 2) << ", value2: " << (i * 3.14) - << ", value3: " << std::hex << i << ", value4: " << std::dec << (i + 100) - << " - End of the long text"; - std::string result = oss.str(); - volatile auto len = result.length(); - (void)len; - } - } - - // テストが正常に実行されたことを確認 - CHECK(true); -} - -TEST_CASE("Benchmark - String View Performance") { - const int iterations = 100000; - - std::cout << "\n--- String View Performance Test ---" << std::endl; - - // FastFormatter view() vs str() comparison - { - BenchmarkTimer timer("FastFormatter view() - No copy"); - for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream oss; - oss << "Test string " << i << " with some content"; - std::string_view result = oss.view(); // No copy - // 結果を使用して最適化を防ぐ - volatile auto len = result.length(); - (void)len; - } - } - - { - BenchmarkTimer timer("FastFormatter str() - With copy"); - for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream oss; - oss << "Test string " << i << " with some content"; - std::string result = oss.str(); // Copy - // 結果を使用して最適化を防ぐ - volatile auto len = result.length(); - (void)len; - } - } - - // Long string performance comparison - { - BenchmarkTimer timer("FastFormatter view() - Long strings"); - for (int i = 0; i < iterations / 10; ++i) { - nodec::optimized_ostringstream oss(1024); - oss << "This is a much longer test string that contains more data to demonstrate " - << "the performance benefits of using string_view instead of copying the entire string. " - << "Iteration number: " << i << ", additional data: " << (i * 3.14159) - << ", more content to make this string significantly longer and test memory efficiency."; - std::string_view result = oss.view(); // No copy - volatile auto len = result.length(); - (void)len; - } - } - - { - BenchmarkTimer timer("FastFormatter str() - Long strings"); - for (int i = 0; i < iterations / 10; ++i) { - nodec::optimized_ostringstream oss(1024); - oss << "This is a much longer test string that contains more data to demonstrate " - << "the performance benefits of using string_view instead of copying the entire string. " - << "Iteration number: " << i << ", additional data: " << (i * 3.14159) - << ", more content to make this string significantly longer and test memory efficiency."; - std::string result = oss.str(); // Copy - volatile auto len = result.length(); - (void)len; - } - } - // テストが正常に実行されたことを確認 CHECK(true); } diff --git a/tests/formatter/formatter_detailed_benchmark.cpp b/tests/formatter/formatter_detailed_benchmark.cpp deleted file mode 100644 index cf64de7..0000000 --- a/tests/formatter/formatter_detailed_benchmark.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -#include - -#include -#include -#include -#include - -#include - -TEST_CASE("Detailed Benchmark - Construction Overhead") { - std::cout << "\n--- Construction Overhead Test ---" << std::endl; - const int iterations = 1000000; - - // Formatterオブジェクトの構築コスト - auto start = std::chrono::high_resolution_clock::now(); - for (int i = 0; i < iterations; ++i) { - nodec::Formatter formatter; - volatile auto* ptr = &formatter; - (void)ptr; - } - auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - std::cout << "Formatter construction: " << duration.count() << " microseconds" << std::endl; - - // ostringstream構築コスト(比較用) - start = std::chrono::high_resolution_clock::now(); - for (int i = 0; i < iterations; ++i) { - std::ostringstream oss; - volatile auto* ptr = &oss; - (void)ptr; - } - end = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration_cast(end - start); - std::cout << "ostringstream construction: " << duration.count() << " microseconds" << std::endl; - - CHECK(true); -} - -TEST_CASE("Detailed Benchmark - String Types") { - std::cout << "\n--- String Types Test ---" << std::endl; - const int iterations = 100000; - - // 異なる文字列型での性能テスト - std::string std_string = "Hello World"; - const char* c_string = "Hello World"; - - // std::string - auto start = std::chrono::high_resolution_clock::now(); - for (int i = 0; i < iterations; ++i) { - std::string result = nodec::Formatter() << std_string << i; - volatile auto len = result.length(); - (void)len; - } - auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - std::cout << "std::string input: " << duration.count() << " microseconds" << std::endl; - - // const char* - start = std::chrono::high_resolution_clock::now(); - for (int i = 0; i < iterations; ++i) { - std::string result = nodec::Formatter() << c_string << i; - volatile auto len = result.length(); - (void)len; - } - end = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration_cast(end - start); - std::cout << "const char* input: " << duration.count() << " microseconds" << std::endl; - - CHECK(true); -} - -TEST_CASE("Detailed Benchmark - Error Formatter") { - std::cout << "\n--- Error Formatter Test ---" << std::endl; - const int iterations = 50000; - - auto start = std::chrono::high_resolution_clock::now(); - for (int i = 0; i < iterations; ++i) { - std::string result = nodec::ErrorFormatter(__FILE__, __LINE__) - << "Error occurred with value: " << i; - volatile auto len = result.length(); - (void)len; - } - auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - std::cout << "ErrorFormatter: " << duration.count() << " microseconds" << std::endl; - - CHECK(true); -} - -TEST_CASE("Detailed Benchmark - Multiple Instances") { - std::cout << "\n--- Multiple Instances Test ---" << std::endl; - // 注意: このテストは実際の並行性テストではなく、 - // 単一スレッドでの複数インスタンス作成テストです - - const int iterations = 10000; - std::vector results; - results.reserve(iterations * 3); - - auto start = std::chrono::high_resolution_clock::now(); - for (int i = 0; i < iterations; ++i) { - // 複数のFormatterを同時に作成 - nodec::Formatter f1, f2, f3; - f1 << "Thread1: " << i; - f2 << "Thread2: " << (i * 2); - f3 << "Thread3: " << (i * 3); - - results.push_back(f1); - results.push_back(f2); - results.push_back(f3); - } - auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - std::cout << "Multiple instances: " << duration.count() << " microseconds" << std::endl; - - // 結果を使用して最適化を防ぐ - volatile auto total_size = results.size(); - (void)total_size; - - CHECK(true); -} diff --git a/tests/formatter/formatter_ostringstream_compat.cpp b/tests/formatter/formatter_ostringstream_compat.cpp index 2a246ae..8977b7f 100644 --- a/tests/formatter/formatter_ostringstream_compat.cpp +++ b/tests/formatter/formatter_ostringstream_compat.cpp @@ -1,136 +1,180 @@ #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include -#include #include +#include #include -TEST_CASE("Formatter - Enhanced Manipulator Support") { +TEST_CASE("StringBuilderStream - Enhanced Manipulator Support") { // setw テスト - std::string result1 = nodec::Formatter() << nodec::setw(10) << 42; - CHECK(result1 == " 42"); // 右揃え、幅10 - + std::string buffer1; + nodec::StringBuilderStream stream1(buffer1); + stream1 << std::setw(10) << 42; + CHECK(buffer1 == " 42"); // 右揃え、幅10 + // setfill テスト - std::string result2 = nodec::Formatter() << nodec::setfill('0') << nodec::setw(5) << 123; - CHECK(result2 == "00123"); - + std::string buffer2; + nodec::StringBuilderStream stream2(buffer2); + stream2 << std::setfill('0') << std::setw(5) << 123; + CHECK(buffer2 == "00123"); + // setprecision テスト - std::string result3 = nodec::Formatter() << std::fixed << nodec::setprecision(2) << 3.14159; - CHECK(result3 == "3.14"); - + std::string buffer3; + nodec::StringBuilderStream stream3(buffer3); + stream3 << std::fixed << std::setprecision(2) << 3.14159; + CHECK(buffer3 == "3.14"); + // 組み合わせテスト - std::string result4 = nodec::Formatter() << nodec::setfill('*') << nodec::setw(8) << "Hi"; - CHECK(result4 == "******Hi"); + std::string buffer4; + nodec::StringBuilderStream stream4(buffer4); + stream4 << std::setfill('*') << std::setw(8) << "Hi"; + CHECK(buffer4 == "******Hi"); } -TEST_CASE("Formatter - Number Base Support") { +TEST_CASE("StringBuilderStream - Number Base Support") { // 16進数テスト(小文字) - std::string result1 = nodec::Formatter() << std::hex << 255; - CHECK(result1 == "ff"); - + std::string buffer1; + nodec::StringBuilderStream stream1(buffer1); + stream1 << std::hex << 255; + CHECK(buffer1 == "ff"); + // 16進数テスト(大文字) - std::string result2 = nodec::Formatter() << std::hex << std::uppercase << 255; - CHECK(result2 == "FF"); - + std::string buffer2; + nodec::StringBuilderStream stream2(buffer2); + stream2 << std::hex << std::uppercase << 255; + CHECK(buffer2 == "FF"); + // 16進数 with showbase - std::string result3 = nodec::Formatter() << std::hex << std::showbase << 255; - CHECK(result3 == "0xff"); - + std::string buffer3; + nodec::StringBuilderStream stream3(buffer3); + stream3 << std::hex << std::showbase << 255; + CHECK(buffer3 == "0xff"); + // 8進数テスト - std::string result4 = nodec::Formatter() << std::oct << 64; - CHECK(result4 == "100"); - + std::string buffer4; + nodec::StringBuilderStream stream4(buffer4); + stream4 << std::oct << 64; + CHECK(buffer4 == "100"); + // 8進数 with showbase - std::string result5 = nodec::Formatter() << std::oct << std::showbase << 64; - CHECK(result5 == "0100"); - + std::string buffer5; + nodec::StringBuilderStream stream5(buffer5); + stream5 << std::oct << std::showbase << 64; + CHECK(buffer5 == "0100"); + // 10進数に戻す - std::string result6 = nodec::Formatter() << std::hex << 16 << ", " << std::dec << 16; - CHECK(result6 == "10, 16"); + std::string buffer6; + nodec::StringBuilderStream stream6(buffer6); + stream6 << std::hex << 16 << ", " << std::dec << 16; + CHECK(buffer6 == "10, 16"); } -TEST_CASE("Formatter - Alignment Support") { +TEST_CASE("StringBuilderStream - Alignment Support") { // 左揃え - std::string result1 = nodec::Formatter() << std::left << nodec::setw(8) << "Hi"; - CHECK(result1 == "Hi "); - + std::string buffer1; + nodec::StringBuilderStream stream1(buffer1); + stream1 << std::left << std::setw(8) << "Hi"; + CHECK(buffer1 == "Hi "); + // 右揃え(デフォルト) - std::string result2 = nodec::Formatter() << std::right << nodec::setw(8) << "Hi"; - CHECK(result2 == " Hi"); - + std::string buffer2; + nodec::StringBuilderStream stream2(buffer2); + stream2 << std::right << std::setw(8) << "Hi"; + CHECK(buffer2 == " Hi"); + // 内部揃え(符号付き数値) - std::string result3 = nodec::Formatter() << std::internal << nodec::setfill('0') << nodec::setw(8) << -42; - CHECK(result3 == "-0000042"); - + std::string buffer3; + nodec::StringBuilderStream stream3(buffer3); + stream3 << std::internal << std::setfill('0') << std::setw(8) << -42; + CHECK(buffer3 == "-0000042"); + // 内部揃え(16進数) - std::string result4 = nodec::Formatter() << std::hex << std::showbase << std::internal - << nodec::setfill('0') << nodec::setw(8) << 255; - CHECK(result4 == "0x0000ff"); + std::string buffer4; + nodec::StringBuilderStream stream4(buffer4); + stream4 << std::hex << std::showbase << std::internal + << std::setfill('0') << std::setw(8) << 255; + CHECK(buffer4 == "0x0000ff"); } -TEST_CASE("Formatter - Bool Support") { +TEST_CASE("StringBuilderStream - Bool Support") { // 数値としてのbool - std::string result1 = nodec::Formatter() << true << ", " << false; - CHECK(result1 == "1, 0"); - + std::string buffer1; + nodec::StringBuilderStream stream1(buffer1); + stream1 << true << ", " << false; + CHECK(buffer1 == "1, 0"); + // boolalpha - std::string result2 = nodec::Formatter() << std::boolalpha << true << ", " << false; - CHECK(result2 == "true, false"); - + std::string buffer2; + nodec::StringBuilderStream stream2(buffer2); + stream2 << std::boolalpha << true << ", " << false; + CHECK(buffer2 == "true, false"); + // noboolalpha - std::string result3 = nodec::Formatter() << std::boolalpha << true << ", " - << std::noboolalpha << false; - CHECK(result3 == "true, 0"); + std::string buffer3; + nodec::StringBuilderStream stream3(buffer3); + stream3 << std::boolalpha << true << ", " + << std::noboolalpha << false; + CHECK(buffer3 == "true, 0"); } -TEST_CASE("Formatter - Float Formatting") { +TEST_CASE("StringBuilderStream - Float Formatting") { // fixed フォーマット - std::string result1 = nodec::Formatter() << std::fixed << nodec::setprecision(3) << 3.14159; - CHECK(result1 == "3.142"); - + std::string buffer1; + nodec::StringBuilderStream stream1(buffer1); + stream1 << std::fixed << std::setprecision(3) << 3.14159; + CHECK(buffer1 == "3.142"); + // scientific フォーマット - std::string result2 = nodec::Formatter() << std::scientific << nodec::setprecision(2) << 1234.5; - CHECK(result2 == "1.23e+03"); - + std::string buffer2; + nodec::StringBuilderStream stream2(buffer2); + stream2 << std::scientific << std::setprecision(2) << 1234.5; + CHECK(buffer2 == "1.23e+03"); + // uppercase scientific - std::string result3 = nodec::Formatter() << std::scientific << std::uppercase - << nodec::setprecision(2) << 1234.5; - CHECK(result3 == "1.23E+03"); + std::string buffer3; + nodec::StringBuilderStream stream3(buffer3); + stream3 << std::scientific << std::uppercase + << std::setprecision(2) << 1234.5; + CHECK(buffer3 == "1.23E+03"); } -TEST_CASE("Formatter - ostringstream Compatibility Check") { +TEST_CASE("StringBuilderStream - ostringstream Compatibility Check") { // 同じフォーマットでostringreamと結果比較 int value = 42; - - // Formatter - std::string fmt_result = nodec::Formatter() << nodec::setfill('0') << nodec::setw(6) << value; - + + // StringBuilderStream + std::string buffer1; + nodec::StringBuilderStream stream1(buffer1); + stream1 << std::setfill('0') << std::setw(6) << value; + // ostringstream std::ostringstream oss; oss << std::setfill('0') << std::setw(6) << value; std::string oss_result = oss.str(); - - CHECK(fmt_result == oss_result); - + + CHECK(buffer1 == oss_result); + // 16進数比較 - std::string fmt_hex = nodec::Formatter() << std::hex << std::uppercase << 255; - + std::string buffer2; + nodec::StringBuilderStream stream2(buffer2); + stream2 << std::hex << std::uppercase << 255; + std::ostringstream oss_hex; oss_hex << std::hex << std::uppercase << 255; std::string oss_hex_result = oss_hex.str(); - - CHECK(fmt_hex == oss_hex_result); + + CHECK(buffer2 == oss_hex_result); } -TEST_CASE("Formatter - Complex Format Chains") { +TEST_CASE("StringBuilderStream - Complex Format Chains") { // 複雑なフォーマットチェーン - std::string result = nodec::Formatter() - << "Value: " << std::hex << std::uppercase << std::showbase - << nodec::setfill('0') << nodec::setw(8) << 255 - << ", Float: " << std::dec << std::fixed << nodec::setprecision(2) << 3.14159 - << ", Bool: " << std::boolalpha << true; - - // 期待値:"Value: 00000XFF, Float: 3.14, Bool: true" + std::string buffer; + nodec::StringBuilderStream stream(buffer); + stream << "Value: " << std::hex << std::uppercase << std::showbase + << std::setfill('0') << std::setw(8) << 255 + << ", Float: " << std::dec << std::fixed << std::setprecision(2) << 3.14159 + << ", Bool: " << std::boolalpha << true; + std::string expected = "Value: 00000XFF, Float: 3.14, Bool: true"; - CHECK(result == expected); + CHECK(buffer == expected); } diff --git a/tests/formatter/formatter_vector3_benchmark.cpp b/tests/formatter/formatter_vector3_benchmark.cpp deleted file mode 100644 index e3a2bc7..0000000 --- a/tests/formatter/formatter_vector3_benchmark.cpp +++ /dev/null @@ -1,243 +0,0 @@ -#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -#include - -#include -#include -#include -#include -#include - -#include -#include - -// ベンチマーク測定用のヘルパークラス -class BenchmarkTimer { -public: - BenchmarkTimer(const std::string& name) : name_(name) { - start_time_ = std::chrono::high_resolution_clock::now(); - } - - ~BenchmarkTimer() { - auto end_time = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end_time - start_time_); - std::cout << name_ << ": " << duration.count() << " microseconds" << std::endl; - } - -private: - std::string name_; - std::chrono::high_resolution_clock::time_point start_time_; -}; - -TEST_CASE("Benchmark - Vector3 Fallback Performance") { - const int iterations = 50000; - - std::cout << "\n--- Vector3 Fallback Performance Test ---" << std::endl; - - // テスト用のVector3データ - std::vector vectors; - vectors.reserve(iterations); - for (int i = 0; i < iterations; ++i) { - vectors.emplace_back(static_cast(i), static_cast(i * 2), static_cast(i * 3)); - } - - // nodec::Formatter(フォールバック使用) - { - BenchmarkTimer timer("nodec::Formatter - Vector3f fallback"); - std::vector results; - results.reserve(iterations); - - for (int i = 0; i < iterations; ++i) { - results.push_back(nodec::Formatter() << "Vec: " << vectors[i] << ", Index: " << i); - } - - // 結果を使用して最適化を防ぐ - volatile auto total_size = results.size(); - (void)total_size; - } - - // std::ostringstream(比較用) - { - BenchmarkTimer timer("std::ostringstream - Vector3f"); - std::vector results; - results.reserve(iterations); - - for (int i = 0; i < iterations; ++i) { - std::ostringstream oss; - oss << "Vec: " << vectors[i] << ", Index: " << i; - results.push_back(oss.str()); - } - - // 結果を使用して最適化を防ぐ - volatile auto total_size = results.size(); - (void)total_size; - } - - // std::string + 演算子(手動でVector3を文字列化) - { - BenchmarkTimer timer("std::string + operator - Manual Vector3f"); - std::vector results; - results.reserve(iterations); - - for (int i = 0; i < iterations; ++i) { - std::string result = std::string("Vec: ( ") - + std::to_string(vectors[i].x) + ", " - + std::to_string(vectors[i].y) + ", " - + std::to_string(vectors[i].z) + " ), Index: " - + std::to_string(i); - results.push_back(result); - } - - // 結果を使用して最適化を防ぐ - volatile auto total_size = results.size(); - (void)total_size; - } - - CHECK(true); -} - -TEST_CASE("Benchmark - Vector3 Types Comparison") { - const int iterations = 30000; - - std::cout << "\n--- Vector3 Types Performance Comparison ---" << std::endl; - - // Vector3f のベンチマーク - { - BenchmarkTimer timer("nodec::Formatter - Vector3f"); - nodec::Vector3f vec(1.5f, 2.7f, 3.9f); - - for (int i = 0; i < iterations; ++i) { - std::string result = nodec::Formatter() << "Float Vec: " << vec << " [" << i << "]"; - volatile auto len = result.length(); - (void)len; - } - } - - // Vector3i のベンチマーク - { - BenchmarkTimer timer("nodec::Formatter - Vector3i"); - nodec::Vector3i vec(15, 27, 39); - - for (int i = 0; i < iterations; ++i) { - std::string result = nodec::Formatter() << "Int Vec: " << vec << " [" << i << "]"; - volatile auto len = result.length(); - (void)len; - } - } - - // Vector3d のベンチマーク - { - BenchmarkTimer timer("nodec::Formatter - Vector3d"); - nodec::Vector3d vec(1.5, 2.7, 3.9); - - for (int i = 0; i < iterations; ++i) { - std::string result = nodec::Formatter() << "Double Vec: " << vec << " [" << i << "]"; - volatile auto len = result.length(); - (void)len; - } - } - - CHECK(true); -} - -TEST_CASE("Benchmark - Complex Vector3 Chains") { - const int iterations = 10000; - - std::cout << "\n--- Complex Vector3 Chain Test ---" << std::endl; - - // 複雑なVector3チェーンのベンチマーク - { - BenchmarkTimer timer("nodec::Formatter - Complex Vector3 chain"); - - for (int i = 0; i < iterations; ++i) { - nodec::Vector3f pos(static_cast(i), static_cast(i * 2), static_cast(i * 3)); - nodec::Vector3f vel(1.0f, 0.5f, -1.0f); - nodec::Vector3i id(i, i + 1, i + 2); - - std::string result = nodec::Formatter() - << "Entity[" << i << "] " - << "Pos: " << pos << ", " - << "Vel: " << vel << ", " - << "ID: " << id << ", " - << "Time: " << (i * 0.016f); - - volatile auto len = result.length(); - (void)len; - } - } - - // std::ostringstream での同等処理(比較用) - { - BenchmarkTimer timer("std::ostringstream - Complex Vector3 chain"); - - for (int i = 0; i < iterations; ++i) { - nodec::Vector3f pos(static_cast(i), static_cast(i * 2), static_cast(i * 3)); - nodec::Vector3f vel(1.0f, 0.5f, -1.0f); - nodec::Vector3i id(i, i + 1, i + 2); - - std::ostringstream oss; - oss << "Entity[" << i << "] " - << "Pos: " << pos << ", " - << "Vel: " << vel << ", " - << "ID: " << id << ", " - << "Time: " << (i * 0.016f); - - std::string result = oss.str(); - volatile auto len = result.length(); - (void)len; - } - } - - CHECK(true); -} - -TEST_CASE("Benchmark - Vector3 Memory Efficiency") { - const int iterations = 20000; - - std::cout << "\n--- Vector3 Memory Efficiency Test ---" << std::endl; - - // 大量のVector3文字列化でのメモリ効率テスト - { - BenchmarkTimer timer("nodec::Formatter - Vector3 memory test"); - std::vector results; - results.reserve(iterations); - - for (int i = 0; i < iterations; ++i) { - // 事前リザーブを使用 - nodec::Formatter formatter(64); // Vector3文字列は通常30-40文字程度 - - nodec::Vector3f vec( - static_cast(i * 1.1f), - static_cast(i * 2.2f), - static_cast(i * 3.3f) - ); - - formatter << "Vector[" << i << "]: " << vec << " magnitude"; - results.push_back(formatter.str()); - } - - volatile auto total_size = results.size(); - (void)total_size; - } - - // 事前リザーブなしでの比較 - { - BenchmarkTimer timer("nodec::Formatter - Vector3 without reserve"); - std::vector results; - results.reserve(iterations); - - for (int i = 0; i < iterations; ++i) { - nodec::Vector3f vec( - static_cast(i * 1.1f), - static_cast(i * 2.2f), - static_cast(i * 3.3f) - ); - - results.push_back(nodec::Formatter() << "Vector[" << i << "]: " << vec << " magnitude"); - } - - volatile auto total_size = results.size(); - (void)total_size; - } - - CHECK(true); -} diff --git a/tests/formatter/hex_debug.cpp b/tests/formatter/hex_debug.cpp deleted file mode 100644 index 764f88b..0000000 --- a/tests/formatter/hex_debug.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -#include -#include -#include -#include -#include - -TEST_CASE("Detailed hex format debugging") { - // Formatterでのテスト - nodec::Formatter fmt; - std::string result = fmt << std::hex << std::uppercase << std::showbase - << nodec::setfill('0') << nodec::setw(8) << 255; - std::cout << "Formatter result: '" << result << "'" << std::endl; - - // ostringstreamでのテスト - std::ostringstream oss; - oss << std::hex << std::uppercase << std::showbase << std::setfill('0') << std::setw(8) << 255; - std::string expected = oss.str(); - std::cout << "ostringstream result: '" << expected << "'" << std::endl; - - CHECK(result == expected); -} - -TEST_CASE("Step-by-step hex formatting") { - // ステップごとにフォーマットを確認 - nodec::Formatter fmt; - - // 1. 基本の16進数 - std::string result1 = nodec::Formatter() << std::hex << 255; - std::cout << "hex only: '" << result1 << "'" << std::endl; - - // 2. 大文字 - std::string result2 = nodec::Formatter() << std::hex << std::uppercase << 255; - std::cout << "hex + uppercase: '" << result2 << "'" << std::endl; - - // 3. showbaseを追加 - std::string result3 = nodec::Formatter() << std::hex << std::uppercase << std::showbase << 255; - std::cout << "hex + uppercase + showbase: '" << result3 << "'" << std::endl; - - // 4. setfillとsetwを追加 - std::string result4 = nodec::Formatter() << std::hex << std::uppercase << std::showbase - << nodec::setfill('0') << nodec::setw(8) << 255; - std::cout << "full format: '" << result4 << "'" << std::endl; -} diff --git a/tests/formatter/openmode_examples.cpp b/tests/formatter/openmode_examples.cpp deleted file mode 100644 index f7bc6dd..0000000 --- a/tests/formatter/openmode_examples.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include - -int main() { - std::cout << "=== openmode の実用例 ===" << std::endl; - - // 1. 通常の出力(デフォルト) - std::ostringstream oss1; - oss1 << "Hello"; - oss1 << " World"; - std::cout << "通常: " << oss1.str() << std::endl; // "Hello World" - - // 2. 追記モード - std::ostringstream oss2("Initial: ", std::ios_base::app); - oss2 << "Added"; - std::cout << "追記: " << oss2.str() << std::endl; // "Initial: Added" - - // 3. 初期文字列を削除して開始 - std::ostringstream oss3("ToBeDeleted", std::ios_base::out | std::ios_base::trunc); - oss3 << "New content"; - std::cout << "削除後: " << oss3.str() << std::endl; // "New content" - - // 4. 初期文字列を保持(デフォルト動作) - std::ostringstream oss4("Keep: "); - oss4 << "Added"; - std::cout << "保持: " << oss4.str() << std::endl; // "Keep: Added" - - return 0; -} diff --git a/tests/formatter/optimized_ostringstream_test.cpp b/tests/formatter/optimized_ostringstream_test.cpp deleted file mode 100644 index ac544b8..0000000 --- a/tests/formatter/optimized_ostringstream_test.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -#include - -#include -#include -#include -#include -#include - -TEST_CASE("Optimized ostringstream - Basic functionality") { - SUBCASE("Default construction and basic output") { - nodec::optimized_ostringstream oss; - oss << "Hello, World!"; - CHECK(oss.str() == "Hello, World!"); - } - - SUBCASE("Reserved construction") { - nodec::optimized_ostringstream oss(1024); - CHECK(oss.capacity() >= 1024); - - oss << "Reserved string"; - CHECK(oss.str() == "Reserved string"); - CHECK(oss.capacity() >= 1024); // 容量は保持される - } - - SUBCASE("Initial string with reserve") { - std::string initial = "Start: "; - nodec::optimized_ostringstream oss(initial, 512); - CHECK(oss.capacity() >= 512); - - oss << "Additional content"; - CHECK(oss.str() == "Start: Additional content"); - } -} - -TEST_CASE("Optimized ostringstream - Complete ostringstream compatibility") { - SUBCASE("Manipulators work exactly like ostringstream") { - // optimized_ostringstream - nodec::optimized_ostringstream oss; - oss << std::hex << std::uppercase << std::showbase - << std::setfill('0') << std::setw(8) << 255; - std::string result1 = oss.str(); - - // standard ostringstream - std::ostringstream std_oss; - std_oss << std::hex << std::uppercase << std::showbase - << std::setfill('0') << std::setw(8) << 255; - std::string result2 = std_oss.str(); - - CHECK(result1 == result2); - CHECK(result1 == "00000XFF"); // 期待される結果 - } - - SUBCASE("Float formatting") { - nodec::optimized_ostringstream oss; - oss << std::fixed << std::setprecision(2) << 3.14159; - - std::ostringstream std_oss; - std_oss << std::fixed << std::setprecision(2) << 3.14159; - - CHECK(oss.str() == std_oss.str()); - CHECK(oss.str() == "3.14"); - } - - SUBCASE("Boolean formatting") { - nodec::optimized_ostringstream oss; - oss << std::boolalpha << true << ", " << false; - - std::ostringstream std_oss; - std_oss << std::boolalpha << true << ", " << false; - - CHECK(oss.str() == std_oss.str()); - CHECK(oss.str() == "true, false"); - } -} - -TEST_CASE("Optimized ostringstream - Custom types support") { - SUBCASE("Vector3 support via ostream operator") { - nodec::Vector3f vec(1.0f, 2.0f, 3.0f); - - nodec::optimized_ostringstream oss; - oss << "Vector: " << vec; - - std::ostringstream std_oss; - std_oss << "Vector: " << vec; - - CHECK(oss.str() == std_oss.str()); - // Vector3のoperator<<が正しく呼ばれることを確認 - CHECK(oss.str().find("( 1, 2, 3 )") != std::string::npos); - } -} - -TEST_CASE("Optimized ostringstream - Performance features") { - SUBCASE("Reserve prevents reallocations") { - nodec::optimized_ostringstream oss(1000); - size_t initial_capacity = oss.capacity(); - - // 大量のデータを追加 - for (int i = 0; i < 100; ++i) { - oss << "Data chunk " << i << "; "; - } - - // 容量が維持されていることを確認(再配置が発生していない) - CHECK(oss.capacity() >= initial_capacity); - CHECK(oss.size() > 0); - } - - SUBCASE("Clear and reuse") { - nodec::optimized_ostringstream oss(512); - - oss << "First use"; - CHECK(oss.str() == "First use"); - - oss.clear_content(); - CHECK(oss.str().empty()); - CHECK(oss.capacity() >= 512); // 容量は保持 - - oss << "Second use"; - CHECK(oss.str() == "Second use"); - } -} - -TEST_CASE("Optimized ostringstream - Complex formatting chains") { - SUBCASE("Complex mixed formatting") { - nodec::optimized_ostringstream oss(256); - oss << "Value: " << std::hex << std::uppercase << std::showbase - << std::setfill('0') << std::setw(8) << 255 - << ", Float: " << std::dec << std::fixed << std::setprecision(2) << 3.14159 - << ", Bool: " << std::boolalpha << true; - - std::ostringstream std_oss; - std_oss << "Value: " << std::hex << std::uppercase << std::showbase - << std::setfill('0') << std::setw(8) << 255 - << ", Float: " << std::dec << std::fixed << std::setprecision(2) << 3.14159 - << ", Bool: " << std::boolalpha << true; - - CHECK(oss.str() == std_oss.str()); - CHECK(oss.str() == "Value: 00000XFF, Float: 3.14, Bool: true"); - } -} - -TEST_CASE("Optimized ostringstream - Move semantics") { - SUBCASE("Move construction") { - nodec::optimized_ostringstream oss1(256); - oss1 << "Original content"; - - auto oss2 = std::move(oss1); - CHECK(oss2.str() == "Original content"); - CHECK(oss2.capacity() >= 256); - } - - SUBCASE("Move assignment") { - nodec::optimized_ostringstream oss1(256); - oss1 << "Original content"; - - nodec::optimized_ostringstream oss2; - oss2 = std::move(oss1); - - CHECK(oss2.str() == "Original content"); - CHECK(oss2.capacity() >= 256); - } -} - -TEST_CASE("Optimized ostringstream - Conversion operators") { - SUBCASE("String conversion operator") { - nodec::optimized_ostringstream oss; - oss << "Test content"; - - std::string result = oss; // implicit conversion - CHECK(result == "Test content"); - - // 関数への渡し方も確認 - auto check_string = [](const std::string& s) { return s == "Test content"; }; - CHECK(check_string(oss)); - } -} - -// パフォーマンステスト(参考用) -TEST_CASE("Optimized ostringstream - Performance comparison") { - const int iterations = 1000; - const size_t reserve_size = 10000; - - SUBCASE("Reserved vs non-reserved performance indication") { - // このテストは実際のパフォーマンス測定ではなく、 - // APIが正しく動作することの確認 - - // 事前リザーブあり - nodec::optimized_ostringstream oss_reserved(reserve_size); - for (int i = 0; i < iterations; ++i) { - oss_reserved << "Item " << i << ": some content; "; - } - - // 事前リザーブなし - nodec::optimized_ostringstream oss_normal; - for (int i = 0; i < iterations; ++i) { - oss_normal << "Item " << i << ": some content; "; - } - - // 両方とも同じ結果を生成することを確認 - CHECK(oss_reserved.str() == oss_normal.str()); - CHECK(oss_reserved.capacity() >= reserve_size); - - // パフォーマンス上の利点は実際の使用で測定される - // ここでは機能の正確性のみを確認 - } -} diff --git a/tests/formatter/ostringstream_verification.cpp b/tests/formatter/ostringstream_verification.cpp deleted file mode 100644 index 20ab977..0000000 --- a/tests/formatter/ostringstream_verification.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -#include -#include -#include -#include - -TEST_CASE("ostringstream behavior verification") { - std::ostringstream oss; - oss << std::hex << std::uppercase << std::showbase << std::setfill('0') << std::setw(8) << 255; - std::string result = oss.str(); - std::cout << "ostringstream result: " << result << std::endl; - - // 期待される動作をここで確認 - CHECK(result.length() > 0); -} diff --git a/tests/formatter/overflow_xsputn_timing.cpp b/tests/formatter/overflow_xsputn_timing.cpp deleted file mode 100644 index 4c3f603..0000000 --- a/tests/formatter/overflow_xsputn_timing.cpp +++ /dev/null @@ -1,217 +0,0 @@ -#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -#include -#include -#include -#include -#include -#include - -// デバッグ用のstreambuf - 関数呼び出しを追跡 -template> -class debug_stringbuf : public nodec::basic_reserved_stringbuf { -public: - using base_type = nodec::basic_reserved_stringbuf; - using int_type = typename Traits::int_type; - using char_type = CharT; - using string_type = std::basic_string; - using size_type = typename string_type::size_type; - - debug_stringbuf() = default; - explicit debug_stringbuf(size_type reserve_size) : base_type(reserve_size) {} - - // overflow()呼び出しを追跡 - int_type overflow(int_type c = Traits::eof()) override { - std::cout << "📞 overflow() called with char: "; - if (!Traits::eq_int_type(c, Traits::eof())) { - char ch = Traits::to_char_type(c); - if (ch >= 32 && ch <= 126) { // 印刷可能文字 - std::cout << "'" << ch << "'"; - } else { - std::cout << "(non-printable:" << static_cast(ch) << ")"; - } - } else { - std::cout << "EOF"; - } - std::cout << " | Buffer size: " << this->size() - << " | Capacity: " << this->capacity() << std::endl; - - return base_type::overflow(c); - } - - // xsputn()呼び出しを追跡 - std::streamsize xsputn(const char_type *s, std::streamsize count) override { - std::cout << "📞 xsputn() called with " << count << " chars: \""; - // 最初の20文字まで表示 - for (std::streamsize i = 0; i < std::min(count, std::streamsize(20)); ++i) { - char ch = s[i]; - if (ch >= 32 && ch <= 126) { - std::cout << ch; - } else { - std::cout << "\\x" << std::hex << static_cast(ch) << std::dec; - } - } - if (count > 20) std::cout << "..."; - std::cout << "\" | Buffer size before: " << this->size() - << " | Capacity: " << this->capacity() << std::endl; - - auto result = base_type::xsputn(s, count); - - std::cout << " → After xsputn: Buffer size: " << this->size() - << " | Capacity: " << this->capacity() << std::endl; - - return result; - } - - // sync()呼び出しを追跡 - int sync() override { - std::cout << "📞 sync() called | Buffer size: " << this->size() << std::endl; - return base_type::sync(); - } -}; - -// デバッグ用のostringstream -template> -class debug_ostringstream : public std::basic_ostream { -public: - using base_type = std::basic_ostream; - using stringbuf_type = debug_stringbuf; - using string_type = std::basic_string; - using size_type = typename string_type::size_type; - - debug_ostringstream() : base_type(&buffer_), buffer_() {} - explicit debug_ostringstream(size_type reserve_size) - : base_type(&buffer_), buffer_(reserve_size) {} - - stringbuf_type* rdbuf() const { - return const_cast(&buffer_); - } - - string_type str() const { - return buffer_.str(); - } - -private: - stringbuf_type buffer_; -}; - -TEST_CASE("overflow() と xsputn() の呼び出しタイミング解説") { - std::cout << "\n=== overflow() と xsputn() 呼び出しタイミングの詳細解説 ===" << std::endl; - - std::cout << "\n【1. 単一文字の書き込み - overflow()が呼ばれる】" << std::endl; - { - debug_ostringstream dss; - std::cout << "初期状態 - Buffer size: " << dss.rdbuf()->size() - << ", Capacity: " << dss.rdbuf()->capacity() << std::endl; - - std::cout << "\n単一文字 'A' を書き込み:" << std::endl; - dss << 'A'; // 単一文字 → overflow()呼び出し - - std::cout << "\n単一文字 'B' を書き込み:" << std::endl; - dss << 'B'; // 単一文字 → overflow()呼び出し - } - - std::cout << "\n【2. 文字列の書き込み - xsputn()が呼ばれる】" << std::endl; - { - debug_ostringstream dss; - std::cout << "\n文字列 \"Hello\" を書き込み:" << std::endl; - dss << "Hello"; // 文字列 → xsputn()呼び出し - - std::cout << "\n文字列 \" World\" を書き込み:" << std::endl; - dss << " World"; // 文字列 → xsputn()呼び出し - } - - std::cout << "\n【3. 数値の書き込み - 内部で文字列変換後にxsputn()】" << std::endl; - { - debug_ostringstream dss; - std::cout << "\n整数 123 を書き込み:" << std::endl; - dss << 123; // 数値 → 内部でstr変換 → xsputn()呼び出し - - std::cout << "\n浮動小数点 3.14 を書き込み:" << std::endl; - dss << 3.14; // 浮動小数点 → 内部でstr変換 → xsputn()呼び出し - } - - std::cout << "\n【4. マニピュレータとの組み合わせ】" << std::endl; - { - debug_ostringstream dss; - std::cout << "\nstd::hex マニピュレータと数値:" << std::endl; - dss << std::hex << 255; // マニピュレータ設定後、数値変換 → xsputn() - - std::cout << "\nstd::setw(10) と文字列:" << std::endl; - dss << std::setw(10) << "test"; // 幅設定後、文字列 → xsputn() - } - - std::cout << "\n【5. バッファ容量不足時の動作】" << std::endl; - { - debug_ostringstream dss(5); // 小さな初期容量 - std::cout << "\n初期容量5で開始、長い文字列を書き込み:" << std::endl; - dss << "This is a very long string that exceeds initial capacity"; - // 容量不足 → xsputn()内でreserve()が呼ばれる - } - - std::cout << "\n【6. flush() と sync() の関係】" << std::endl; - { - debug_ostringstream dss; - dss << "Some content"; - std::cout << "\nflush() を明示的に呼び出し:" << std::endl; - dss.flush(); // flush() → sync()呼び出し - } - - REQUIRE(true); -} - -TEST_CASE("std::ostringstreamとの比較") { - std::cout << "\n=== std::ostringstream vs debug_ostringstream 動作比較 ===" << std::endl; - - std::cout << "\n【通常のstd::ostringstream】" << std::endl; - { - std::ostringstream oss; - oss << "Hello" << " " << 123 << " " << 'X'; - std::cout << "結果: \"" << oss.str() << "\"" << std::endl; - std::cout << "※内部関数呼び出しは見えない" << std::endl; - } - - std::cout << "\n【debug_ostringstream(関数呼び出し可視化)】" << std::endl; - { - debug_ostringstream dss; - dss << "Hello" << " " << 123 << " " << 'X'; - std::cout << "結果: \"" << dss.str() << "\"" << std::endl; - std::cout << "※上記のように内部関数呼び出しが見える" << std::endl; - } - - REQUIRE(true); -} - -TEST_CASE("パフォーマンス影響の実演") { - std::cout << "\n=== パフォーマンス影響の実演 ===" << std::endl; - - const int iterations = 1000; - - std::cout << "\n【文字を1個ずつ書き込み - overflow()多発】" << std::endl; - { - debug_ostringstream dss; - std::cout << "最初の5回の文字書き込み:" << std::endl; - for (int i = 0; i < 5; ++i) { - dss << static_cast('A' + i); - } - std::cout << "... (残り" << (iterations - 5) << "回は省略)" << std::endl; - - // 残りは静かに実行 - for (int i = 5; i < iterations; ++i) { - debug_ostringstream quiet_dss; - quiet_dss << static_cast('A' + (i % 26)); - } - } - - std::cout << "\n【文字列をまとめて書き込み - xsputn()効率的】" << std::endl; - { - debug_ostringstream dss; - std::cout << "1回の文字列書き込み:" << std::endl; - std::string long_str; - for (int i = 0; i < iterations; ++i) { - long_str += static_cast('A' + (i % 26)); - } - dss << long_str; // 一度にまとめて書き込み - } - - REQUIRE(true); -} diff --git a/tests/formatter/smart_formatter_analysis.cpp b/tests/formatter/smart_formatter_analysis.cpp deleted file mode 100644 index 1773247..0000000 --- a/tests/formatter/smart_formatter_analysis.cpp +++ /dev/null @@ -1,220 +0,0 @@ -#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -#include -#include -#include -#include -#include -#include - -using namespace std::chrono; - -// 改良版: 文字列長に応じて自動選択するバージョン -template> -class smart_optimized_ostringstream : public nodec::basic_optimized_ostringstream { -public: - using base_type = nodec::basic_optimized_ostringstream; - using string_type = typename base_type::string_type; - using string_view_type = typename base_type::string_view_type; - - // 基底クラスのコンストラクタを継承 - using base_type::base_type; - - // 文字列長に応じて最適な方法を自動選択 - auto get_optimal() const { - const auto size = this->rdbuf()->view().size(); - if (size > 100) { // 閾値: 100文字 - return this->view(); - } else { - return string_view_type(this->str()); // str()をstring_viewでラップ - } - } - - // コンパイル時選択版 - template - auto get_string() const { - if constexpr (PreferView) { - return this->view(); - } else { - return this->str(); - } - } -}; - -using SmartFormatter = smart_optimized_ostringstream<>; - -TEST_CASE("Smart Formatter Performance Test") { - const int iterations = 100000; - - std::cout << "=== Smart Formatter Analysis ===" << std::endl; - - // 短い文字列テスト - { - auto start = high_resolution_clock::now(); - for (int i = 0; i < iterations; ++i) { - SmartFormatter sf; - sf << "Test" << i; - auto result = sf.get_optimal(); // 自動選択 - (void)result.size(); // 使用することで最適化を防ぐ - } - auto end = high_resolution_clock::now(); - auto duration = duration_cast(end - start); - std::cout << "Smart Formatter (short strings): " << duration.count() << " microseconds" << std::endl; - } - - // 長い文字列テスト - { - auto start = high_resolution_clock::now(); - for (int i = 0; i < iterations / 10; ++i) { // 回数を減らして調整 - SmartFormatter sf; - sf << "This is a very long string that exceeds the threshold of 100 characters. "; - sf << "It contains multiple parts to make it definitely longer than the threshold. "; - sf << "Test number: " << i << " with additional padding to ensure length."; - auto result = sf.get_optimal(); // 自動選択 - (void)result.size(); - } - auto end = high_resolution_clock::now(); - auto duration = duration_cast(end - start); - std::cout << "Smart Formatter (long strings): " << duration.count() << " microseconds" << std::endl; - } - - // 比較: 従来のview()固定 - { - auto start = high_resolution_clock::now(); - for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream sf; - sf << "Test" << i; - auto result = sf.view(); - (void)result.size(); - } - auto end = high_resolution_clock::now(); - auto duration = duration_cast(end - start); - std::cout << "Regular view() (short strings): " << duration.count() << " microseconds" << std::endl; - } - - // 比較: 従来のstr()固定 - { - auto start = high_resolution_clock::now(); - for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream sf; - sf << "Test" << i; - auto result = sf.str(); - (void)result.size(); - } - auto end = high_resolution_clock::now(); - auto duration = duration_cast(end - start); - std::cout << "Regular str() (short strings): " << duration.count() << " microseconds" << std::endl; - } - - std::cout << std::endl; - - // テンプレート版のテスト - std::cout << "=== Template-based Selection ===" << std::endl; - - { - auto start = high_resolution_clock::now(); - for (int i = 0; i < iterations; ++i) { - SmartFormatter sf; - sf << "Test" << i; - auto result = sf.get_string(); // str() 相当 - (void)result.size(); - } - auto end = high_resolution_clock::now(); - auto duration = duration_cast(end - start); - std::cout << "Template-based str(): " << duration.count() << " microseconds" << std::endl; - } - - { - auto start = high_resolution_clock::now(); - for (int i = 0; i < iterations; ++i) { - SmartFormatter sf; - sf << "Test" << i; - auto result = sf.get_string(); // view() 相当 - (void)result.size(); - } - auto end = high_resolution_clock::now(); - auto duration = duration_cast(end - start); - std::cout << "Template-based view(): " << duration.count() << " microseconds" << std::endl; - } - - REQUIRE(true); // テストが正常に実行されたことを示す -} - -TEST_CASE("Memory Overhead Analysis") { - std::cout << "=== Memory Overhead Analysis ===" << std::endl; - - // メモリ使用量の比較 - std::cout << "sizeof(std::string): " << sizeof(std::string) << " bytes" << std::endl; - std::cout << "sizeof(std::string_view): " << sizeof(std::string_view) << " bytes" << std::endl; - std::cout << "sizeof(nodec::optimized_ostringstream): " << sizeof(nodec::optimized_ostringstream) << " bytes" << std::endl; - std::cout << "sizeof(SmartFormatter): " << sizeof(SmartFormatter) << " bytes" << std::endl; - - // 実際の文字列でのメモリ使用量テスト - { - std::string short_str = "Test123"; - std::string_view short_view = short_str; - - std::cout << "Short string - std::string size: " << short_str.size() - << ", capacity: " << short_str.capacity() << std::endl; - std::cout << "Short string - std::string_view size: " << short_view.size() << std::endl; - } - - { - std::string long_str(200, 'A'); // 200文字の長い文字列 - std::string_view long_view = long_str; - - std::cout << "Long string - std::string size: " << long_str.size() - << ", capacity: " << long_str.capacity() << std::endl; - std::cout << "Long string - std::string_view size: " << long_view.size() << std::endl; - } - - REQUIRE(true); -} - -TEST_CASE("Function Call Overhead Analysis") { - std::cout << "=== Function Call Overhead Analysis ===" << std::endl; - - const int iterations = 1000000; - nodec::optimized_ostringstream ss; - ss << "Test string for overhead analysis"; - - // str() 呼び出しオーバーヘッド - { - auto start = high_resolution_clock::now(); - for (int i = 0; i < iterations; ++i) { - auto result = ss.str(); - volatile auto len = result.size(); - (void)len; - } - auto end = high_resolution_clock::now(); - auto duration = duration_cast(end - start); - std::cout << "str() call overhead: " << duration.count() / iterations << " ns per call" << std::endl; - } - - // view() 呼び出しオーバーヘッド - { - auto start = high_resolution_clock::now(); - for (int i = 0; i < iterations; ++i) { - auto result = ss.view(); - volatile auto len = result.size(); - (void)len; - } - auto end = high_resolution_clock::now(); - auto duration = duration_cast(end - start); - std::cout << "view() call overhead: " << duration.count() / iterations << " ns per call" << std::endl; - } - - // 直接バッファアクセス(参考) - { - auto start = high_resolution_clock::now(); - for (int i = 0; i < iterations; ++i) { - auto buffer = ss.rdbuf()->str(); - volatile auto len = buffer.size(); - (void)len; - } - auto end = high_resolution_clock::now(); - auto duration = duration_cast(end - start); - std::cout << "Direct buffer access: " << duration.count() / iterations << " ns per call" << std::endl; - } - - REQUIRE(true); -} diff --git a/tests/formatter/traits_eof_analysis.cpp b/tests/formatter/traits_eof_analysis.cpp deleted file mode 100644 index e49fec9..0000000 --- a/tests/formatter/traits_eof_analysis.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -#include -#include -#include -#include - -// Traitsの動作を詳しく調べるテスト -TEST_CASE("Traits::eq_int_type と EOF 処理の詳細解説") { - std::cout << "\n=== Traits と EOF 処理の詳細分析 ===" << std::endl; - - using Traits = std::char_traits; - using int_type = Traits::int_type; - - // EOFの値を確認 - int_type eof_value = Traits::eof(); - std::cout << "EOF値: " << eof_value << std::endl; - std::cout << "EOF値 (16進): 0x" << std::hex << eof_value << std::dec << std::endl; - - // 通常の文字値との比較 - char normal_char = 'A'; - int_type normal_int_type = Traits::to_int_type(normal_char); - - std::cout << "\n--- 通常文字の処理 ---" << std::endl; - std::cout << "文字 'A' の int_type値: " << normal_int_type << std::endl; - std::cout << "文字 'A' (16進): 0x" << std::hex << normal_int_type << std::dec << std::endl; - - // eq_int_type による比較 - bool is_eof_normal = Traits::eq_int_type(normal_int_type, eof_value); - bool is_not_eof_normal = !Traits::eq_int_type(normal_int_type, eof_value); - - std::cout << "Traits::eq_int_type('A', EOF): " << std::boolalpha << is_eof_normal << std::endl; - std::cout << "!Traits::eq_int_type('A', EOF): " << std::boolalpha << is_not_eof_normal << std::endl; - - // EOFとの比較 - bool is_eof_eof = Traits::eq_int_type(eof_value, eof_value); - bool is_not_eof_eof = !Traits::eq_int_type(eof_value, eof_value); - - std::cout << "\n--- EOF値の処理 ---" << std::endl; - std::cout << "Traits::eq_int_type(EOF, EOF): " << std::boolalpha << is_eof_eof << std::endl; - std::cout << "!Traits::eq_int_type(EOF, EOF): " << std::boolalpha << is_not_eof_eof << std::endl; - - // 実際の overflow() 内での処理シミュレーション - std::cout << "\n--- overflow() 内での処理シミュレーション ---" << std::endl; - - auto simulate_overflow = [](int_type c) { - std::cout << "入力値: " << c << " (0x" << std::hex << c << std::dec << ")" << std::endl; - - if (!Traits::eq_int_type(c, Traits::eof())) { - std::cout << "→ 有効な文字として処理される" << std::endl; - char actual_char = Traits::to_char_type(c); - std::cout << "→ 変換された文字: '" << actual_char << "'" << std::endl; - return c; // 成功 - } else { - std::cout << "→ EOF として認識され、処理をスキップ" << std::endl; - return Traits::not_eof(c); // EOFでない値を返す - } - }; - - std::cout << "\n1. 通常文字 'B' の場合:" << std::endl; - simulate_overflow(Traits::to_int_type('B')); - - std::cout << "\n2. EOF値の場合:" << std::endl; - simulate_overflow(Traits::eof()); - - std::cout << "\n3. 数値 255 (0xFF) の場合:" << std::endl; - simulate_overflow(255); - - REQUIRE(true); -} - -TEST_CASE("型安全性の重要性") { - std::cout << "\n=== 型安全性の重要性 ===" << std::endl; - - using Traits = std::char_traits; - - // 危険な比較 vs 安全な比較 - int_type c = Traits::to_int_type('A'); - int_type eof_val = Traits::eof(); - - std::cout << "文字 'A' の値: " << c << std::endl; - std::cout << "EOF の値: " << eof_val << std::endl; - - // 直接比較(推奨されない) - bool unsafe_comparison = (c == eof_val); - std::cout << "直接比較 (c == eof): " << std::boolalpha << unsafe_comparison << std::endl; - - // 安全な比較(推奨) - bool safe_comparison = Traits::eq_int_type(c, eof_val); - std::cout << "安全な比較 Traits::eq_int_type(c, eof): " << std::boolalpha << safe_comparison << std::endl; - - // なぜ安全な比較が重要か - std::cout << "\n--- 型変換の問題 ---" << std::endl; - - // 文字型の範囲 - std::cout << "char の最小値: " << static_cast(std::numeric_limits::min()) << std::endl; - std::cout << "char の最大値: " << static_cast(std::numeric_limits::max()) << std::endl; - std::cout << "int_type のサイズ: " << sizeof(Traits::int_type) << " bytes" << std::endl; - std::cout << "char のサイズ: " << sizeof(char) << " bytes" << std::endl; - - REQUIRE(true); -} - -TEST_CASE("実際のoverflow()動作確認") { - std::cout << "\n=== 実際の overflow() 動作確認 ===" << std::endl; - - // カスタムデバッグstreambuf - class debug_stringbuf : public nodec::basic_reserved_stringbuf { - public: - using base_type = nodec::basic_reserved_stringbuf; - using int_type = std::char_traits::int_type; - - int_type overflow(int_type c) override { - std::cout << "overflow() called with: " << c; - - if (!std::char_traits::eq_int_type(c, std::char_traits::eof())) { - std::cout << " → 有効な文字 '" << std::char_traits::to_char_type(c) << "'" << std::endl; - return base_type::overflow(c); - } else { - std::cout << " → EOF(処理スキップ)" << std::endl; - return std::char_traits::not_eof(c); - } - } - }; - - debug_stringbuf buf; - std::ostream stream(&buf); - - std::cout << "文字 'X' を書き込み:" << std::endl; - stream << 'X'; - - std::cout << "文字 'Y' を書き込み:" << std::endl; - stream << 'Y'; - - std::cout << "結果: \"" << buf.str() << "\"" << std::endl; - - REQUIRE(true); -} diff --git a/tests/formatter/vector3_debug.cpp b/tests/formatter/vector3_debug.cpp deleted file mode 100644 index 1bc5167..0000000 --- a/tests/formatter/vector3_debug.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include -#include -#include - -int main() { - nodec::Vector3f vec(1.0f, 2.0f, 3.0f); - - std::cout << "=== Vector3 Output Test ===" << std::endl; - - // optimized_ostringstream - nodec::optimized_ostringstream oss; - oss << "Vector: " << vec; - std::cout << "optimized_ostringstream: '" << oss.str() << "'" << std::endl; - - // standard ostringstream - std::ostringstream std_oss; - std_oss << "Vector: " << vec; - std::cout << "standard ostringstream: '" << std_oss.str() << "'" << std::endl; - - // 直接Vector3出力 - std::cout << "Direct output: " << vec << std::endl; - - return 0; -} diff --git a/tests/formatter/view_analysis.cpp b/tests/formatter/view_analysis.cpp deleted file mode 100644 index d6bad58..0000000 --- a/tests/formatter/view_analysis.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include -#include -#include - -class PreciseBenchmark { -public: - PreciseBenchmark(const std::string& name) : name_(name) { - start_ = std::chrono::high_resolution_clock::now(); - } - - ~PreciseBenchmark() { - auto end = std::chrono::high_resolution_clock::now(); - auto ns = std::chrono::duration_cast(end - start_); - std::cout << std::setw(40) << name_ << ": " << std::setw(10) << ns.count() << " ns" << std::endl; - } - -private: - std::string name_; - std::chrono::high_resolution_clock::time_point start_; -}; - -int main() { - const int iterations = 1000000; - - std::cout << "=== View() vs str() Detailed Analysis ===" << std::endl; - std::cout << std::setw(40) << "Test Case" << ": " << std::setw(10) << "Time (ns)" << std::endl; - std::cout << std::string(55, '-') << std::endl; - - // 1. シンプルな文字列での比較 - { - PreciseBenchmark bench("Simple str() - 1M iterations"); - for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream oss; - oss << "Test " << i; - std::string result = oss.str(); - volatile auto len = result.length(); - (void)len; - } - } - - { - PreciseBenchmark bench("Simple view() - 1M iterations"); - for (int i = 0; i < iterations; ++i) { - nodec::optimized_ostringstream oss; - oss << "Test " << i; - std::string_view result = oss.view(); - volatile auto len = result.length(); - (void)len; - } - } - - // 2. 単一操作での比較 - std::cout << std::endl; - nodec::optimized_ostringstream test_oss; - test_oss << "This is a test string with some content: " << 12345 << ", float: " << 3.14159; - - { - PreciseBenchmark bench("Single str() call - 1M times"); - for (int i = 0; i < iterations; ++i) { - std::string result = test_oss.str(); - volatile auto len = result.length(); - (void)len; - } - } - - { - PreciseBenchmark bench("Single view() call - 1M times"); - for (int i = 0; i < iterations; ++i) { - std::string_view result = test_oss.view(); - volatile auto len = result.length(); - (void)len; - } - } - - // 3. コンストラクション コストの比較 - std::cout << std::endl; - { - PreciseBenchmark bench("string construction only"); - for (int i = 0; i < iterations; ++i) { - std::string result("Test string content for analysis"); - volatile auto len = result.length(); - (void)len; - } - } - - { - PreciseBenchmark bench("string_view construction only"); - std::string source("Test string content for analysis"); - for (int i = 0; i < iterations; ++i) { - std::string_view result(source); - volatile auto len = result.length(); - (void)len; - } - } - - return 0; -} diff --git a/view_performance_analysis.md b/view_performance_analysis.md deleted file mode 100644 index 0557b9d..0000000 --- a/view_performance_analysis.md +++ /dev/null @@ -1,171 +0,0 @@ -# FastFormatter view() vs str() Performance Analysis - -## 実行結果の要約 - -### Release Build (O2最適化) -``` ---- Simple Concatenation Test --- -FastFormatter - Simple concatenation: 64771 microseconds -FastFormatter view() - Simple concatenation: 59674 microseconds (✓ view() が 8% 高速) - ---- Numeric Formatting Test --- -FastFormatter - Numeric formatting: 161807 microseconds -FastFormatter view() - Numeric formatting: 162424 microseconds (≈ ほぼ同等) - ---- Long Chain Test --- -FastFormatter - Long chain: 25528 microseconds -FastFormatter view() - Long chain: 27137 microseconds (✗ view() が 6% 遅い) - ---- Memory Usage Test --- -FastFormatter - Multiple instances: 64753 microseconds -FastFormatter view() - Multiple instances: 73830 microseconds (✗ view() が 14% 遅い) - ---- Reserve Capacity Test --- -FastFormatter view() with reserve - Long text: 114391 microseconds -FastFormatter without reserve - Long text: 115304 microseconds (✓ view() が僅かに高速) - ---- String View Performance Test --- -FastFormatter view() - No copy: 75927 microseconds -FastFormatter str() - With copy: 67877 microseconds (✗ view() が 12% 遅い) -FastFormatter view() - Long strings: 14468 microseconds -FastFormatter str() - Long strings: 22152 microseconds (✓ view() が 35% 高速) -``` - -### Debug Build (最適化なし) -``` ---- Simple Concatenation Test --- -FastFormatter - Simple concatenation: 524202 microseconds -FastFormatter view() - Simple concatenation: 457610 microseconds (✓ view() が 13% 高速) - ---- Numeric Formatting Test --- -FastFormatter - Numeric formatting: 1553837 microseconds -FastFormatter view() - Numeric formatting: 1198604 microseconds (✓ view() が 23% 高速) - ---- Memory Usage Test --- -FastFormatter - Multiple instances: 623764 microseconds -FastFormatter view() - Multiple instances: 507135 microseconds (✓ view() が 19% 高速) - ---- String View Performance Test --- -FastFormatter view() - No copy: 963670 microseconds -FastFormatter str() - With copy: 955954 microseconds (≈ ほぼ同等) -FastFormatter view() - Long strings: 104077 microseconds -FastFormatter str() - Long strings: 118974 microseconds (✓ view() が 13% 高速) -``` - -### マイクロベンチマーク結果 (Release) -``` -Simple str() - 1M iterations: 813138500 ns -Simple view() - 1M iterations: 755528800 ns (✓ view() が 7% 高速) -Single str() call - 1M times: 70939900 ns -Single view() call - 1M times: 925100 ns (✓ view() が 98.7% 高速) -``` - -## 分析結果 - -### 1. view()が高速な場合 -- **長い文字列の処理**: 文字列が長いほど、コピーコストが増大するため、view()の優位性が明確に現れる -- **単純な呼び出し**: `view()`自体は`string_view`の軽量な構築なので、純粋な呼び出し時間は非常に短い -- **Debug builds**: 最適化されていない環境では、コピーコストが顕著に現れるため、view()が多くの場合で高速 - -### 2. view()が遅くなる場合 -- **短い文字列での複雑な処理**: 短い文字列では、コピーコストが小さく、その他の処理オーバーヘッドが相対的に大きくなる -- **Release builds での最適化の影響**: - -#### 2.1 コンパイラ最適化の影響 -```cpp -// str()の場合 -string_type str() const { - return buffer_; // RVO (Return Value Optimization) やNRVO適用可能 -} - -// view()の場合 -string_view_type view() const { - return string_view_type(buffer_.data(), buffer_.size()); // 関数呼び出しが必要 -} -``` - -#### 2.2 メモリアクセスパターンの違い -- `str()`: 文字列コピーが発生するが、連続的なメモリアクセスで、CPU キャッシュに優しい -- `view()`: `buffer_.data()` と `buffer_.size()` の2つの関数呼び出しが必要 - -#### 2.3 標準ライブラリの最適化 -- `std::string` のコピーコンストラクタは高度に最適化されており、短い文字列に対してはSSO (Small String Optimization) が有効 -- `std::string_view` の構築も軽量だが、関数呼び出しオーバーヘッドが存在 - -### 3. ベンチマーク結果の解釈 - -#### 3.1 "Long Chain Test" で view() が遅い理由 -```cpp -// 複数の短い文字列を連結する場合 -ss << "Item" << i << ": " << (i * 2.5) << " units"; -``` -- 各操作で生成される文字列が比較的短い -- 繰り返し処理でのオーバーヘッドが累積 -- Release最適化により、str()のコピーが高速化 - -#### 3.2 "Memory Usage Test" で view() が遅い理由 -```cpp -// 多数のインスタンスを作成・破棄する場合 -for (int i = 0; i < iterations; ++i) { - FastFormatter ff; - ff << "Test" << i; - auto result = ff.view(); // または ff.str() -} -``` -- インスタンス作成・破棄のオーバーヘッドが支配的 -- view() の軽量性が相対的に目立たない -- Release最適化により、全体の処理が高速化され、差が縮まる - -## 結論と推奨事項 - -### view() を使うべき場合 -1. **長い文字列を扱う場合** (目安: 100文字以上) -2. **Debug builds での開発時** -3. **文字列内容を変更しない読み取り専用の用途** -4. **メモリ使用量を抑えたい場合** - -### str() を使うべき場合 -1. **短い文字列を扱う場合** (目安: 100文字未満) -2. **Release builds での高性能が要求される場合** -3. **文字列を他の関数に渡す必要がある場合** -4. **既存のコードとの互換性が重要な場合** - -### 最適化のための提案 - -#### 1. Conditional Return (条件分岐によるリターン) -```cpp -// 文字列長に応じて自動選択 -auto get_result() const { - if (buffer_.size() > 100) { - return view(); // 長い文字列はview() - } else { - return str(); // 短い文字列はstr() - } -} -``` - -#### 2. Template Specialization -```cpp -template -auto get_string() const { - if constexpr (PreferView) { - return view(); - } else { - return str(); - } -} -``` - -#### 3. Benchmark-driven Selection -実際の使用パターンに基づいて、アプリケーション固有の最適化を行う。 - -## 技術的な洞察 - -この分析により、以下の重要な洞察が得られました: - -1. **コンパイラ最適化の影響は非常に大きい**: Release builds では予想外の結果が生じる -2. **文字列長がパフォーマンスの決定要因**: 閾値を超えるとview()が明確に有利 -3. **コンテキストが重要**: 単純な処理 vs 複雑な処理で結果が異なる -4. **標準ライブラリの最適化は侮れない**: 特にSSO (Small String Optimization) の効果 - -これらの結果は、実際のアプリケーションでどちらを選択するかを決定する際の重要な指針となります。 From 376df3566d1be1730dabcef41589ef851111c413 Mon Sep 17 00:00:00 2001 From: IOE Date: Sat, 12 Jul 2025 20:29:34 +0900 Subject: [PATCH 4/6] add string_builder --- include/nodec/formatter.hpp | 109 +----------------- include/nodec/string_builder.hpp | 104 +++++++++++++++++ tests/CMakeLists.txt | 6 +- .../basic_test.cpp} | 4 +- .../benchmark_test.cpp} | 19 +-- .../oss_compat_test.cpp} | 3 +- 6 files changed, 126 insertions(+), 119 deletions(-) create mode 100644 include/nodec/string_builder.hpp rename tests/{formatter/formatter.cpp => string_builder/basic_test.cpp} (97%) rename tests/{formatter/formatter_benchmark.cpp => string_builder/benchmark_test.cpp} (91%) rename tests/{formatter/formatter_ostringstream_compat.cpp => string_builder/oss_compat_test.cpp} (99%) diff --git a/include/nodec/formatter.hpp b/include/nodec/formatter.hpp index d5f59db..19484bf 100644 --- a/include/nodec/formatter.hpp +++ b/include/nodec/formatter.hpp @@ -13,13 +13,13 @@ namespace nodec { -class FormatterLegacy { +class Formatter { public: - FormatterLegacy() noexcept {} - ~FormatterLegacy() noexcept {} + Formatter() noexcept {} + ~Formatter() noexcept {} template - FormatterLegacy &operator<<(const T &value) noexcept { + Formatter &operator<<(const T &value) noexcept { stream_ << value; return *this; } @@ -31,108 +31,9 @@ class FormatterLegacy { private: std::ostringstream stream_; - NODEC_DISABLE_COPY(FormatterLegacy) + NODEC_DISABLE_COPY(Formatter) }; -template> -class BasicStringBuilderStream { -private: - class BasicStringBuilderStreamBuf : public std::basic_streambuf { - public: - using base_type = std::basic_streambuf; - using string_type = std::basic_string; - using string_view_type = std::basic_string_view; - 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(count)); - return count; - } - - private: - string_type &dest_; - }; - -public: - using string_type = std::basic_string; - using string_view_type = std::basic_string_view; - using size_type = typename string_type::size_type; - using stringbuf_type = BasicStringBuilderStreamBuf; - - BasicStringBuilderStream(string_type &dest): base_stream_(&buffer_), buffer_(dest) {} - - string_type str() const { - return buffer_.str(); - } - - BasicStringBuilderStream &operator<<(const char *str) { - if (str == nullptr) { - return *this; - } - - // 文字列の長さを取得 - std::streamsize count = static_cast(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 - BasicStringBuilderStream &operator<<(const T &value) { - base_stream_ << value; - return *this; - } - -private: - stringbuf_type buffer_; - std::basic_ostream base_stream_; -}; - -using StringBuilderStream = BasicStringBuilderStream; - template class ErrorFormatter { public: diff --git a/include/nodec/string_builder.hpp b/include/nodec/string_builder.hpp new file mode 100644 index 0000000..c56bc01 --- /dev/null +++ b/include/nodec/string_builder.hpp @@ -0,0 +1,104 @@ +#include + +namespace nodec { + +template> +class BasicStringBuilderStream { +private: + class BasicStringBuilderStreamBuf : public std::basic_streambuf { + public: + using base_type = std::basic_streambuf; + using string_type = std::basic_string; + using string_view_type = std::basic_string_view; + 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(count)); + return count; + } + + private: + string_type &dest_; + }; + +public: + using string_type = std::basic_string; + using string_view_type = std::basic_string_view; + using size_type = typename string_type::size_type; + using stringbuf_type = BasicStringBuilderStreamBuf; + + BasicStringBuilderStream(string_type &dest): base_stream_(&buffer_), buffer_(dest) {} + + string_type str() const { + return buffer_.str(); + } + + BasicStringBuilderStream &operator<<(const char *str) { + if (str == nullptr) { + return *this; + } + + // 文字列の長さを取得 + std::streamsize count = static_cast(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 + BasicStringBuilderStream &operator<<(const T &value) { + base_stream_ << value; + return *this; + } + +private: + stringbuf_type buffer_; + std::basic_ostream base_stream_; +}; + +using StringBuilderStream = BasicStringBuilderStream; + +} // namespace nodec \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 10b0163..19a7263 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,9 +15,9 @@ 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__formatter_benchmark" formatter/formatter_benchmark.cpp) -add_basic_test("nodec__formatter_ostringstream_compat" formatter/formatter_ostringstream_compat.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) diff --git a/tests/formatter/formatter.cpp b/tests/string_builder/basic_test.cpp similarity index 97% rename from tests/formatter/formatter.cpp rename to tests/string_builder/basic_test.cpp index 896d3f1..53e9920 100644 --- a/tests/formatter/formatter.cpp +++ b/tests/string_builder/basic_test.cpp @@ -1,10 +1,10 @@ #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include -#include +#include #include -TEST_CASE("Testing Formatter - Basic Types") { +TEST_CASE("Testing StringBuilder - Basic Types") { // 文字列連結 { std::string result; diff --git a/tests/formatter/formatter_benchmark.cpp b/tests/string_builder/benchmark_test.cpp similarity index 91% rename from tests/formatter/formatter_benchmark.cpp rename to tests/string_builder/benchmark_test.cpp index 1ad1e21..303c984 100644 --- a/tests/formatter/formatter_benchmark.cpp +++ b/tests/string_builder/benchmark_test.cpp @@ -9,6 +9,7 @@ #include #include +#include #include // ベンチマーク測定用のヘルパークラス @@ -34,11 +35,11 @@ TEST_CASE("Benchmark - Simple Concatenation") { std::cout << "\n--- Simple Concatenation Test ---" << std::endl; - // nodec::FormatterLegacyのベンチマーク + // nodec::Formatterのベンチマーク { - BenchmarkTimer timer("nodec::FormatterLegacy - Simple concatenation"); + BenchmarkTimer timer("nodec::Formatter - Simple concatenation"); for (int i = 0; i < iterations; ++i) { - std::string result = nodec::FormatterLegacy() << "Hello" << " " << "World" << i; + std::string result = nodec::Formatter() << "Hello" << " " << "World" << i; // 結果を使用して最適化を防ぐ volatile auto len = result.length(); (void)len; @@ -92,11 +93,11 @@ TEST_CASE("Benchmark - Numeric Formatting") { std::cout << "\n--- Numeric Formatting Test ---" << std::endl; - // nodec::FormatterLegacyのベンチマーク + // nodec::Formatterのベンチマーク { - BenchmarkTimer timer("nodec::FormatterLegacy - Numeric formatting"); + BenchmarkTimer timer("nodec::Formatter - Numeric formatting"); for (int i = 0; i < iterations; ++i) { - std::string result = nodec::FormatterLegacy() << "Number: " << i + std::string result = nodec::Formatter() << "Number: " << i << ", Float: " << (i * 3.14) << ", Hex: " << std::hex << i; volatile auto len = result.length(); @@ -137,11 +138,11 @@ TEST_CASE("Benchmark - Long Chain") { std::cout << "\n--- Long Chain Test ---" << std::endl; - // nodec::FormatterLegacyのベンチマーク - 長いチェーン + // nodec::Formatterのベンチマーク - 長いチェーン { - BenchmarkTimer timer("nodec::FormatterLegacy - Long chain"); + BenchmarkTimer timer("nodec::Formatter - Long chain"); for (int i = 0; i < iterations; ++i) { - std::string result = nodec::FormatterLegacy() << "Start: " << i + std::string result = nodec::Formatter() << "Start: " << i << ", Value1: " << (i * 2) << ", Value2: " << (i * 3.5) << ", Value3: " << (i % 100) diff --git a/tests/formatter/formatter_ostringstream_compat.cpp b/tests/string_builder/oss_compat_test.cpp similarity index 99% rename from tests/formatter/formatter_ostringstream_compat.cpp rename to tests/string_builder/oss_compat_test.cpp index 8977b7f..d0bbb35 100644 --- a/tests/formatter/formatter_ostringstream_compat.cpp +++ b/tests/string_builder/oss_compat_test.cpp @@ -2,9 +2,10 @@ #include #include -#include #include +#include + TEST_CASE("StringBuilderStream - Enhanced Manipulator Support") { // setw テスト std::string buffer1; From c9efb37f5a93cdf1f2c205a15a6520c07f6a51f6 Mon Sep 17 00:00:00 2001 From: IOE Date: Sat, 12 Jul 2025 22:39:20 +0900 Subject: [PATCH 5/6] improve perf --- include/nodec/formatter.hpp | 5 - .../logging/formatters/simple_formatter.hpp | 29 ++- include/nodec/logging/log_record.hpp | 14 +- include/nodec/logging/logger.hpp | 10 +- include/nodec/string_builder.hpp | 76 +++++- tests/CMakeLists.txt | 9 +- tests/formatter/complex_format_debug.cpp | 27 -- tests/formatter/constructor_debug.cpp | 29 --- tests/formatter/debug_test.cpp | 29 --- tests/formatter/eof_necessity_test.cpp | 245 ------------------ tests/logging/{bench.cpp => bench_test.cpp} | 10 +- .../logging/{logging.cpp => logger_test.cpp} | 0 tests/logging/simple_formatter_test.cpp | 80 ++++++ tests/string_builder/basic_test.cpp | 8 +- tests/string_builder/benchmark_test.cpp | 12 +- tests/string_builder/oss_compat_test.cpp | 62 ++--- 16 files changed, 232 insertions(+), 413 deletions(-) delete mode 100644 tests/formatter/complex_format_debug.cpp delete mode 100644 tests/formatter/constructor_debug.cpp delete mode 100644 tests/formatter/debug_test.cpp delete mode 100644 tests/formatter/eof_necessity_test.cpp rename tests/logging/{bench.cpp => bench_test.cpp} (93%) rename tests/logging/{logging.cpp => logger_test.cpp} (100%) create mode 100644 tests/logging/simple_formatter_test.cpp diff --git a/include/nodec/formatter.hpp b/include/nodec/formatter.hpp index 19484bf..58c1143 100644 --- a/include/nodec/formatter.hpp +++ b/include/nodec/formatter.hpp @@ -3,12 +3,7 @@ #include -#include -#include #include -#include -#include -#include #include namespace nodec { diff --git a/include/nodec/logging/formatters/simple_formatter.hpp b/include/nodec/logging/formatters/simple_formatter.hpp index 1497029..374d1d0 100644 --- a/include/nodec/logging/formatters/simple_formatter.hpp +++ b/include/nodec/logging/formatters/simple_formatter.hpp @@ -3,6 +3,7 @@ #include +#include "../../string_builder.hpp" #include "../log_record.hpp" namespace nodec { @@ -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(); } }; diff --git a/include/nodec/logging/log_record.hpp b/include/nodec/logging/log_record.hpp index d0875e2..8f05d35 100644 --- a/include/nodec/logging/log_record.hpp +++ b/include/nodec/logging/log_record.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include "level.hpp" @@ -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; }; diff --git a/include/nodec/logging/logger.hpp b/include/nodec/logging/logger.hpp index 4be8d66..dfff5a4 100644 --- a/include/nodec/logging/logger.hpp +++ b/include/nodec/logging/logger.hpp @@ -10,6 +10,7 @@ #include "../macros.hpp" #include "../optional.hpp" #include "../signals/signal.hpp" +#include "../string_builder.hpp" #include "log_record.hpp" namespace nodec { @@ -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 LogStream &operator<<(const T &value) { - stream_ << value; + builder_ << value; return *this; } @@ -84,7 +85,8 @@ class Logger { Level level_; const char *file_; std::size_t line_; - std::ostringstream stream_; + std::string message; + StringBuilder builder_; }; public: diff --git a/include/nodec/string_builder.hpp b/include/nodec/string_builder.hpp index c56bc01..17631fc 100644 --- a/include/nodec/string_builder.hpp +++ b/include/nodec/string_builder.hpp @@ -1,9 +1,12 @@ +#ifndef NODEC__STRING_BUILDER_HPP_ +#define NODEC__STRING_BUILDER_HPP_ + #include namespace nodec { template> -class BasicStringBuilderStream { +class BasicStringBuilder { private: class BasicStringBuilderStreamBuf : public std::basic_streambuf { public: @@ -50,21 +53,72 @@ class BasicStringBuilderStream { using size_type = typename string_type::size_type; using stringbuf_type = BasicStringBuilderStreamBuf; - BasicStringBuilderStream(string_type &dest): base_stream_(&buffer_), buffer_(dest) {} + BasicStringBuilder(string_type &dest): base_stream_(&buffer_), buffer_(dest) {} string_type str() const { return buffer_.str(); } - BasicStringBuilderStream &operator<<(const char *str) { - if (str == nullptr) { - return *this; + 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(base_stream_.width()) <= str_size) { + pad = 0; + } else { + pad = static_cast(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(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(Traits::length(str)); - // パディングの計算 std::streamsize pad = base_stream_.width() <= 0 || base_stream_.width() <= count ? 0 : base_stream_.width() - count; // 左詰め以外の場合は左側にパディング @@ -89,7 +143,7 @@ class BasicStringBuilderStream { } template - BasicStringBuilderStream &operator<<(const T &value) { + BasicStringBuilder &operator<<(const T &value) { base_stream_ << value; return *this; } @@ -99,6 +153,8 @@ class BasicStringBuilderStream { std::basic_ostream base_stream_; }; -using StringBuilderStream = BasicStringBuilderStream; +using StringBuilder = BasicStringBuilder; + +} // namespace nodec -} // namespace nodec \ No newline at end of file +#endif \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 19a7263..1424378 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,12 +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__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) diff --git a/tests/formatter/complex_format_debug.cpp b/tests/formatter/complex_format_debug.cpp deleted file mode 100644 index a1e350f..0000000 --- a/tests/formatter/complex_format_debug.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -#include -#include -#include -#include -#include - -TEST_CASE("Complex format chain debugging") { - // 複雑なフォーマットチェーンのデバッグ - std::string result = nodec::Formatter() - << "Value: " << std::hex << std::uppercase << std::showbase - << nodec::setfill('0') << nodec::setw(8) << 255 - << ", Float: " << std::dec << std::fixed << nodec::setprecision(2) << 3.14159 - << ", Bool: " << std::boolalpha << true; - std::cout << "Formatter result: '" << result << "'" << std::endl; - - // ostringstream での同じチェーン - std::ostringstream oss; - oss << "Value: " << std::hex << std::uppercase << std::showbase - << std::setfill('0') << std::setw(8) << 255 - << ", Float: " << std::dec << std::fixed << std::setprecision(2) << 3.14159 - << ", Bool: " << std::boolalpha << true; - std::string expected = oss.str(); - std::cout << "ostringstream result: '" << expected << "'" << std::endl; - - CHECK(result == expected); -} diff --git a/tests/formatter/constructor_debug.cpp b/tests/formatter/constructor_debug.cpp deleted file mode 100644 index f077bd9..0000000 --- a/tests/formatter/constructor_debug.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include - -int main() { - std::cout << "=== Constructor Debug Test ===" << std::endl; - - // Case 1: explicit size_type コンストラクタが呼ばれるべき - std::cout << "Case 1: nodec::optimized_ostringstream oss(1024);" << std::endl; - nodec::optimized_ostringstream oss1(1024); - std::cout << "Capacity: " << oss1.capacity() << std::endl; - - // Case 2: size_typeを明示的にキャスト - std::cout << "\nCase 2: nodec::optimized_ostringstream oss(static_cast(1024));" << std::endl; - nodec::optimized_ostringstream oss2(static_cast(1024)); - std::cout << "Capacity: " << oss2.capacity() << std::endl; - - // Case 3: openmodeコンストラクタを明示的に呼ぶ - std::cout << "\nCase 3: nodec::optimized_ostringstream oss(std::ios_base::out);" << std::endl; - nodec::optimized_ostringstream oss3(std::ios_base::out); - std::cout << "Capacity: " << oss3.capacity() << std::endl; - - // Case 4: どの型が1024として解釈されているかチェック - std::cout << "\n=== Type Analysis ===" << std::endl; - std::cout << "1024 as int: " << static_cast(1024) << std::endl; - std::cout << "1024 as size_t: " << static_cast(1024) << std::endl; - std::cout << "1024 as openmode: " << static_cast(1024) << std::endl; - - return 0; -} diff --git a/tests/formatter/debug_test.cpp b/tests/formatter/debug_test.cpp deleted file mode 100644 index 63fac34..0000000 --- a/tests/formatter/debug_test.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include - -int main() { - // デバッグ用テスト - std::cout << "Creating ostringstream with reserve 1024..." << std::endl; - nodec::optimized_ostringstream oss(1024); - - std::cout << "Initial capacity: " << oss.capacity() << std::endl; - std::cout << "Initial size: " << oss.size() << std::endl; - - // 手動でリザーブを試行 - std::cout << "Calling reserve(2048)..." << std::endl; - oss.reserve(2048); - std::cout << "After manual reserve capacity: " << oss.capacity() << std::endl; - - oss << "Test"; - - std::cout << "After write capacity: " << oss.capacity() << std::endl; - std::cout << "After write size: " << oss.size() << std::endl; - std::cout << "Content: " << oss.str() << std::endl; - - // 標準のstd::stringでの比較 - std::string test_string; - test_string.reserve(1024); - std::cout << "Standard string reserve(1024) capacity: " << test_string.capacity() << std::endl; - - return 0; -} diff --git a/tests/formatter/eof_necessity_test.cpp b/tests/formatter/eof_necessity_test.cpp deleted file mode 100644 index e136c22..0000000 --- a/tests/formatter/eof_necessity_test.cpp +++ /dev/null @@ -1,245 +0,0 @@ -#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -#include -#include -#include -#include -#include - -// EOFチェックを省略したバージョン -template> -class no_eof_check_stringbuf : public std::basic_streambuf { -public: - using base_type = std::basic_streambuf; - using string_type = std::basic_string; - using size_type = typename string_type::size_type; - using int_type = typename Traits::int_type; - using char_type = CharT; - - // EOFチェックを完全に省略したoverflow() - int_type overflow(int_type c = Traits::eof()) override { - // 警告:EOFチェックなし! - if (buffer_.size() < buffer_.capacity()) { - buffer_.push_back(Traits::to_char_type(c)); - } else { - buffer_.reserve(buffer_.capacity() == 0 ? 16 : buffer_.capacity() * 2); - buffer_.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; - size_type needed = buffer_.size() + static_cast(count); - if (needed > buffer_.capacity()) { - buffer_.reserve(needed); - } - buffer_.append(s, static_cast(count)); - return count; - } - - string_type str() const { return buffer_; } - void clear() { buffer_.clear(); } - -private: - string_type buffer_; -}; - -// EOFチェックありとなしの統計を取るバージョン -template> -class statistics_stringbuf : public std::basic_streambuf { -public: - using base_type = std::basic_streambuf; - using string_type = std::basic_string; - using size_type = typename string_type::size_type; - using int_type = typename Traits::int_type; - using char_type = CharT; - - int_type overflow(int_type c = Traits::eof()) override { - overflow_calls_++; - - if (!Traits::eq_int_type(c, Traits::eof())) { - valid_char_calls_++; - if (buffer_.size() < buffer_.capacity()) { - buffer_.push_back(Traits::to_char_type(c)); - } else { - buffer_.reserve(buffer_.capacity() == 0 ? 16 : buffer_.capacity() * 2); - buffer_.push_back(Traits::to_char_type(c)); - } - return c; - } else { - eof_calls_++; - return Traits::not_eof(c); - } - } - - std::streamsize xsputn(const char_type *s, std::streamsize count) override { - xsputn_calls_++; - if (count <= 0) return 0; - size_type needed = buffer_.size() + static_cast(count); - if (needed > buffer_.capacity()) { - buffer_.reserve(needed); - } - buffer_.append(s, static_cast(count)); - return count; - } - - string_type str() const { return buffer_; } - - void print_statistics() const { - std::cout << "=== 統計情報 ===" << std::endl; - std::cout << "overflow() 総呼び出し回数: " << overflow_calls_ << std::endl; - std::cout << " - 有効な文字: " << valid_char_calls_ << std::endl; - std::cout << " - EOF: " << eof_calls_ << std::endl; - std::cout << "xsputn() 呼び出し回数: " << xsputn_calls_ << std::endl; - } - - void reset_statistics() { - overflow_calls_ = valid_char_calls_ = eof_calls_ = xsputn_calls_ = 0; - } - -private: - string_type buffer_; - mutable size_t overflow_calls_ = 0; - mutable size_t valid_char_calls_ = 0; - mutable size_t eof_calls_ = 0; - mutable size_t xsputn_calls_ = 0; -}; - -TEST_CASE("EOFチェックの必要性を検証") { - std::cout << "\n=== EOFチェックの必要性検証 ===" << std::endl; - - statistics_stringbuf stats_buf; - std::ostream stats_stream(&stats_buf); - - std::cout << "\n1. 通常の文字列操作テスト:" << std::endl; - stats_buf.reset_statistics(); - - stats_stream << "Hello"; - stats_stream << " "; - stats_stream << "World"; - stats_stream << 123; - stats_stream << 3.14; - - stats_buf.print_statistics(); - std::cout << "結果: \"" << stats_buf.str() << "\"" << std::endl; - - std::cout << "\n2. 大量文字処理テスト:" << std::endl; - stats_buf.reset_statistics(); - - for (int i = 0; i < 1000; ++i) { - stats_stream << static_cast('A' + (i % 26)); - } - - stats_buf.print_statistics(); - - std::cout << "\n3. 混合処理テスト:" << std::endl; - stats_buf.reset_statistics(); - - for (int i = 0; i < 100; ++i) { - stats_stream << "Item" << i << ": " << (i * 1.5) << " "; - } - - stats_buf.print_statistics(); -} - -TEST_CASE("EOFチェックなしでの動作確認") { - std::cout << "\n=== EOFチェック省略版の動作確認 ===" << std::endl; - - try { - no_eof_check_stringbuf no_check_buf; - std::ostream no_check_stream(&no_check_buf); - - std::cout << "EOFチェックなしでの書き込みテスト:" << std::endl; - - no_check_stream << "Test"; - no_check_stream << 123; - no_check_stream << " works!"; - - std::cout << "結果: \"" << no_check_buf.str() << "\"" << std::endl; - std::cout << "→ 正常に動作(EOFは渡されていない)" << std::endl; - - } catch (const std::exception& e) { - std::cout << "例外発生: " << e.what() << std::endl; - } -} - -TEST_CASE("手動でEOFを渡した場合の動作") { - std::cout << "\n=== 手動EOF送信テスト ===" << std::endl; - - statistics_stringbuf stats_buf; - - std::cout << "1. EOFチェックありの場合:" << std::endl; - auto eof_val = std::char_traits::eof(); - auto result_with_check = stats_buf.overflow(eof_val); - stats_buf.print_statistics(); - std::cout << "EOF処理結果: " << result_with_check << std::endl; - - std::cout << "\n2. 通常文字との比較:" << std::endl; - stats_buf.reset_statistics(); - auto normal_result = stats_buf.overflow(std::char_traits::to_int_type('A')); - stats_buf.print_statistics(); - std::cout << "通常文字処理結果: " << normal_result << std::endl; - std::cout << "バッファ内容: \"" << stats_buf.str() << "\"" << std::endl; -} - -TEST_CASE("C++標準準拠とパフォーマンスの比較") { - std::cout << "\n=== 標準準拠 vs パフォーマンス ===" << std::endl; - - const int iterations = 100000; - - // EOFチェックありのバージョン - { - auto start = std::chrono::high_resolution_clock::now(); - - nodec::optimized_ostringstream oss_with_check; - for (int i = 0; i < iterations; ++i) { - oss_with_check << static_cast('A' + (i % 26)); - } - - auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - - std::cout << "EOFチェックあり: " << duration.count() << " μs" << std::endl; - std::cout << "結果長: " << oss_with_check.size() << std::endl; - } - - // EOFチェックなしのバージョン - { - auto start = std::chrono::high_resolution_clock::now(); - - no_eof_check_stringbuf buf_no_check; - std::ostream stream_no_check(&buf_no_check); - - for (int i = 0; i < iterations; ++i) { - stream_no_check << static_cast('A' + (i % 26)); - } - - auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - - std::cout << "EOFチェックなし: " << duration.count() << " μs" << std::endl; - std::cout << "結果長: " << buf_no_check.str().size() << std::endl; - } -} - -TEST_CASE("実用的な推奨事項") { - std::cout << "\n=== 実用的な推奨事項 ===" << std::endl; - - std::cout << "1. C++標準への準拠:" << std::endl; - std::cout << " - overflow()の標準的な実装ではEOFチェックが期待される" << std::endl; - std::cout << " - 他のstreambuf実装との互換性確保" << std::endl; - - std::cout << "\n2. 防御的プログラミング:" << std::endl; - std::cout << " - 予期しないEOF値から保護" << std::endl; - std::cout << " - 将来の仕様変更への対応" << std::endl; - - std::cout << "\n3. パフォーマンスへの影響:" << std::endl; - std::cout << " - 1回の比較操作による影響は微小" << std::endl; - std::cout << " - 現代のCPUでは分岐予測により最適化される" << std::endl; - - std::cout << "\n4. 推奨:" << std::endl; - std::cout << " EOFチェックは『保持する』ことを推奨" << std::endl; - std::cout << " 理由: 安全性 > 微小なパフォーマンス差" << std::endl; - - REQUIRE(true); -} diff --git a/tests/logging/bench.cpp b/tests/logging/bench_test.cpp similarity index 93% rename from tests/logging/bench.cpp rename to tests/logging/bench_test.cpp index 4e81a1b..594009f 100644 --- a/tests/logging/bench.cpp +++ b/tests/logging/bench_test.cpp @@ -9,7 +9,7 @@ #include TEST_CASE("Single thread, 100,000 iterations") { - int iters = 100000; + int iters = 10'000; using namespace nodec::logging; auto logger = get_logger(); @@ -33,11 +33,12 @@ TEST_CASE("Single thread, 100,000 iterations") { // NOTE: '<<' operator is very slow. // About 50% of total cpu consumption is occupied... - // Should use std::format() instead if c++17 is available. + // Should use std::format() instead if c++20 is available. + CHECK(true); } -TEST_CASE("10 threads, competing over the same logger object, 100,000 iterations") { - int iters = 100000; +TEST_CASE("10 threads, competing over the same logger object, 10,000 iterations") { + int iters = 10'000; int thread_count = 10; using namespace nodec::logging; @@ -69,4 +70,5 @@ TEST_CASE("10 threads, competing over the same logger object, 100,000 iterations auto delta_d = duration_cast>(delta).count(); MESSAGE("Elapsed: ", delta_d, " secs ", std::size_t(iters / delta_d), "/sec"); + CHECK(true); } \ No newline at end of file diff --git a/tests/logging/logging.cpp b/tests/logging/logger_test.cpp similarity index 100% rename from tests/logging/logging.cpp rename to tests/logging/logger_test.cpp diff --git a/tests/logging/simple_formatter_test.cpp b/tests/logging/simple_formatter_test.cpp new file mode 100644 index 0000000..7ddbd06 --- /dev/null +++ b/tests/logging/simple_formatter_test.cpp @@ -0,0 +1,80 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +// ベンチマーク測定用のヘルパークラス +class BenchmarkTimer { +public: + BenchmarkTimer(const std::string &name, int iterations = 1): name_(name), iterations_(iterations) { + start_time_ = std::chrono::high_resolution_clock::now(); + } + + ~BenchmarkTimer() { + auto end_time = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end_time - start_time_); + double avg_time = static_cast(duration.count()) / iterations_; + MESSAGE(name_, ": ", duration.count(), " microseconds total, ", avg_time, " microseconds per operation"); + } + +private: + std::string name_; + int iterations_; + std::chrono::high_resolution_clock::time_point start_time_; +}; + +TEST_CASE("SimpleFormatter - Output Format Structure") { + using namespace std::chrono; + using namespace nodec::logging; + using namespace std::string_view_literals; + + formatters::SimpleFormatter formatter; + LogRecord record{system_clock::time_point(), "TestLogger"sv, Level::Info, "Test message"sv, "test.cpp"sv, 100}; + std::string result = formatter(record); + + // MESSAGE("Formatted output: ", result); + CHECK(result == "[INFO] [TestLogger] - Test message\n(test.cpp line 100)\n"); +} + +TEST_CASE("SimpleFormatter - 10'000 Format Benchmark") { + const int iterations = 10'000; + + using namespace std::chrono; + using namespace nodec::logging; + using namespace std::string_view_literals; + + MESSAGE("--- SimpleFormatter 10'000 Format Benchmark ---"); + + formatters::SimpleFormatter formatter; + + { + BenchmarkTimer timer("SimpleFormatter - 10'000 format operations", iterations); + + for (int i = 0; i < iterations; ++i) { + LogRecord record{ + system_clock::now(), + "BenchLogger"sv, + Level::Info, + "Benchmark message " + std::to_string(i), + __FILE__, + __LINE__}; + + std::string result = formatter(record); + + // 結果を使用して最適化を防ぐ + volatile auto len = result.length(); + (void)len; + } + } + + // テストが正常に実行されたことを確認 + CHECK(true); +} diff --git a/tests/string_builder/basic_test.cpp b/tests/string_builder/basic_test.cpp index 53e9920..f656f8a 100644 --- a/tests/string_builder/basic_test.cpp +++ b/tests/string_builder/basic_test.cpp @@ -8,28 +8,28 @@ TEST_CASE("Testing StringBuilder - Basic Types") { // 文字列連結 { std::string result; - nodec::StringBuilderStream(result) << "Hello" << " " << "World"; + nodec::StringBuilder(result) << "Hello" << " " << "World"; CHECK(result == "Hello World"); } // 整数 { std::string result; - nodec::StringBuilderStream(result) << "Number: " << 42; + nodec::StringBuilder(result) << "Number: " << 42; CHECK(result == "Number: 42"); } // 浮動小数点 { std::string result; - nodec::StringBuilderStream(result) << "Float: " << 3.14; + nodec::StringBuilder(result) << "Float: " << 3.14; CHECK(result == "Float: 3.14"); } // 文字 { std::string result; - nodec::StringBuilderStream(result) << 'A' << 'B' << 'C'; + nodec::StringBuilder(result) << 'A' << 'B' << 'C'; CHECK(result == "ABC"); } } diff --git a/tests/string_builder/benchmark_test.cpp b/tests/string_builder/benchmark_test.cpp index 303c984..114d20f 100644 --- a/tests/string_builder/benchmark_test.cpp +++ b/tests/string_builder/benchmark_test.cpp @@ -61,10 +61,10 @@ TEST_CASE("Benchmark - Simple Concatenation") { // nodec::StringBuilderStreamのベンチマーク { - BenchmarkTimer timer("StringBuilderStream - Simple concatenation"); + BenchmarkTimer timer("StringBuilder - Simple concatenation"); for (int i = 0; i < iterations; ++i) { std::string result; - nodec::StringBuilderStream builder(result); + nodec::StringBuilder builder(result); builder << "Hello" << " " << "World" << i; // 結果を使用して最適化を防ぐ @@ -107,10 +107,10 @@ TEST_CASE("Benchmark - Numeric Formatting") { // nodec::FormatterStreamのベンチマーク { - BenchmarkTimer timer("StringBuilderStream - Numeric formatting"); + BenchmarkTimer timer("StringBuilder - Numeric formatting"); for (int i = 0; i < iterations; ++i) { std::string result; - nodec::StringBuilderStream builder(result); + nodec::StringBuilder builder(result); builder << "Number: " << i << ", Float: " << (i * 3.14) << ", Hex: " << std::hex << i; volatile auto len = result.length(); (void)len; @@ -156,10 +156,10 @@ TEST_CASE("Benchmark - Long Chain") { // StringBuilderStreamのベンチマーク { - BenchmarkTimer timer("StringBuilderStream - Long chain"); + BenchmarkTimer timer("StringBuilder - Long chain"); for (int i = 0; i < iterations; ++i) { std::string result; - nodec::StringBuilderStream builder(result); + nodec::StringBuilder builder(result); builder << "Start: " << i << ", Value1: " << (i * 2) << ", Value2: " << (i * 3.5) diff --git a/tests/string_builder/oss_compat_test.cpp b/tests/string_builder/oss_compat_test.cpp index d0bbb35..8f380df 100644 --- a/tests/string_builder/oss_compat_test.cpp +++ b/tests/string_builder/oss_compat_test.cpp @@ -6,146 +6,146 @@ #include -TEST_CASE("StringBuilderStream - Enhanced Manipulator Support") { +TEST_CASE("StringBuilder - Enhanced Manipulator Support") { // setw テスト std::string buffer1; - nodec::StringBuilderStream stream1(buffer1); + nodec::StringBuilder stream1(buffer1); stream1 << std::setw(10) << 42; CHECK(buffer1 == " 42"); // 右揃え、幅10 // setfill テスト std::string buffer2; - nodec::StringBuilderStream stream2(buffer2); + nodec::StringBuilder stream2(buffer2); stream2 << std::setfill('0') << std::setw(5) << 123; CHECK(buffer2 == "00123"); // setprecision テスト std::string buffer3; - nodec::StringBuilderStream stream3(buffer3); + nodec::StringBuilder stream3(buffer3); stream3 << std::fixed << std::setprecision(2) << 3.14159; CHECK(buffer3 == "3.14"); // 組み合わせテスト std::string buffer4; - nodec::StringBuilderStream stream4(buffer4); + nodec::StringBuilder stream4(buffer4); stream4 << std::setfill('*') << std::setw(8) << "Hi"; CHECK(buffer4 == "******Hi"); } -TEST_CASE("StringBuilderStream - Number Base Support") { +TEST_CASE("StringBuilder - Number Base Support") { // 16進数テスト(小文字) std::string buffer1; - nodec::StringBuilderStream stream1(buffer1); + nodec::StringBuilder stream1(buffer1); stream1 << std::hex << 255; CHECK(buffer1 == "ff"); // 16進数テスト(大文字) std::string buffer2; - nodec::StringBuilderStream stream2(buffer2); + nodec::StringBuilder stream2(buffer2); stream2 << std::hex << std::uppercase << 255; CHECK(buffer2 == "FF"); // 16進数 with showbase std::string buffer3; - nodec::StringBuilderStream stream3(buffer3); + nodec::StringBuilder stream3(buffer3); stream3 << std::hex << std::showbase << 255; CHECK(buffer3 == "0xff"); // 8進数テスト std::string buffer4; - nodec::StringBuilderStream stream4(buffer4); + nodec::StringBuilder stream4(buffer4); stream4 << std::oct << 64; CHECK(buffer4 == "100"); // 8進数 with showbase std::string buffer5; - nodec::StringBuilderStream stream5(buffer5); + nodec::StringBuilder stream5(buffer5); stream5 << std::oct << std::showbase << 64; CHECK(buffer5 == "0100"); // 10進数に戻す std::string buffer6; - nodec::StringBuilderStream stream6(buffer6); + nodec::StringBuilder stream6(buffer6); stream6 << std::hex << 16 << ", " << std::dec << 16; CHECK(buffer6 == "10, 16"); } -TEST_CASE("StringBuilderStream - Alignment Support") { +TEST_CASE("StringBuilder - Alignment Support") { // 左揃え std::string buffer1; - nodec::StringBuilderStream stream1(buffer1); + nodec::StringBuilder stream1(buffer1); stream1 << std::left << std::setw(8) << "Hi"; CHECK(buffer1 == "Hi "); // 右揃え(デフォルト) std::string buffer2; - nodec::StringBuilderStream stream2(buffer2); + nodec::StringBuilder stream2(buffer2); stream2 << std::right << std::setw(8) << "Hi"; CHECK(buffer2 == " Hi"); // 内部揃え(符号付き数値) std::string buffer3; - nodec::StringBuilderStream stream3(buffer3); + nodec::StringBuilder stream3(buffer3); stream3 << std::internal << std::setfill('0') << std::setw(8) << -42; CHECK(buffer3 == "-0000042"); // 内部揃え(16進数) std::string buffer4; - nodec::StringBuilderStream stream4(buffer4); + nodec::StringBuilder stream4(buffer4); stream4 << std::hex << std::showbase << std::internal << std::setfill('0') << std::setw(8) << 255; CHECK(buffer4 == "0x0000ff"); } -TEST_CASE("StringBuilderStream - Bool Support") { +TEST_CASE("StringBuilder - Bool Support") { // 数値としてのbool std::string buffer1; - nodec::StringBuilderStream stream1(buffer1); + nodec::StringBuilder stream1(buffer1); stream1 << true << ", " << false; CHECK(buffer1 == "1, 0"); // boolalpha std::string buffer2; - nodec::StringBuilderStream stream2(buffer2); + nodec::StringBuilder stream2(buffer2); stream2 << std::boolalpha << true << ", " << false; CHECK(buffer2 == "true, false"); // noboolalpha std::string buffer3; - nodec::StringBuilderStream stream3(buffer3); + nodec::StringBuilder stream3(buffer3); stream3 << std::boolalpha << true << ", " << std::noboolalpha << false; CHECK(buffer3 == "true, 0"); } -TEST_CASE("StringBuilderStream - Float Formatting") { +TEST_CASE("StringBuilder - Float Formatting") { // fixed フォーマット std::string buffer1; - nodec::StringBuilderStream stream1(buffer1); + nodec::StringBuilder stream1(buffer1); stream1 << std::fixed << std::setprecision(3) << 3.14159; CHECK(buffer1 == "3.142"); // scientific フォーマット std::string buffer2; - nodec::StringBuilderStream stream2(buffer2); + nodec::StringBuilder stream2(buffer2); stream2 << std::scientific << std::setprecision(2) << 1234.5; CHECK(buffer2 == "1.23e+03"); // uppercase scientific std::string buffer3; - nodec::StringBuilderStream stream3(buffer3); + nodec::StringBuilder stream3(buffer3); stream3 << std::scientific << std::uppercase << std::setprecision(2) << 1234.5; CHECK(buffer3 == "1.23E+03"); } -TEST_CASE("StringBuilderStream - ostringstream Compatibility Check") { +TEST_CASE("StringBuilder - ostringstream Compatibility Check") { // 同じフォーマットでostringreamと結果比較 int value = 42; - // StringBuilderStream + // StringBuilder std::string buffer1; - nodec::StringBuilderStream stream1(buffer1); + nodec::StringBuilder stream1(buffer1); stream1 << std::setfill('0') << std::setw(6) << value; // ostringstream @@ -157,7 +157,7 @@ TEST_CASE("StringBuilderStream - ostringstream Compatibility Check") { // 16進数比較 std::string buffer2; - nodec::StringBuilderStream stream2(buffer2); + nodec::StringBuilder stream2(buffer2); stream2 << std::hex << std::uppercase << 255; std::ostringstream oss_hex; @@ -167,10 +167,10 @@ TEST_CASE("StringBuilderStream - ostringstream Compatibility Check") { CHECK(buffer2 == oss_hex_result); } -TEST_CASE("StringBuilderStream - Complex Format Chains") { +TEST_CASE("StringBuilder - Complex Format Chains") { // 複雑なフォーマットチェーン std::string buffer; - nodec::StringBuilderStream stream(buffer); + nodec::StringBuilder stream(buffer); stream << "Value: " << std::hex << std::uppercase << std::showbase << std::setfill('0') << std::setw(8) << 255 << ", Float: " << std::dec << std::fixed << std::setprecision(2) << 3.14159 From afe8fac23b6014d3ed1808bf5ba3238248aed570 Mon Sep 17 00:00:00 2001 From: IOE Date: Sun, 13 Jul 2025 09:37:39 +0900 Subject: [PATCH 6/6] add test --- .github/workflows/build.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1c3a88f..69c58dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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