diff --git a/src/impls/cpp/Makefile b/src/impls/cpp/Makefile index 3b9767c..1bc5103 100644 --- a/src/impls/cpp/Makefile +++ b/src/impls/cpp/Makefile @@ -1,11 +1,19 @@ CXX = g++ #CXX = clang++ -CXXFLAGS = -std=c++2a -O2 -Wall +CXXFLAGS = -std=c++23 -O2 -Wall + +# For MinGW (Windows), specify `-lstdc++exp` to use std::print. +# ref: https://www.reddit.com/r/cpp_questions/comments/1bsh7nd/undefined_reference_to_std_open_terminal/ +CXX_TARGET := $(shell $(CXX) -dumpmachine) + +ifneq (,$(findstring w64-mingw32,$(CXX_TARGET))) +LDFLAGS += -lstdc++exp +endif all: polish polish: polish.o - $(CXX) polish.o -o polish + $(CXX) polish.o -o polish $(LDFLAGS) polish.o: polish.cpp $(CXX) $(CXXFLAGS) -c polish.cpp diff --git a/src/impls/cpp/polish.cpp b/src/impls/cpp/polish.cpp index bf7ffca..35bc0f2 100644 --- a/src/impls/cpp/polish.cpp +++ b/src/impls/cpp/polish.cpp @@ -1,14 +1,13 @@ // SPDX-FileCopyrightText: 2022 smdn // SPDX-License-Identifier: MIT #include +#include #include #include -#include #include #include #include -#include -#include +#include #include #include @@ -28,31 +27,28 @@ class Node { // 二分木を巡回し、ノードの行きがけ・通りがけ・帰りがけに指定された関数をコールバックするメソッド void traverse( - std::function on_visit, // ノードの行きがけにコールバックする関数 - std::function on_transit, // ノードの通りがけにコールバックする関数 - std::function on_leave // ノードの帰りがけにコールバックする関数 + std::invocable auto on_visit, // ノードの行きがけにコールバックする関数 + std::invocable auto on_transit, // ノードの通りがけにコールバックする関数 + std::invocable auto on_leave // ノードの帰りがけにコールバックする関数 ); // 後行順序訪問(帰りがけ順)で二分木を巡回して // すべてのノードの演算子または項をstreamに出力するメソッド - void write_postorder(std::ostream& stream); + void write_postorder(std::FILE* stream); // 中間順序訪問(通りがけ順)で二分木を巡回して // すべてのノードの演算子または項をstreamに出力するメソッド - void write_inorder(std::ostream& stream); + void write_inorder(std::FILE* stream); // 先行順序訪問(行きがけ順)で二分木を巡回して // すべてのノードの演算子または項をstreamに出力するメソッド - void write_preorder(std::ostream& stream); + void write_preorder(std::FILE* stream); // 後行順序訪問(帰りがけ順)で二分木を巡回して、二分木全体の値を計算するメソッド // すべてのノードの値が計算できた場合はtrue、そうでない場合(記号を含む場合など)はfalseを返す // 計算結果はresult_valueに代入する bool calculate_expression_tree(double& result_value); - // 演算結果の数値を文字列化するためのメソッド - static std::string format_number(const double& number) noexcept; - private: // 式expression内の括弧の対応を検証するメソッド // 開き括弧と閉じ括弧が同数でない場合はエラーとする @@ -256,85 +252,82 @@ std::string::size_type Node::get_operator_position(const std::string_view& expre } void Node::traverse( - std::function on_visit, - std::function on_transit, - std::function on_leave + std::invocable auto on_visit, + std::invocable auto on_transit, + std::invocable auto on_leave ) { // このノードの行きがけに行う動作をコールバックする - if (on_visit) - on_visit(*this); + on_visit(*this); // 左に子ノードをもつ場合は、再帰的に巡回する if (left) left->traverse(on_visit, on_transit, on_leave); // このノードの通りがけに行う動作をコールバックする - if (on_transit) - on_transit(*this); + on_transit(*this); // 右に子ノードをもつ場合は、再帰的に巡回する if (right) right->traverse(on_visit, on_transit, on_leave); // このノードの帰りがけに行う動作をコールバックする - if (on_leave) - on_leave(*this); + on_leave(*this); } -void Node::write_postorder(std::ostream& stream) +void Node::write_postorder(std::FILE* const stream) { // 巡回を開始する traverse( - nullptr, // ノードへの行きがけには何もしない - nullptr, // ノードの通りがけには何もしない + [](const Node&) {}, // ノードへの行きがけには何もしない + [](const Node&) {}, // ノードの通りがけには何もしない // ノードからの帰りがけに、ノードの演算子または項を出力する // (読みやすさのために項の後に空白を補って出力する) - [&stream](Node& node) { stream << node.expression << ' '; } + [&stream](const Node& node) { std::print(stream, "{} ", node.expression); } ); } -void Node::write_inorder(std::ostream& stream) +void Node::write_inorder(std::FILE* const stream) { // 巡回を開始する traverse( // ノードへの行きがけに、必要なら開き括弧を補う - [&stream](Node& node) { + [&stream](const Node& node) { // 左右に項を持つ場合、読みやすさのために項の前(行きがけ)に開き括弧を補う if (node.left && node.right) - stream << '('; + std::print(stream, "("); }, // ノードの通りがけに、ノードの演算子または項を出力する - [&stream](Node& node) { + [&stream](const Node& node) { // 左に子ノードを持つ場合は、読みやすさのために空白を補う if (node.left) - stream << ' '; + std::print(stream, " "); // 左の子ノードから右の子ノードへ巡回する際に、ノードの演算子または項を出力する - stream << node.expression; + std::print(stream, "{}", node.expression); // 右に子ノードを持つ場合は、読みやすさのために空白を補う if (node.right) - stream << ' '; + std::print(stream, " "); }, // ノードからの帰りがけに、必要なら閉じ括弧を補う - [&stream](Node& node) { + [&stream](const Node& node) { // 左右に項を持つ場合、読みやすさのために項の後(帰りがけ)に閉じ括弧を補う if (node.left && node.right) - stream << ')'; + std::print(stream, ")"); } ); } -void Node::write_preorder(std::ostream& stream) +void Node::write_preorder(std::FILE* const stream) { // 巡回を開始する traverse( // ノードへの行きがけに、ノードの演算子または項を出力する // (読みやすさのために項の後に空白を補って出力する) - [&stream](Node& node) { stream << node.expression << ' '; }, - nullptr, // ノードの通りがけ時には何もしない - nullptr // ノードからの帰りがけ時には何もしない + [&stream](const Node& node) { std::print(stream, "{} ", node.expression); }, + [](const Node&) {}, // ノードの通りがけ時には何もしない + [](const Node&) {} // ノードからの帰りがけ時には何もしない ); } @@ -344,8 +337,8 @@ bool Node::calculate_expression_tree(double& result_value) // ノードからの帰りがけに、ノードが表す部分式から、その値を計算する // 帰りがけに計算することによって、末端の部分木から順次計算し、再帰的に木全体の値を計算する traverse( - nullptr, // ノードへの行きがけには何もしない - nullptr, // ノードの通りがけには何もしない + [](const Node&) {}, // ノードへの行きがけには何もしない + [](const Node&) {}, // ノードの通りがけには何もしない Node::calculate_node // ノードからの帰りがけに、ノードの値を計算する ); @@ -378,10 +371,10 @@ void Node::calculate_node(Node& node) // 現在のノードの演算子に応じて左右の子ノードの値を演算し、 // 演算した結果を文字列に変換して再度expressionに代入することで現在のノードの値とする switch (node.expression.front()) { - case '+': node.expression = format_number(left_operand + right_operand); break; - case '-': node.expression = format_number(left_operand - right_operand); break; - case '*': node.expression = format_number(left_operand * right_operand); break; - case '/': node.expression = format_number(left_operand / right_operand); break; + case '+': node.expression = std::format("{:.17g}", left_operand + right_operand); break; + case '-': node.expression = std::format("{:.17g}", left_operand - right_operand); break; + case '*': node.expression = std::format("{:.17g}", left_operand * right_operand); break; + case '/': node.expression = std::format("{:.17g}", left_operand / right_operand); break; // 上記以外の演算子の場合は計算できないものとして扱い、処理を終える default: return; } @@ -406,26 +399,13 @@ bool Node::parse_number(const std::string_view& expression, double& number) noex return ptr == std::to_address(std::end(expression)); } -std::string Node::format_number(const double& number) noexcept -{ - std::ostringstream stream; - - // %.17g - stream.precision(17); // %.17 - stream - << std::defaultfloat // %g - << number; - - return stream.str(); -} - // main関数。 結果によって次の値を返す。 // 0: 正常終了 (二分木への分割、および式全体の値の計算に成功した場合) // 1: 入力のエラーによる終了 (二分木への分割に失敗した場合) // 2: 計算のエラーによる終了 (式全体の値の計算に失敗した場合) int main() { - std::cout << "input expression: "; + std::print("input expression: "); // 標準入力から二分木に分割したい式を入力する std::string expression; @@ -450,44 +430,44 @@ int main() // 二分木の根(root)ノードを作成し、式全体を格納する root = std::make_unique(expression); - std::cout << "expression: " << expression << std::endl; + std::println("expression: {}", expression); // 根ノードに格納した式を二分木へと分割する root->parse_expression(); } catch (const MalformedExpressionException& err) { - std::cerr << err.what() << std::endl; + std::println(stderr, "{}", err.what()); return 1; } // 分割した二分木を帰りがけ順で巡回して表示する(前置記法/逆ポーランド記法で表示される) - std::cout << "reverse polish notation: "; - root->write_postorder(std::cout); - std::cout << std::endl; + std::print("reverse polish notation: "); + root->write_postorder(stdout); + std::println(); // 分割した二分木を通りがけ順で巡回して表示する(中置記法で表示される) - std::cout << "infix notation: "; - root->write_inorder(std::cout); - std::cout << std::endl; + std::print("infix notation: "); + root->write_inorder(stdout); + std::println(); // 分割した二分木を行きがけ順で巡回して表示する(後置記法/ポーランド記法で表示される) - std::cout << "polish notation: "; - root->write_preorder(std::cout); - std::cout << std::endl; + std::print("polish notation: "); + root->write_preorder(stdout); + std::println(); // 分割した二分木から式全体の値を計算する double result_value; if (root->calculate_expression_tree(result_value)) { // 計算できた場合はその値を表示する - std::cout << "calculated result: " << Node::format_number(result_value) << std::endl; + std::println("calculated result: {:.17g}", result_value); return 0; } else { // (式の一部あるいは全部が)計算できなかった場合は、計算結果の式を中置記法で表示する - std::cout << "calculated expression: "; - root->write_inorder(std::cout); - std::cout << std::endl; + std::print("calculated expression: "); + root->write_inorder(stdout); + std::println(); return 2; } } diff --git a/src/impls/cpp/polish.vcxproj b/src/impls/cpp/polish.vcxproj index 73b1af4..937a3a4 100644 --- a/src/impls/cpp/polish.vcxproj +++ b/src/impls/cpp/polish.vcxproj @@ -1,4 +1,4 @@ - @@ -19,7 +19,7 @@ SPDX-License-Identifier: MIT - stdcpp20 + stdcpp23 /utf-8 Level3