Stack Overflow with tail recursion function c++

0

What The Code Does:
The code below is supposed to format .cpp files for a printer, by limiting the number of chars in each line of code without losing any code. The code is supposed to go through the file char by char. It does so by opening an ifstream then using the get method to get each char till the EOF is found. As each char is grabbed it is pushed to a std::string (vector) as each char is pushed, a line char count is incremented. If a new line char is found before the line char count is larger than it's max size, it is reset and continues on the next line. If the line char count is 2 less than the max char count before a '\n' char is found a \, \n, ' ''s(x tab count) are appended to the end of the string being written to. Then the rest of the line is appended to the string. Though, if the last char added to the string is not a ' ' char then the string has all the chars between it's end and the closest to the left ' ' char removed and placed at the start of the new line, after the 5 ' ' chars on the new line. The last bit is where I think the error is.

Sample Line: (pretend is bigger than max char count)

LongLong123 longLineOfCode;\n;

Sample Output:

Bad:

LongLong123 longLineOfCo\\n
      de;\n

Good:

LongLong123\\n
      longLineOfCode;\n

The Error:

Unhandled exception at 0x55CC1949 (ucrtbased.dll) in SubmitFormater.exe: 0xC00000FD: Stack overflow (parameters: 0x00000000, 0x00F02000). occurred

Sample Command line:

110 5 "C:/Users/holycatcrusher/Documents/OC 2018 Fall/222/Labs/COSC_222_Data_Structures_Cpp/SubmitFormater.cpp"

The 110 is the number of chars that can be in a line, the 5 is the number of chars the tab is (5 blank spaces), and the long string is the address of the file to be formatted (make a copy of file as upon error the file entered will be destroyed). I am using the code that makes up this project as sample input (a copy of it).


The code:

/* SubmitFormater.cpp
    This file was created by Bradley Honeyman
    Oct 25, 2018

    This file is used to format submit files for COSC 222 asignments

*/

#include "pch.h"
#include <iostream>
#include <fstream>
#include <string>
#include <stdbool.h>
#include <stddef.h>

#define _CRTDBG_MAP_ALLOC  
#include <stdlib.h>  
#include <crtdbg.h>

std::string *processFile(std::ifstream *input, int maxCharCount, int tabSpaceSize, char delimiter, std::string *code, int lineCharCount);
int toInt(char *input);

int main(int argc, char **argv) {

    std::cout << "Running 222 formatter" << std::endl << "by: Bradley Honeyman" << std::endl;

    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

    //make sure there is the min number of commands
    if (argc < 3) {
        std::cout << "Ensure there is a specified max line length and at least one file to modify!" << std::endl;
        return 0;
    }

    for (int i = 3; i < argc; i++) {

        //open file and make sure it opens
        std::ifstream input;
        input.open(argv[i]);
        if (!input.is_open()) {
            std::cout << "Could not open the input file: " << argv[i] << std::endl;
            return EXIT_FAILURE;
        }

        //process code
        std::string *code = new std::string();
        processFile(&input, toInt(argv[1]), toInt(argv[2]), ' ', code, 0);

        //check for error 
        if (code == NULL) {
            return EXIT_FAILURE;
        }

        //output
        input.close();
        std::ofstream out(argv[i]);
        if (!out.is_open()) {
            std::cout << "could not write to output file" << std::endl;
            return EXIT_FAILURE;
        }

        //print to file
        std::cout << code->c_str() << std::endl;
        out << code->c_str();

        //close out delete code pointer
        out.close();
        delete code;

    }

    return 0;
}

/*
    formats a file by placing \\\n the custom tab whereever the length of a line is bigger than the max
    also a delimiter is used, so words aren't cut in half
*/
#define FORMAT_CHAR_COUNT 2
std::string *processFile(std::ifstream *input, int maxCharCount, int tabSpaceSize, char delimiter, std::string *code, int lineCharCount) {

    //std::cout << "Processing" << std::endl;

    //get char and check if is end of file
    char current = input->get();
    //std::cout << "\'" << current << "\'" << std::endl;
    if (input->eof()) {
        return code;
    }

    //incerment char count then check if are at furthest possible position
    lineCharCount++;
    //std::cout << lineCharCount << std::endl;
    if (current == '\n') {
        lineCharCount = 0;

        //check if are at max char count, split code and put to new line
    } else if (lineCharCount >= maxCharCount && current != '\\') {

        //if not at the end of the line go back to the closest delimiter to break
        std::string *pre = new std::string("");
        bool fail = false;
        if (current != '\n' && code->at(code->size() - 1)) {
            code->push_back(current);
            int i = code->size() - 1;
            int back = 0;
            for (i; i >= 0; i--) {
                pre->push_back(code->at(i));
                back++;
                if (back > maxCharCount - tabSpaceSize - FORMAT_CHAR_COUNT) {
                    std::cout << "Can not format file because there isn't a place close enough to break at a delimiter!" << std::endl;
                    fail = true;
                    break;
                }
            }

            //check for fail
            if (!fail) {

                //add delimiter if it needs to be
                if (pre->size() > 0 && pre->at(pre->size() - 1) != delimiter) {
                    pre->push_back(delimiter);
                }

                //reverse prepending string, and remove code that is to be prepended
                std::reverse(pre->begin(), pre->end());
                code->assign(code->substr(0, code->size() - back - 1));

            }
        }

        //insert \ then new line then tab then set char count to tab size + pre size + 1 for char to be added
        code->push_back('\\');
        code->push_back('\n');
        for (int i = 0; i < tabSpaceSize; i++) { code->push_back(' '); } //create tab
        code->append(pre->c_str());
        lineCharCount = tabSpaceSize + 1 + pre->size();
        pre->clear();
        delete pre;

    }

    //add char to code and run again
    code->push_back(current);

    return processFile(input, maxCharCount, tabSpaceSize, delimiter, code, lineCharCount);
}

