c++csvmemoryallocationbad-alloc

Trying to code Graph in c++, getting bad_alloc some of the time


I'm new to c++ after learning basic Object Oriented Programming in Java so I'm having a difficult time grasping memory deallocation. The assignment was to create a Weighted Directed Graph...

I'm getting the error: "terminate called after throwing an instance of 'std::bad_alloc' what(): std::bad_alloc" when I run certain inputs through my code, and I'm having a difficult time figuring out what is causing it.

I googled the error and found that it was a memory problem, so I attempted to go through my code and try to find any leaks, but I am not sure where they are. Most posts are talking about pointers, which I do not tend to implement because I am unfamiliar with them. Thank you for your time!

#include <iostream>
#include <fstream>
#include <string>
#include <array>
#include <iterator>
#include <map>
#include <list>
#include <vector>
#include <algorithm>

using namespace std;

class WDGraph {
    private:
        map<string,map<string,int>> edges;
        vector<string> verts;
        list<string> leaves;
        list<string> roots;
        list<string> selfEdges;
    public:
        list<string> getRoots() { return roots; }
        list<string> getLeaves() { return leaves; }

        void addVert(string key) {
            verts.push_back(key);
        }

        void link(string start, string dest, int cost) {
            edges[start].insert(make_pair(dest,cost));
            if (!containsLeaf(dest) && !containsVert(dest)) 
                leaves.push_back(dest);
            if (!containsRoot(start) && !containsVert(start)) 
                roots.push_back(start);
            if (start == dest)
                    selfEdges.push_back(start);
            roots.remove(dest);
            leaves.remove(start);
        }

        bool containsVert(string key) {
            for (int i=0; i < verts.size(); i++) {
                if (key == verts[i]) {
                    return true;
                }
            }
            return false;
        }

        bool containsRoot(string key) {
            bool found = (find(roots.begin(), roots.end(), key) != roots.end());
            return found;
        }

        bool containsLeaf(string key) {
            bool found = (find(leaves.begin(), leaves.end(), key) != leaves.end());
            return found;
        }

        WDGraph() { }
        void printWDG() { 
            cout << "Printing Weighted Directed Graph." << endl;
            for (auto itr1 = edges.begin(); itr1 != edges.end(); ++itr1) {
                for (auto itr2 = itr1->second.begin(); itr2 != itr1->second.end(); ++itr2) {
                    if (itr2->first == "null" && containsRoot(itr1->first)) {
                        cout << "[" << itr1->first << "]";
                    }
                    else if (itr2->first != "null")
                        cout << "[" << itr1->first << " -> ";
                        cout << itr2->first << ", " << itr2->second << "] ";
                } 
                cout << "" << endl;
            }            
        }

        void printNumVerts() { 
            cout << "Total number of vertices: " << verts.size() << endl;
        }

        void printRoots() { 
            int num_roots = 0;
            cout << "Vertices with zero inbound edges: " << endl;
            for (auto itr = roots.begin(); itr != roots.end(); ++itr) { 
                cout << "[" << *itr << "]" << endl;
                num_roots++;
            }
            if (num_roots == 0) cout << "None" << endl;
        }

        void printLeaves() { 
            int num_leaves = 0;
            cout << "Vertices with zero outbound edges:" << endl;
            for (auto itr = leaves.begin(); itr != leaves.end(); ++itr) {
                if (*itr != "null")
                    cout << "[" << *itr << "]" << endl;
                    num_leaves++;
            }
            if (num_leaves == 0) cout << "None" << endl;
        }

        void printSelfEdges() {
            cout << "Vertices with self edges:" << endl;
            for (auto itr = selfEdges.begin(); itr != selfEdges.end(); ++itr) {
                cout << "[" << *itr << "]" << endl;
            }
        }    
};

int main() {
    WDGraph myWDG;
    string filePath;
    string line;
    int weight;
    size_t commaPos;
    vector<string> sVector;
    ifstream dataFile;

    // cout << "Please enter the relative path to an input file." << endl;
    // getline (cin, filePath);
    // cout << "The file path you entered was " << filePath << endl;
    // dataFile.open(filePath);

    dataFile.open("input.csv"); //test input

    while (getline (dataFile, line)) {
        commaPos = line.find(',');

        //Parse input file into string vector
        while (line.length() >= 1) { 
            if (line.length() == 1) {
                sVector.push_back(line);
                break;
            }

            sVector.push_back(line.substr(0,commaPos));
            line = line.substr(commaPos+1);
            commaPos = line.find(',');   
        }

        //Create vertices depending on number of parameters
        if (sVector.size() == 1) {            
            if (!myWDG.containsVert(sVector[0])) {
                myWDG.addVert(sVector[0]);\
            } 
            myWDG.link(sVector[0], "null", 0);
        }

        if (sVector.size() == 3) {
            if (!myWDG.containsVert(sVector[0])) {
                myWDG.addVert(sVector[0]);
            } 
            if (!myWDG.containsVert(sVector[1])) {
                myWDG.addVert(sVector[1]);
            } 

            weight = stoi(sVector[2]);
            myWDG.link(sVector[0], sVector[1], weight);
        }
        sVector.clear();
    }

    myWDG.printWDG();
    myWDG.printNumVerts();
    myWDG.printRoots();
    myWDG.printLeaves();
    myWDG.printSelfEdges();
}

When my .csv has simple stuff it works as expected, for example:

a,b,1
c,d,2
e
f,f,3

However, if I have stuff like this I get the error "terminate called after throwing an instance of 'std::bad_alloc':

Hello
World,Hello,3
My,Name,4
Is
Nikki,Hello,3

Solution

  • As mentioned by Z E Nir, your line parsing code fails to consume any input if there is no comma "," in the line. You can of course debug your line parsing code, as debugging is a valuable skill to develop anyway.

    However, a possible alternative to debugging consists in finding an existing C++ language construct that does what you want to do, and is part of the language library so it is already debugged.

    Quite often, what you want to do is "common stuff", so debugging manual code will take more time than finding the appropriate pre-existing language construct, courtesy of your favorite internet search engine and/or stackoverflow itself. And being able to quickly find the language construct is also a very valuable skill.

    In your case, function getline() takes an optional delimiter, which is a newline by default, but you can instead have "," as delimiter and so use getline() again, but to parse a single line. It just takes a string object pretending to be a file stream, that is an std::istringstream object.

    So you end up with two nested loops, both using getline():

        #include  <sstream>
    
        while (getline (dataFile, line)) {
    
            std::istringstream  iss{line};
            std::string         token;
    
            while (getline (iss, token, ',')) {
                std::cout << "DEBUG  TOKEN  LEN=" << token.length() << std::endl;
                sVector.push_back(token);
            }
    
            // go build myWDG
        }
    

    That way, you don't have to mess up with lowly details such as the value of your commaPos variable. And the resulting code is easier to understand for another programmer.