0

I'm fairly new to C++ but am working on a program where I'm using unit testing to do the development. The specific unit test in question deals with saving and loading from a binary file, and seems to be throwing an access violation exception code during deallocation at the end of the unit test. If I comment out the line of code that loads from the file, the problem goes away however, which I've assumed means there's something in my load function creating the issue but I can't seem to figure out what's going on. When I step through the unit test, it seems to be saving and loading the file information correctly into the playbook objects.

Can anyone help point me in the right direction?

Unit Test Code:

Playbook testPlays;
        Playbook testPlaysLoad;

        string playName = "Play1";
        int numDLine = 4;
        int numLB = 3;
        vector<int> playerPos = { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 19 };
        vector<int> playerStance = { 2, 1, 0, 0, 1, 2, 2, 3, 2, 3, 3 };
        vector<int> playerBlitzGaps = { 0, 3, 0, 0, 3, 0, 0, 0, 0, 0, 0 };


        testPlays.setName("testPlays");
        testPlays.addPlay(playName, numDLine, numLB, playerPos, playerStance, playerBlitzGaps);
        testPlays.save();

        testPlaysLoad.load(testPlays.getName());

        
        Assert::IsTrue(testPlays == testPlaysLoad);

Load Function Code:

void Playbook::load(string fileName)

std::ifstream loadFile;

//create file path to load file from
string filePath = "./Playbooks/" + fileName + ".playbk";

//open the file
loadFile.open(filePath, std::ios::binary);

//end if file doesn't open correctly
if (loadFile.bad())
{
    return;
}

//read the individual pieces of the play
while (!loadFile.eof())
{
    //create a variable to store each play in one at a time before storing into the vector
    Play curPlay;
    
    loadFile.read((char*)&curPlay.playName, sizeof(curPlay.playName));
    loadFile.read((char*)&curPlay.numDLine, sizeof(curPlay.numDLine));
    loadFile.read((char*)&curPlay.numLB, sizeof(curPlay.numLB));
    
    //temp int variable
    int curInt = 0;
    
    //loop through each element and read it in
    for (int i = 0; i < 11; i++)
    {
        loadFile.read((char*)&curInt, sizeof(int));
        curPlay.playerPos.push_back(curInt);
    }

    //loop through each element and read it in
    for (int i = 0; i < 11; i++)
    {
        loadFile.read((char*)&curInt, sizeof(int));
        curPlay.playerStance.push_back(curInt);
    }
    
    //loop through each element and read it in
    for (int i = 0; i < 11; i++)
    {
        loadFile.read((char*)&curInt, sizeof(int));
        curPlay.playerBlitzGaps.push_back(curInt);
    }
    
    plays.push_back(curPlay);
} 

//close the file
loadFile.close();

//set name of playbook as the file name
setName(fileName);

//set play num to vector size
numPlays = plays.size();

Play definition:

Play(string name, int numLine, int LB, vector<int> pos, vector<int> stance, vector<int> blitzGaps) { playName = name; numDLine = numLine; numLB = LB; playerPos = pos; playerStance = stance; playerBlitzGaps = blitzGaps; }
Play() {
    playName = ""; numDLine = 0; numLB = 0; playerPos = { 0,0,0,0,0,0,0,0,0,0,0 }; playerStance = { 0,0,0,0,0,0,0,0,0,0,0 }; playerBlitzGaps = { 0,0,0,0,0,0,0,0,0,0,0 };
}
~Play() {}

string playName = "";
int numDLine = 0;
int numLB = 0;
vector<int> playerPos = { 0,0,0,0,0,0,0,0,0,0,0 };
vector<int> playerStance = { 0,0,0,0,0,0,0,0,0,0,0 };
vector<int> playerBlitzGaps = { 0,0,0,0,0,0,0,0,0,0,0 };

Playbook definition:

friend bool operator== (Playbook& book1, Playbook& book2);

public: Playbook(); ~Playbook();

void setName(string name);
void setCurrentPlay(int playNum);

string getName();
int getNumPlays();

