Building Contest Ranking and Student Management Systems with C++ Stream I/O

Overview

The C++ standard library provides a powerful abstraction through streams. The std::ostream class serves as a base for both console output (std::cout) and file output (std::ofstream). Similarly, std::istream is the base for std::cin and std::ifstream. This polymorphic relationship allows functions accepting stream references to work with any derived stream type without modification.

Task 1: ACM Contestant Ranking System

Data Structure Definition

// contestant.hpp
#pragma once
#include <iomanip>
#include <iostream>
#include <string>

struct Contestant {
    long id;
    std::string name;
    std::string department;
    int solved;
    int penalty;
};

inline std::ostream& operator<<(std::ostream& out, const Contestant& c) {
    out << std::left;
    out << std::setw(15) << c.id
        << std::setw(15) << c.name
        << std::setw(15) << c.department
        << std::setw(10) << c.solved
        << std::setw(10) << c.penalty;
    return out;
}

inline std::istream& operator>>(std::istream& in, Contestant& c) {
    in >> c.id >> c.name >> c.department >> c.solved >> c.penalty;
    return in;
}

Utility Functions

// utils.hpp
#pragma once
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include "contestant.hpp"

// ACM ranking: descending by problems solved, ascending by penalty
inline bool rank_contestants(const Contestant& a, const Contestant& b) {
    if (a.solved != b.solved)
        return a.solved > b.solved;
    return a.penalty < b.penalty;
}

inline void output_results(std::ostream& os, const std::vector<Contestant>& data) {
    for (const auto& entry : data)
        os << entry << '\n';
}

inline void display_results(const std::vector<Contestant>& data) {
    output_results(std::cout, data);
}

inline void write_file(const std::string& filename, const std::vector<Contestant>& data) {
    std::ofstream os(filename);
    if (!os)
        throw std::runtime_error("cannot open " + filename);
    output_results(os, data);
}

inline std::vector<Contestant> read_file(const std::string& filename) {
    std::ifstream is(filename);
    if (!is)
        throw std::runtime_error("cannot open " + filename);

    std::string header;
    std::getline(is, header);  // skip header line

    std::vector<Contestant> data;
    Contestant record;
    int index;
    while (is >> index >> record)
        data.push_back(record);

    return data;
}

Main Application

// main.cpp
#include <algorithm>
#include <iostream>
#include <stdexcept>
#include <vector>
#include "contestant.hpp"
#include "utils.hpp"

const std::string input_file = "./data.txt";
const std::string output_file = "./ans.txt";

void run() {
    std::vector<Contestant> participants;

    try {
        participants = read_file(input_file);
        std::sort(participants.begin(), participants.end(), rank_contestants);
        display_results(participants);
        write_file(output_file, participants);
    } catch (const std::exception& e) {
        std::cerr << e.what() << '\n';
    }
}

int main() {
    run();
}

Discussion

Question 1: Polymorphic Stream Usage

std::ostream acts as a common base class for both std::cout (screen output) and std::ofstream (file output). By accepting a reference to the base class, the output_results() funtcion can work with either stream type. This design enables code reuse without modification.

Question 2: Exception Handling

When read_file() attempts to open a non-existent file, it throws a std::runtime_error. The calling code wraps the operation in a try-catch block that catches std::exception (the base class for all standard exceptions). The what() method retrieves the error message, and return terminates the function cleanly.

Question 3: Function Replacement

The display_results() function could be replaced with a direct call to output_results(std::cout, data) since it merely wraps that functionality. The behavior remains identical.


Task 2: Student Management System

Class Declaration

// student.hpp
#pragma once
#include <iostream>
#include <string>

class Student {
public:
    Student() = default;
    ~Student() = default;

    std::string get_major() const;
    int get_score() const;

    friend std::ostream& operator<<(std::ostream& os, const Student& s);
    friend std::istream& operator>>(std::istream& is, Student& s);

private:
    int id;
    std::string name;
    std::string major;
    int score;
};

Manager Class Declaration

// stumgr.hpp
#pragma once
#include <string>
#include <vector>
#include "student.hpp"

class StuMgr {
public:
    void load(const std::string& file);
    void sort_records();
    void display() const;
    void persist(const std::string& file) const;

private:
    void write(std::ostream& os) const;
    std::vector<Student> records;
};

Class Implementation

// student.cpp
#include "student.hpp"

std::string Student::get_major() const {
    return major;
}

int Student::get_score() const {
    return score;
}

std::ostream& operator<<(std::ostream& os, const Student& s) {
    os << s.id << ' ' << s.name << ' ' << s.major << ' ' << s.score;
    return os;
}

std::istream& operator>>(std::istream& is, Student& s) {
    is >> s.id >> s.name >> s.major >> s.score;
    return is;
}
// stumgr.cpp
#include "stumgr.hpp"
#include <fstream>
#include <stdexcept>
#include <algorithm>

void StuMgr::load(const std::string& file) {
    std::ifstream fs(file);
    if (!fs.is_open())
        throw std::runtime_error("cannot open file: " + file);

    records.clear();
    Student s;
    std::string header;
    std::getline(fs, header);

    while (fs >> s)
        records.push_back(s);

    fs.close();
}

void StuMgr::sort_records() {
    std::sort(records.begin(), records.end(),
        [](const Student& a, const Student& b) {
            if (a.get_major() != b.get_major())
                return a.get_major() < b.get_major();
            return a.get_score() > b.get_score();
        });
}

void StuMgr::display() const {
    write(std::cout);
}

void StuMgr::persist(const std::string& file) const {
    std::ofstream fs(file);
    if (!fs.is_open())
        throw std::runtime_error("cannot open file: " + file);
    write(fs);
    fs.close();
}

void StuMgr::write(std::ostream& os) const {
    for (const auto& s : records)
        os << s << '\n';
}

Main Application

// task2.cpp
#include <iostream>
#include "stumgr.hpp"

const std::string input_file = "./data.txt";
const std::string output_file = "./ans.txt";

void show_menu() {
    std::cout << "\n========== Menu ==========\n"
                "1. Load file\n"
                "2. Sort\n"
                "3. Display\n"
                "4. Save to file\n"
                "5. Exit\n"
                "Select: ";
}

void run() {
    StuMgr manager;

    while (true) {
        show_menu();
        int choice;
        std::cin >> choice;

        try {
            switch (choice) {
            case 1:
                manager.load(input_file);
                std::cout << "Load succeeded\n";
                break;
            case 2:
                manager.sort_records();
                std::cout << "Sort completed\n";
                break;
            case 3:
                manager.display();
                std::cout << "Display completed\n";
                break;
            case 4:
                manager.persist(output_file);
                std::cout << "Save succeeded\n";
                break;
            case 5:
                return;
            default:
                std::cout << "Invalid choice\n";
            }
        } catch (const std::exception& e) {
            std::cout << "Error: " << e.what() << '\n';
        }
    }
}

int main() {
    run();
}

Key Concepts Demonstrated

  1. Stream Polymorphism: Both implementations use base class references (std::ostream&, std::istream&) to achieve code reuse across console and file operations.

  2. Exception Safety: File operations throw std::runtime_error when fialures occur, and calling code handles these exceptions gracefully with try-catch blocks.

  3. Algorithm Integration: std::sort with custom comparators handles multi-criteria sorting (ACM ranking and student sorting by major/score).

Tags: C++ Stream I/O Polymorphism Exception Handling Sorting

Posted on Tue, 04 Aug 2026 17:02:00 +0000 by shawngibson