在以前的信友队中,向stderr
输出会被判题机正常忽略,但是现在会被判定为RuntimeError。
问题代码:
// A
// Template generated by oi_helper (https://github.com/onion108/oi_helper)
//
#include <functional>
#include <iostream>
#include <algorithm>
#include <map>
#include <stdexcept>
#include <string>
#include <utility>
#include <fstream>
#include <deque>
#include <chrono>
using std::cin;
using std::cout;
using std::endl;
// Utility functions & structs
template <typename T, typename E>
struct Result {
private:
union _InternalStorageType {
T success;
E error;
};
bool _succeeded;
_InternalStorageType _value;
bool _valid;
void _ensure_valid() const {
if (!_valid) throw std::runtime_error("Trying to interact with an invalid Result object. ");
}
public:
Result() : _valid(false) {}
static Result Ok(T value) {
Result res;
res._value.success = value;
res._succeeded = true;
res._valid = true;
return res;
}
static Result Err(E value) {
Result res;
res._value.error = value;
res._succeeded = false;
res._valid = true;
return res;
}
bool is_ok() const {
return _succeeded && _valid;
}
bool is_err() const {
return (!is_ok()) && _valid;
}
T expect(const std::string& message) const {
_ensure_valid();
if (_succeeded) {
return _value.success;
} else {
throw std::runtime_error(message);
}
}
E expect_err(const std::string& message) const {
_ensure_valid();
if (_succeeded) {
return _value.success;
} else {
throw std::runtime_error(message);
}
}
T unwrap() const {
return expect("Trying to unwrap an Err object. ");
}
E unwrap_err() const {
return expect_err("Trying to unwrap an Err object. ");
}
};
class Logger {
public:
static const bool MAKE_CRASH_REPORT = true;
static const bool STORE_LOG = true;
static const bool DEBUG_MODE = true;
static const int SAVED_LOG_COUNT = 100;
static const bool OUTPUT_LOG = true;
enum class LogLevel {
Debug,
Info,
Warn,
Error,
Fatal,
};
struct LogEntry {
LogLevel level;
std::string message;
};
private:
std::deque<LogEntry> _logs;
void _write_level(LogLevel level, std::ostream& out) {
switch (level) {
case LogLevel::Debug:
out << "[DEBUG]";
break;
case LogLevel::Info:
out << "[INFO]";
break;
case LogLevel::Warn:
out << "[WARN]";
break;
case LogLevel::Error:
out << "[ERROR]";
break;
case LogLevel::Fatal:
out << "[FATAL]";
break;
}
}
void _write_log(LogEntry log, std::ostream& out) {
_write_level(log.level, out);
out << " " << log.message;
}
public:
Logger() : _logs() {}
void log(LogLevel level, const std::string& message, std::ostream& out = std::cerr) {
if (STORE_LOG) {
_logs.push_back(LogEntry {
.level = level,
.message = message,
});
}
if (!DEBUG_MODE && level == LogLevel::Debug) return;
if (OUTPUT_LOG) {
_write_level(level, out);
out << " " << message << endl;
}
if (_logs.size() > SAVED_LOG_COUNT) {
_logs.pop_front();
}
}
void info(const std::string& message, std::ostream& out = std::cerr) {
log(LogLevel::Info, message, out);
}
void warn(const std::string& message, std::ostream& out = std::cerr) {
log(LogLevel::Warn, message, out);
}
void debug(const std::string& message, std::ostream& out = std::cerr) {
log(LogLevel::Debug, message, out);
}
void error(const std::string& message, std::ostream& out = std::cerr) {
log(LogLevel::Error, message, out);
}
void fatal(const std::string& message, std::ostream& out = std::cerr) {
log(LogLevel::Fatal, message, out);
}
void make_crash_report(const std::string& path) {
if (MAKE_CRASH_REPORT) {
std::ofstream ofs(path);
if (!ofs.is_open()) return;
ofs << "=======CRASH REPORT======\n";
auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
ofs << "Oops! Program crashed at " << std::ctime(&now) << endl;
ofs << "If you are user and you see this, please contact the author of this program and send the entire file to them. \n";
ofs << endl;
ofs << "Last logs from crash (" << _logs.size() << " logs saved): \n";
for (auto i : _logs) {
_write_log(i, ofs);
ofs << "\n";
}
ofs << endl;
ofs << "";
ofs.close();
}
}
};
Logger logger;
enum class IOException {
BadStateException,
};
Result<int, IOException> read_int() {
int _buf;
cin >> _buf;
if (cin.good()) {
return Result<int, IOException>::Ok(_buf);
} else {
return Result<int, IOException>::Err(IOException::BadStateException);
}
}
template <typename CounterType = int>
void repeat(CounterType times, std::function<void(CounterType)> operation) {
for (CounterType _counter = 0; _counter < times; _counter++) {
operation(_counter);
}
}
class ProblemGraph {
private:
std::map<std::pair<int, int>, bool> _connected_map;
public:
ProblemGraph() : _connected_map() {}
// Connect two edges.
void connect(int i, int j) {
_connected_map[std::pair<int, int>(i, j)] = true;
_connected_map[std::pair<int, int>(j, i)] = true;
}
// Check if `i` and `j` are connected.
bool is_connected(int i, int j) {
return _connected_map[std::pair<int, int>(i, j)];
}
static ProblemGraph create_then(std::function<void(ProblemGraph&)> operation) {
ProblemGraph result;
operation(result);
return result;
}
ProblemGraph& for_each_edge(std::function<void(int, int)> operation) {
for (auto i : _connected_map) {
if (i.second) {
operation(i.first.first, i.first.second);
}
}
return *this;
}
ProblemGraph& then(std::function<void(ProblemGraph&)> operation) {
operation(*this);
return *this;
}
};
class DisjointSetUnion {
private:
std::map<int, int> _parentmap;
public:
DisjointSetUnion() : _parentmap() {}
void make_set(int v) {
_parentmap[v] = v;
}
int find_set(int v) {
if (v == _parentmap[v]) {
return v;
} else {
return find_set(_parentmap[v]);
}
}
void union_sets(int a, int b) {
a = find_set(a);
b = find_set(b);
if (a != b) {
_parentmap[b] = a;
}
}
void for_each_topmost(std::function<void(int value)> operation) {
for (auto i : _parentmap) {
if (i.first == i.second) {
operation(i.first);
}
}
}
};
void program_main() {
// Read in the graph.
logger.debug("Reading node_count and side_count. ");
int node_count = read_int().expect("Cannot read node_count"),
side_count = read_int().expect("Cannot read side_count");
int counter = 0;
logger.debug("Creating dsu object. ");
DisjointSetUnion dsu;
repeat<int>(node_count, [&dsu](int i) {
dsu.make_set(i+1);
});
ProblemGraph::create_then([&](ProblemGraph& it) {
repeat<int>(side_count, [&](int ignored) {
logger.debug("Reading two nodes to make an edge. ");
int u = read_int().expect("Cannot read the first node of an edge"),
v = read_int().expect("Cannot read the second node of an edge");
it.connect(u, v);
dsu.make_set(u);
dsu.make_set(v);
});
}).for_each_edge([&dsu](int u, int v) {
dsu.union_sets(u, v);
}).then([&dsu, &counter](ProblemGraph& ignored) {
dsu.for_each_topmost([&counter](int ignored) {
logger.info(std::string("Find a node with id ") + std::to_string(ignored));
counter += 1;
});
if (counter == 0) {
logger.warn("The result is 0. Algorithm may failed or there isn't any node given. ");
}
cout << counter << endl;
});
}
int main() {
try {
program_main();
} catch (std::runtime_error e) {
std::cerr << "Aborted because of uncaught runtime error: " << e.what() << endl;
logger.fatal(std::string("Aborted because of uncaught runtime error: ") + e.what());
logger.make_crash_report("crash_report.log");
} catch (...) {
std::cerr << "Aborted because of uncaught unknown exception. " << endl;
logger.fatal("Aborted because of unknown exception. ");
logger.make_crash_report("crash_report.log");
}
}
最小可复现代码示例:
#include <iostream>
int main() {
// Let's pretend the output that the problem expected is "Hello, World!". Line feeds don't matter.
std::cout << "Hello, World!" << std::endl;/
// However, uncomment the following code will cause a RuntimeError.
// std::cerr << "Some debug info here.... ";
// According to C++11 or later standards, lacking of `return 0;` doesn't matter here.
}