Skip to content

Commit

Permalink
fix: Do not use cout or clog stream directly
Browse files Browse the repository at this point in the history
  • Loading branch information
yzy-1 committed Aug 4, 2024
1 parent b56c11b commit 6a369c3
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 115 deletions.
52 changes: 29 additions & 23 deletions docs/pages/user-guide/advanced/initializer.en.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,30 @@ Here is a sample implementation:

```cpp
struct CmsReporter : cplib::checker::Reporter {
[[noreturn]] auto report(const cplib::checker::Report &report)
-> void override {
std::cout << std::fixed << std::setprecision(9) << report.score << '\n';
[[noreturn]] auto report(const cplib::checker::Report& report) -> void override {
// Do not use std::cout or std::clog directly: need to prevent the behavior of the user code
// setting the format for them from affecting the Reporter output
std::ostream score_stream(std::cout.rdbuf());
std::ostream status_stream(std::clog.rdbuf());

score_stream << std::fixed << std::setprecision(9) << report.score << '\n';

switch (report.status) {
case cplib::checker::Report::Status::INTERNAL_ERROR:
std::clog << "FAIL " << report.status << '\n';
std::exit(1);
case cplib::checker::Report::Status::ACCEPTED:
std::clog << "translate:success\n";
break;
case cplib::checker::Report::Status::WRONG_ANSWER:
std::clog << "translate:wrong\n";
break;
case cplib::checker::Report::Status::PARTIALLY_CORRECT:
std::clog << "translate:partial\n";
break;
default:
std::clog << "FAIL invalid status\n";
std::exit(1);
case cplib::checker::Report::Status::INTERNAL_ERROR:
status_stream << "FAIL " << report.status << '\n';
std::exit(1);
case cplib::checker::Report::Status::ACCEPTED:
status_stream << "translate:success\n";
break;
case cplib::checker::Report::Status::WRONG_ANSWER:
status_stream << "translate:wrong\n";
break;
case cplib::checker::Report::Status::PARTIALLY_CORRECT:
status_stream << "translate:partial\n";
break;
default:
status_stream << "FAIL invalid status\n";
std::exit(1);
}
std::exit(0);
}
Expand All @@ -52,15 +56,17 @@ struct CmsReporter : cplib::checker::Reporter {
struct CmsInitializer : cplib::checker::Initializer {
auto init(std::string_view arg0, const std::vector<std::string> &args)
-> void override {
if (args.size() != 3) {
std::cerr << "Usage: " << arg0 << " <inf> <ans> <ouf>\n";
std::exit(1);
}

auto& state = this->state();

// Set the reporter as early as possible to ensure that errors reported during init can also be
// reported in the correct way.
state.reporter = std::make_unique<CmsReporter>();

if (args.size() != 3) {
cplib::panic("Program must be run with the following arguments:\n <inf> <ans> <ouf>");
std::exit(1);
}

auto inf = args[0];
auto ouf = args[2];
auto ans = args[1];
Expand Down
52 changes: 29 additions & 23 deletions docs/pages/user-guide/advanced/initializer.zh.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,30 @@ Initializer 需要做的事情可分为三步:

```cpp
struct CmsReporter : cplib::checker::Reporter {
[[noreturn]] auto report(const cplib::checker::Report &report)
-> void override {
std::cout << std::fixed << std::setprecision(9) << report.score << '\n';
[[noreturn]] auto report(const cplib::checker::Report& report) -> void override {
// Do not use std::cout or std::clog directly: need to prevent the behavior of the user code
// setting the format for them from affecting the Reporter output
std::ostream score_stream(std::cout.rdbuf());
std::ostream status_stream(std::clog.rdbuf());

score_stream << std::fixed << std::setprecision(9) << report.score << '\n';

switch (report.status) {
case cplib::checker::Report::Status::INTERNAL_ERROR:
std::clog << "FAIL " << report.status << '\n';
std::exit(1);
case cplib::checker::Report::Status::ACCEPTED:
std::clog << "translate:success\n";
break;
case cplib::checker::Report::Status::WRONG_ANSWER:
std::clog << "translate:wrong\n";
break;
case cplib::checker::Report::Status::PARTIALLY_CORRECT:
std::clog << "translate:partial\n";
break;
default:
std::clog << "FAIL invalid status\n";
std::exit(1);
case cplib::checker::Report::Status::INTERNAL_ERROR:
status_stream << "FAIL " << report.status << '\n';
std::exit(1);
case cplib::checker::Report::Status::ACCEPTED:
status_stream << "translate:success\n";
break;
case cplib::checker::Report::Status::WRONG_ANSWER:
status_stream << "translate:wrong\n";
break;
case cplib::checker::Report::Status::PARTIALLY_CORRECT:
status_stream << "translate:partial\n";
break;
default:
status_stream << "FAIL invalid status\n";
std::exit(1);
}
std::exit(0);
}
Expand All @@ -52,15 +56,17 @@ struct CmsReporter : cplib::checker::Reporter {
struct CmsInitializer : cplib::checker::Initializer {
auto init(std::string_view arg0, const std::vector<std::string> &args)
-> void override {
if (args.size() != 3) {
std::cerr << "Usage: " << arg0 << " <inf> <ans> <ouf>\n";
std::exit(1);
}

auto& state = this->state();

// Set the reporter as early as possible to ensure that errors reported during init can also be
// reported in the correct way.
state.reporter = std::make_unique<CmsReporter>();

if (args.size() != 3) {
cplib::panic("Program must be run with the following arguments:\n <inf> <ans> <ouf>");
std::exit(1);
}

auto inf = args[0];
auto ouf = args[2];
auto ans = args[1];
Expand Down
34 changes: 20 additions & 14 deletions include/checker.i.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <iostream>
#include <map>
#include <memory>
#include <ostream>
#include <string>
#include <string_view>
#include <utility>
Expand Down Expand Up @@ -183,7 +184,8 @@ inline auto State::quit(Report report) -> void {

reporter->report(report);

std::clog << "Unrecoverable error: Reporter didn't exit the program\n";
std::ostream stream(std::clog.rdbuf());
stream << "Unrecoverable error: Reporter didn't exit the program\n";
std::exit(EXIT_FAILURE);
}

Expand Down Expand Up @@ -332,41 +334,45 @@ inline auto JsonReporter::report(const Report& report) -> void {
map.emplace("reader_trace_stack", trace_stack_->to_json());
}

std::clog << json::Map(std::move(map)).to_string() << '\n';
std::ostream stream(std::clog.rdbuf());
stream << json::Map(std::move(map)).to_string() << '\n';
std::exit(report.status == Report::Status::ACCEPTED ? EXIT_SUCCESS : EXIT_FAILURE);
}

inline auto PlainTextReporter::report(const Report& report) -> void {
std::clog << std::fixed << std::setprecision(2)
<< detail::status_to_title_string(report.status).c_str() << ", scores "
<< report.score * 100.0 << " of 100.\n";
std::ostream stream(std::clog.rdbuf());

stream << std::fixed << std::setprecision(2) << detail::status_to_title_string(report.status)
<< ", scores " << report.score * 100.0 << " of 100.\n";

if (report.status != Report::Status::ACCEPTED || !report.message.empty()) {
std::clog << report.message << '\n';
stream << report.message << '\n';
}

if (trace_stack_.has_value()) {
std::clog << "\nReader trace stack (most recent variable last):\n";
stream << "\nReader trace stack (most recent variable last):\n";
for (const auto& line : trace_stack_->to_plain_text_lines()) {
std::clog << " " << line << '\n';
stream << " " << line << '\n';
}
}

std::exit(report.status == Report::Status::ACCEPTED ? EXIT_SUCCESS : EXIT_FAILURE);
}

inline auto ColoredTextReporter::report(const Report& report) -> void {
std::clog << std::fixed << std::setprecision(2)
<< detail::status_to_colored_title_string(report.status).c_str()
<< ", scores \x1b[0;33m" << report.score * 100.0 << "\x1b[0m of 100.\n";
std::ostream stream(std::clog.rdbuf());

stream << std::fixed << std::setprecision(2)
<< detail::status_to_colored_title_string(report.status) << ", scores \x1b[0;33m"
<< report.score * 100.0 << "\x1b[0m of 100.\n";
if (report.status != Report::Status::ACCEPTED || !report.message.empty()) {
std::clog << report.message << '\n';
stream << report.message << '\n';
}

if (trace_stack_.has_value()) {
std::clog << "\nReader trace stack (most recent variable last):\n";
stream << "\nReader trace stack (most recent variable last):\n";
for (const auto& line : trace_stack_->to_colored_text_lines()) {
std::clog << " " << line << '\n';
stream << " " << line << '\n';
}
}

Expand Down
18 changes: 12 additions & 6 deletions include/generator.i.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <map>
#include <memory>
#include <optional>
#include <ostream>
#include <set>
#include <string>
#include <string_view>
Expand Down Expand Up @@ -97,7 +98,8 @@ inline auto State::quit(const Report& report) -> void {

reporter->report(report);

std::clog << "Unrecoverable error: Reporter didn't exit the program\n";
std::ostream stream(std::clog.rdbuf());
stream << "Unrecoverable error: Reporter didn't exit the program\n";
std::exit(EXIT_FAILURE);
}

Expand Down Expand Up @@ -281,30 +283,34 @@ inline auto JsonReporter::report(const Report& report) -> void {
map.emplace("status", std::make_unique<json::String>(std::string(report.status.to_string())));
map.emplace("message", std::make_unique<json::String>(report.message));

std::clog << json::Map(std::move(map)).to_string() << '\n';
std::ostream stream(std::clog.rdbuf());
stream << json::Map(std::move(map)).to_string() << '\n';
std::exit(report.status == Report::Status::OK ? EXIT_SUCCESS : EXIT_FAILURE);
}

inline auto PlainTextReporter::report(const Report& report) -> void {
std::ostream stream(std::clog.rdbuf());

if (report.status == Report::Status::OK && report.message.empty()) {
// Do nothing when the report is OK and message is empty.
std::exit(report.status == Report::Status::OK ? EXIT_SUCCESS : EXIT_FAILURE);
}

std::clog << detail::status_to_title_string(report.status).c_str() << ".\n"
<< report.message << '\n';
stream << detail::status_to_title_string(report.status) << ".\n" << report.message << '\n';

std::exit(report.status == Report::Status::OK ? EXIT_SUCCESS : EXIT_FAILURE);
}

inline auto ColoredTextReporter::report(const Report& report) -> void {
std::ostream stream(std::clog.rdbuf());

if (report.status == Report::Status::OK && report.message.empty()) {
// Do nothing when the report is OK and message is empty.
std::exit(report.status == Report::Status::OK ? EXIT_SUCCESS : EXIT_FAILURE);
}

std::clog << detail::status_to_colored_title_string(report.status).c_str() << ".\n"
<< report.message << '\n';
stream << detail::status_to_colored_title_string(report.status) << ".\n"
<< report.message << '\n';

std::exit(report.status == Report::Status::OK ? EXIT_SUCCESS : EXIT_FAILURE);
}
Expand Down
34 changes: 20 additions & 14 deletions include/interactor.i.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <iostream>
#include <map>
#include <memory>
#include <ostream>
#include <string>
#include <string_view>
#include <utility>
Expand Down Expand Up @@ -159,7 +160,8 @@ inline auto State::quit(const Report& report) -> void {

reporter->report(report);

std::clog << "Unrecoverable error: Reporter didn't exit the program\n";
std::ostream stream(std::clog.rdbuf());
stream << "Unrecoverable error: Reporter didn't exit the program\n";
std::exit(EXIT_FAILURE);
}

Expand Down Expand Up @@ -323,41 +325,45 @@ inline auto JsonReporter::report(const Report& report) -> void {
map.emplace("reader_trace_stack", trace_stack_->to_json());
}

std::clog << json::Map(std::move(map)).to_string() << '\n';
std::ostream stream(std::clog.rdbuf());
stream << json::Map(std::move(map)).to_string() << '\n';
std::exit(report.status == Report::Status::ACCEPTED ? EXIT_SUCCESS : EXIT_FAILURE);
}

inline auto PlainTextReporter::report(const Report& report) -> void {
std::clog << std::fixed << std::setprecision(2)
<< detail::status_to_title_string(report.status).c_str() << ", scores "
<< report.score * 100.0 << " of 100.\n";
std::ostream stream(std::clog.rdbuf());

stream << std::fixed << std::setprecision(2) << detail::status_to_title_string(report.status)
<< ", scores " << report.score * 100.0 << " of 100.\n";

if (report.status != Report::Status::ACCEPTED || !report.message.empty()) {
std::clog << report.message << '\n';
stream << report.message << '\n';
}

if (trace_stack_.has_value()) {
std::clog << "\nReader trace stack (most recent variable last):\n";
stream << "\nReader trace stack (most recent variable last):\n";
for (const auto& line : trace_stack_->to_plain_text_lines()) {
std::clog << " " << line << '\n';
stream << " " << line << '\n';
}
}

std::exit(report.status == Report::Status::ACCEPTED ? EXIT_SUCCESS : EXIT_FAILURE);
}

inline auto ColoredTextReporter::report(const Report& report) -> void {
std::clog << std::fixed << std::setprecision(2)
<< detail::status_to_colored_title_string(report.status).c_str()
<< ", scores \x1b[0;33m" << report.score * 100.0 << "\x1b[0m of 100.\n";
std::ostream stream(std::clog.rdbuf());

stream << std::fixed << std::setprecision(2)
<< detail::status_to_colored_title_string(report.status) << ", scores \x1b[0;33m"
<< report.score * 100.0 << "\x1b[0m of 100.\n";
if (report.status != Report::Status::ACCEPTED || !report.message.empty()) {
std::clog << report.message << '\n';
stream << report.message << '\n';
}

if (trace_stack_.has_value()) {
std::clog << "\nReader trace stack (most recent variable last):\n";
stream << "\nReader trace stack (most recent variable last):\n";
for (const auto& line : trace_stack_->to_colored_text_lines()) {
std::clog << " " << line << '\n';
stream << " " << line << '\n';
}
}

Expand Down
3 changes: 2 additions & 1 deletion include/utils.i.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ inline auto hex_encode(std::string_view s) -> std::string {
// Impl panic {{{
namespace detail {
inline UniqueFunction<auto(std::string_view)->void> panic_impl = [](std::string_view s) {
std::clog << "Unrecoverable error: " << s << '\n';
std::ostream stream(std::clog.rdbuf());
stream << "Unrecoverable error: " << s << '\n';
exit(EXIT_FAILURE);
};
} // namespace detail
Expand Down
Loading

0 comments on commit 6a369c3

Please sign in to comment.