/*
    converts string to an int
*/
int toInt(char *input) {
    int i = 0;
    int out = 0;
    while (input[i] != '\0') {
        out = (out * 10) + (input[i] - '0');
        i++;
    }
    return out;
}


Also, you can use the code above code as the sample .cpp file. Do remember to use a copy though, because the program will modify the file!

Call Stack Part 1 and 2(click images to be able to read them)

First part of call stack second part of call stack



Note:
The cause of the problem is covered in the conversation.

c++
string
visual-studio
ifstream
tail-recursion
asked on Stack Overflow Oct 29, 2018 by holycatcrusher • edited Oct 30, 2018 by holycatcrusher

1 Answer

0

After the amount of talking that went on this post I think It is worth posting what ended up being the solution for me. Also a few notes on what I concluded.

Code:

   /* SubmitFormater.cpp
    This file was created by Bradley Honeyman
    Oct 25, 2018

    This file is used to format submit files for COSC 222 asignments

*/

#include "pch.h"
#include <iostream>
#include <fstream>
#include <string>
#include <stdbool.h>
#include <stddef.h>

#define _CRTDBG_MAP_ALLOC  
#include <stdlib.h>  
#include <crtdbg.h>

std::string *processFile(std::ifstream *input, int maxCharCount, int tabSpaceSize, char delimiter, std::string *code, int lineCharCount);
int toInt(char *input);

int main(int argc, char **argv) {

    std::cout << "Running 222 formatter" << std::endl << "by: Bradley Honeyman" << std::endl;

    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

    //make sure there is the min number of commands
    if (argc < 3) {
        std::cout << "Ensure there is a specified max line length and at least one file to modify!" << std::endl;
        return 0;
    }

    for (int i = 3; i < argc; i++) {

        //open file and make sure it opens
        std::ifstream input;
        input.open(argv[i]);
        if (!input.is_open()) {
            std::cout << "Could not open the input file: " << argv[i] << std::endl;
            return EXIT_FAILURE;
        }

        //process code
        std::string *code = new std::string();
        processFile(&input, toInt(argv[1]), toInt(argv[2]), ' ', code, 0);

        //check for error 
        if (code == NULL) {
            return EXIT_FAILURE;
        }

        //output
        input.close();
        std::ofstream out(argv[i]);
        if (!out.is_open()) {
            std::cout << "could not write to output file" << std::endl;
            return EXIT_FAILURE;
        }

        //print to file
        out << code->c_str();

        //close out delete code pointer
        out.close();
        delete code;

    }

    return 0;
}

/*
    formats a file by placing \\\n the custom tab whereever the length of a line is bigger than the max
    also a delimiter is used, so words aren't cut in half
*/
#define FORMAT_CHAR_COUNT 2
std::string *processFile(std::ifstream *input, int maxCharCount, int tabSpaceSize, char delimiter, std::string *code, int lineCharCount) {

    //get char and check if is end of file
    char current = input->get();
    if (input->eof()) {
        return code;
    }

    //incerment char count then check if are at furthest possible position
    lineCharCount++;
    //std::cout << lineCharCount << std::endl;
    if (current == '\n') {
        lineCharCount = 0;

        //check if are at max char count, split code and put to new line
    } else if (lineCharCount >= maxCharCount && current != '\\') {

        //if not at delimiter push everything to the right of the nearest delimiter to the left to pre
        int back = 0;
        std::string pre("");
        if (current != delimiter) {
            for (int i = code->size() - 1; i >= 0; i--) {
                back++;
                if (code->at(i) == delimiter) {
                    pre.push_back(code->at(i));
                    break;

                } else {
                    pre.push_back(code->at(i));

                }

            }

            //remove what was added to pre from code
            std::reverse(pre.begin(), pre.end());
            code->assign(code->substr(0, code->size() - back));

        }

        //insert \ then new line then tab then set char count to tab size + pre size + 1 for char to be added
        code->push_back('\\');
        code->push_back('\n');
        for (int i = 0; i < tabSpaceSize; i++) { code->push_back(' '); } //create tab
        code->append(pre);
        lineCharCount = tabSpaceSize + 1 + pre.size();

    }

    //add char to code and run again
    code->push_back(current);
    return processFile(input, maxCharCount, tabSpaceSize, delimiter, code, lineCharCount);
}

/*
    converts string to an int
*/
int toInt(char *input) {
    int i = 0;
    int out = 0;
    while (input[i] != '\0') {
        out = (out * 10) + (input[i] - '0');
        i++;
    }
    return out;
}



Notes:

  • The code only runs when compiled as a release version in VS
  • There was a logical error in the while loop
  • Compiler Optimization seems to solve the problem
answered on Stack Overflow Oct 30, 2018 by holycatcrusher • edited Oct 30, 2018 by holycatcrusher

User contributions licensed under CC BY-SA 3.0