Lesson 2
Enhancing Voting Systems with Advanced C++ Features
Introduction

Welcome back to a fascinating session where we will learn about enhancing existing functionality without causing regressions. Our scenario today involves designing a voting system. We'll start with the basic implementation of the voting system and gradually introduce additional elements of complexity.

Initial Solution Development

Let's start by building our basic voting system using C++ standard library features. We will utilize std::map to maintain our data, which allows us to seamlessly store and retrieve information about candidates and voters. This map acts as the backbone of our system for the dynamic association of keys to values.

C++
1#include <iostream> 2#include <map> 3#include <vector> 4#include <algorithm> 5 6class VotingSystem { 7private: 8 std::map<std::string, int> candidates_; // Stores candidate_id as key and votes as value 9 std::map<std::string, std::pair<std::vector<std::string>, std::vector<int>>> voters_; // Tracks each voter's voting history 10 11public: 12 bool register_candidate(const std::string& candidate_id) { 13 // Add a new candidate to the system if not already present 14 if (candidates_.count(candidate_id)) { 15 return false; // Candidate is already registered 16 } 17 candidates_[candidate_id] = 0; // Initialize candidate with 0 votes 18 return true; 19 } 20 21 bool vote(int timestamp, const std::string& voter_id, const std::string& candidate_id) { 22 // Cast a vote if the candidate is registered 23 if (!candidates_.count(candidate_id)) { 24 return false; // Candidate not registered 25 } 26 voters_[voter_id].first.push_back(candidate_id); // Record the vote 27 voters_[voter_id].second.push_back(timestamp); // Record the time of the vote 28 candidates_[candidate_id]++; // Increment vote count for the candidate 29 return true; 30 } 31 32 int get_votes(const std::string& candidate_id) const { 33 // Retrieve vote count for a candidate or return -1 if not found 34 auto it = candidates_.find(candidate_id); 35 if (it != candidates_.end()) { 36 return it->second; 37 } 38 return -1; 39 } 40};
Introduce New Methods

Now that we have a basic voting system, our goal is to enhance this system with additional functionalities:

  • get_voting_history(const std::string& voter_id): Provides a detailed voting history for a specified voter, returning a map that shows how many times the voter has voted for each candidate.
  • block_voter_registration(int timestamp): Implements a mechanism to halt any new voter registrations past a specified timestamp, effectively freezing the voter list as of that moment.
  • change_vote(int timestamp, const std::string& voter_id, const std::string& old_candidate_id, const std::string& new_candidate_id): Enables voters to change their vote from one candidate to another, given the change is made within a 24-hour window from their last vote, ensuring both the old and new candidates are registered, and that the voter initially voted for the old candidate.
Implementing New Methods

We proceed to enhance our existing VotingSystem class to accommodate the new functionalities, utilizing C++ features efficiently.

First, let's incorporate the methods to get the voting history and to block further voter registrations:

C++
1#include <optional> 2 3class VotingSystem { 4 // Existing code... 5 int block_time_ = -1; 6 7public: 8 std::optional<std::map<std::string, int>> get_voting_history(const std::string& voter_id) const { 9 if (voters_.count(voter_id) == 0) { 10 return std::nullopt; // Voter ID does not exist 11 } 12 std::map<std::string, int> voting_history; 13 for (const auto& candidate_id : voters_.at(voter_id).first) { 14 voting_history[candidate_id]++; 15 } 16 return voting_history; 17 } 18 19 bool block_voter_registration(int timestamp) { 20 block_time_ = timestamp; // Set the block time 21 return true; 22 } 23};

The get_voting_history(const std::string& voter_id) method fetches a detailed voting history for a given voter. It returns an optional map, where the keys are candidate IDs and the values represent how many times the voter has voted for each candidate. If the voter ID does not exist, it returns std::nullopt.

The block_voter_registration(int timestamp) method is used to prevent any further voter registrations after a specified timestamp. It effectively freezes the voter list as of the given timestamp, reflecting a critical system state change that needs to be implemented in a versatile and non-intrusive manner.

Updating the `vote` Method

With the introduction of the block_voter_registration functionality, we must revisit our vote method to ensure it respects the new rules set by this feature. We need to ensure that no votes are cast after the voter registration has been blocked. Here’s how we modify the vote method to incorporate this change:

C++
1bool vote(int timestamp, const std::string& voter_id, const std::string& candidate_id) { 2 if (block_time_ != -1 && timestamp >= block_time_) { 3 return false; // Voting is blocked due to the registration freeze 4 } 5 if (!candidates_.count(candidate_id)) { 6 return false; // Candidate not registered 7 } 8 voters_[voter_id].first.push_back(candidate_id); // Record the vote 9 voters_[voter_id].second.push_back(timestamp); // Record the time of the vote 10 candidates_[candidate_id]++; // Increment vote count for the candidate 11 return true; 12}

This update ensures that our voting system behaves as expected, even with the new functionality to block further voter registrations beyond a certain timestamp, demonstrating how new features necessitate revisits and revisions to existing code.

Implementing `change_vote` Method

The change_vote method allows voters to change their vote, adhering to specific rules as detailed below:

  • Verify Candidate and Voter Validity: Check if both the old and new candidate IDs exist in the system, and verify that the voter has previously voted for the old candidate.

  • Timestamp Constraints: Ensure that the voter is trying to change their vote within an allowable timeframe after their initial vote.

  • Update Votes: If all conditions are met, subtract one vote from the old candidate, add one vote to the new candidate, and update the voter's voting record.

C++
1bool change_vote(int timestamp, const std::string& voter_id, const std::string& old_candidate_id, const std::string& new_candidate_id) { 2 if (!candidates_.count(old_candidate_id) || !candidates_.count(new_candidate_id)) { 3 return false; // Invalid candidate IDs 4 } 5 if (!voters_.count(voter_id) || std::find(voters_[voter_id].first.begin(), voters_[voter_id].first.end(), old_candidate_id) == voters_[voter_id].first.end()) { 6 return false; // Voter hasn't voted for the old candidate 7 } 8 9 auto& votes = voters_[voter_id].first; 10 auto& timestamps = voters_[voter_id].second; 11 auto it = std::find(votes.rbegin(), votes.rend(), old_candidate_id); 12 13 if (it == votes.rend() || (timestamp - timestamps[std::distance(votes.begin(), it.base()) - 1]) > 86400) { 14 return false; // Vote change not within the 24-hour window 15 } 16 17 size_t index = std::distance(votes.begin(), it.base() - 1); 18 votes[index] = new_candidate_id; // Change the vote 19 timestamps[index] = timestamp; // Update timestamp 20 21 candidates_[old_candidate_id]--; // Decrease old candidate's vote count 22 candidates_[new_candidate_id]++; // Increase new candidate's vote count 23 24 return true; 25}
Conclusion

Congratulations! You've successfully enhanced the voting system with additional functionalities: viewing voting history, blocking new voter registrations, and allowing vote changes under specific conditions, all while maintaining system integrity and backward compatibility. Continue exploring and refining your skills in C++ by expanding on these concepts to tackle more complex scenarios. Happy coding!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.