C++: Handle User Input to Points

Recently, I dove into a challenge from Chapter 9: Input and Output Streams - Drill [12] in Bjarne Stroustrup’s Programming Principles and Practice Using C++. The exercise was to define a Point structure with x and y coordinates, prompting the user to input seven pairs and storing them in a vector<Point> called originalPoints.

Defining the Point Structure

First, I defined a data type Point:

point.cpp
#include <iostream>
#include <vector>
#include <string>

using namespace std;

struct Point {
    int x;
    int y;

    Point(int xx=0, int yy=0) : x(xx), y(yy) {}

    friend ostream& operator<<(ostream& os, const Point& p) {
        return os << "(" << p.x << ", " << p.y << ")";
    }

    bool operator<(const Point& other) const {
        if (x < other.x) {
            return true;
        } else if (x > other.x) {
            return false;
        } else {
            return y < other.y;
        }
    }
};

With this, Point objects can be effectively printed using the overloaded << operator.

Initial Attempt: Direct Reading

I started with the simplest approach to read input:

int main() {
    vector<Point> originalPoints;

    for (int i = 0; i < 7; ++i) {
        Point point;
        cout << "Enter x and y coordinates for point " << i + 1 << ": ";
        cin >> point.x >> point.y;
        originalPoints.push_back(point);
    }
}

However, this method had limitations:

  • It didn’t handle non-integer input well.
  • It accepted more than two values without error.

Improving Input Validation

To tackle these limitations, I implemented a function to ensure inputs were integers. Additionally, I used getline() to read an entire line and then parsed it:

bool isInteger(const string& str) {
    return str.find_first_not_of("0123456789") == string::npos;
}

int main() {
    vector<Point> originalPoints;

    for (int i = 0; i < 7; ++i) {
        Point point;
        cout << "Enter x and y coordinates for point " << i + 1 << ": ";

        string input;
        getline(cin, input);
        istringstream iss(input);
        string xStr, yStr;
        
        if (iss >> xStr >> yStr && isInteger(xStr) && isInteger(yStr)) {
            char temp;
            if ((iss >> temp) && (isalnum(temp))) {
                cerr << "Too many inputs." << endl;
                i--;
            } else {
                point.x = stoi(xStr);
                point.y = stoi(yStr);
                originalPoints.push_back(point);
            }
        } else {
            cerr << "Invalid input. Please enter two integers separated by a space." << endl;
            i--;
        }
    }
}

Modular Approach: Function-Based

I decided to improve modularity by using a function for input qualification, for testing input types and limiting the number of input to 2. It reduced complexity in the main logic flow:

bool assert_qualify(string cin_str) {
    stringstream ss1(cin_str); 
    int a, b;
    if (!(ss1 >> a >> b)) {
        cerr << "Invalid input format." << endl;
        return false; 
    } 

    char temp;
    if ((ss1 >> temp) && (isalnum(temp))) {
        cerr << "Too many inputs" << endl;
        return false; 
    }
    return true; 
}

vector<Point> get_points_vector(int size_n) {
    vector<Point> points_vector(size_n);
    int i = 0; 
    while (i < size_n) {
        string cin_str;
        cout << "Enter x and y coordinates for point " << i + 1 << ": ";
        cin >> cin_str;

        if (assert_qualify(cin_str)) {
            stringstream ss2(cin_str);
            ss2 >> points_vector[i].x >> points_vector[i].y;
            cout << "Point " << i + 1 << " is: " << points_vector[i] << endl;
            i++; 
        } else {
            cin.clear(); 
            cout << i + 1 << "th point is invalid. Please enter again: " << endl;
        }
    } 
    return points_vector;
}

int main() {
    vector<Point> points_vector = get_points_vector(7);
}

Conclusion

Through this exercise, It interests me in handling input validation and foreseeing potential pitfalls in user-input scenarios.

If you’re delving into C++ or similar challenges, I’d love to hear how you tackled similar problems or if you found other creative solutions!