void addPlay(string name, int numDLine, int numLB, vector<int> playerPos, vector<int> playerStance, vector<int> playerBlitzGaps);
void save();
void load(string fileName);

Play currentPlay;

private: string playbookName = ""; int numPlays = 0;

vector<Play> plays;

Oh, I almost forgot the stack trace:

Stack Trace: 
    _Container_base12::_Orphan_all_unlocked() line 1222
    _Container_base12::_Orphan_all_locked() line 1098
    _Container_base12::_Orphan_all() line 1240
    allocator<char> >::_Tidy_deallocate() line 4618
    char_traits<char>,std::allocator<char> >() line 3005
    Playbook::load() line 168
    PlaybookIO::saveAndLoadPlayblook() line 66
Andy K
  • 3
  • 2
  • Run your test under a debugger and figure out which line of code is accessing which bit of your object. – Botje Oct 19 '21 at 12:44
  • Visual Studio will tell you exactly where the access violation occurs. Switch the "Stack Frame" combo box on the debug toolbar to your code when you get the option to break into the debugger. – drescherjm Oct 19 '21 at 12:44
  • All memory errors can easily be found with AddressSanitizer. It is available for all 3 major compilers (MSVC, gcc, clang). It is a compiler+linker flag essentially, which adds instrumentation to your binary (unit test). If you trigger any memory error, it will show you stacktrace and failure reason. – warchantua Oct 19 '21 at 12:44
  • Looks like the problem is here: `Playbook::load() line 168` – drescherjm Oct 19 '21 at 12:49
  • In `loadFile.read((char*)&curPlay.playName, sizeof(curPlay.playName));` make sure that `playName` is not a `std::string` and is instead a POD type like a char array or you can not use this method of serialization. – drescherjm Oct 19 '21 at 12:50
  • 2
    `loadFile.read((char*)&curPlay.playName, sizeof(curPlay.playName));` -- If `playName` is a `std::string`, this can never work. It's strange why code like this seems to be used by new programmers so much, when all it does is cause all kinds of issues, yours being one of them. Why not a simple `loadFile >> curPlayName.playName;`? That solves that problem. – PaulMcKenzie Oct 19 '21 at 12:52
  • 1
    This may help: [https://isocpp.org/wiki/faq/serialization#serialize-overview](https://isocpp.org/wiki/faq/serialization#serialize-overview) – drescherjm Oct 19 '21 at 12:56
  • 1
    I think there should be a boycott on "binary file reading/writing" tasks given to beginners. There are literally thousands of questions in the C++ section alone where this same error keeps occurring. – PaulMcKenzie Oct 19 '21 at 12:57
  • 2
    The nasty thing with `string playName = "";` and `loadFile.read((char*)&curPlay.playName, sizeof(curPlay.playName));` is that it may _look_ like it's working if your string is short enough for [SSO](https://stackoverflow.com/questions/21694302/what-are-the-mechanics-of-short-string-optimization-in-libc). – Ted Lyngmo Oct 19 '21 at 12:57
  • Also this question: [https://stackoverflow.com/questions/7046244/serializing-a-class-which-contains-a-stdstring](https://stackoverflow.com/questions/7046244/serializing-a-class-which-contains-a-stdstring) – drescherjm Oct 19 '21 at 12:58
  • 2
    @OP -- If you read that line we're talking about carefully, you will conclude yourself it could never work. Assume that `playName` had a thousand characters in it. What is the value of `sizeof(playName)`? It isn't going to be a thousand. Try it yourself. Now given that, the call to `read` has as its last parameter the number of bytes to read. Trouble, since you will not get to read 1000 bytes using `sizeof`. All `sizeof` does is give you the size of the `std::string` class itself, which may be 32, 40, 60 bytes, whatever. Conclusion, the code could never work. – PaulMcKenzie Oct 19 '21 at 13:01
  • Thank you for all the replies, looks like I have a bit of homework to do to understand this better. – Andy K Oct 19 '21 at 13:24

0 Answers0