在`std::cerr`输出时判定RuntimeError的问题

在以前的信友队中,向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.
}
2 个赞

用这种方式在本地C++中不会报错,同样的,在本地数组越界时也会输出一个不知对错的结果。
因为Runtime Error不属于编译错误。在一般的判题机上,这种非stdout的输出不会被作为结果进行比对;正如数组越界一般,在给定的内存外出现了一些数据即会被判定为Runtime Error,用这种方式的输出同样不在给定内存范围内(其实一般判题机是真没想到有人会这么写

3 个赞