CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 3 of 3
  1. #1
    Join Date
    Jan 2023
    Posts
    1

    debugging help for string manipulation program

    I'm a c++ beginner that wrote a lexicological word evolution program in c++, but I am having issues figuring out this bug. I created a custom word class that holds the STL string value for the word and a STL vector that holds the vowels in the word. after modifying the string value in the word class as part of the word "evolving", I rebuild the vowel vector to make sure the vector only contains strings that are within the string value of the Word. However this rebuild functions seems to be problematic because sometimes it will work fine and other times I will get a npos out of range error from the word modifying/evolution functions showing that the word vector of values has different strings that are not contained in the string value of the word. I have looked over the and nothing really stands out as being an issue. I have also ran the code under a debugger and still have no idea why this behavior is happening.

    i'm using g++ on a debian 11 machine

    here is the two main files where the problem most likely is

    Word.cpp:

    Code:
    #include "Word.hpp"
    
    Word::Word(std::string value, std::string meaning, std::vector<std::string> InVowels)
    {
        this->value = value;
        this->meaning = meaning;
        this->vowels = rebuildVowelList(InVowels);
    }
    
    
    bool Word::Equal(Word otherWord)
    {
        if(otherWord.getValue() == this->value && otherWord.getMeaning() == this->meaning && otherWord.getVowels() == this->vowels)
        {
            return true;
        }
        
        return false;
    }
    
    bool Word::isDummyWord()
    {
        std::string IgnoreList[4] = {"KILL", "DEFAULT", "", " "};
        for(int i = 0; i < 4; i++)
        {
            if(value == IgnoreList[i] || meaning == IgnoreList[i] || vowels[0] == IgnoreList[i])
            {
                return true;
            }
        }
    
        return false;
    }
    
    Word Word::LengthenVowel(std::vector<std::string> vowelPool)
    {
        std::vector<std::string> LongVowelPool;
        for( std::string vowel : vowels)
        {
            for(std::string poolVowel : vowelPool)
            {
                if(poolVowel.size() >= vowel.size())
                {
                    LongVowelPool.push_back(poolVowel);
                } else if( poolVowel.size() <= vowel.size() && vowel.size() == 1)
                {
                    //figure out how to handle if all the pool and the vowels are one char
                }
            }
        }
    
        //find the index of the vowel in the word and replace it with a random one from the vowel pool.
        std::uniform_int_distribution<uint_least32_t> distVowel = WRandGen::distribute( 0,  vowels.size()-1);
        std::vector<std::string>::iterator it = std::find(vowels.begin(), vowels.end(), vowels[distVowel(LangSeed::rng)]);
        int indexOfVowel = it - vowels.begin();
    
        std::uniform_int_distribution<uint_least32_t> distVowelPool = WRandGen::distribute( 0,  LongVowelPool.size() -1);
    
        std::string tempVal = value.erase(indexOfVowel, 1).insert(indexOfVowel, LongVowelPool[distVowelPool(LangSeed::rng)]);
        return Word(tempVal, meaning, vowels);
    }
    
    Word Word::ShortenVowel(std::vector<std::string> vowelPool)
    {
        //check if the word is only one char
        if(value.size() < 2)
        {
            return Word(value, meaning, vowels); 
        } else {
           std::vector<std::string> ShortVowelPool;
            for( std::string vowel : vowels)
            {
                for(std::string poolVowel : vowelPool)
                {
                    //if the vowel is only 1 char just replace the char
                    if(poolVowel.size() <= vowel.size())
                    {
                        ShortVowelPool.push_back(poolVowel);
                    } else if( poolVowel.size() >= vowel.size() && vowel.size() == 1)
                    {
                    //figure out how to handle if all the pool and the vowels are 2 char
                    }
                }
            }
    
            //find the index of the vowel in the word and replace it with a random one from the vowel pool.
            std::uniform_int_distribution<uint_least32_t> distVowel = WRandGen::distribute( 0,  vowels.size() - 1);
            std::string selectedVowel = vowels[distVowel(LangSeed::rng)];
            size_t indexOfVowel = value.find(selectedVowel);
    
            std::uniform_int_distribution<uint_least32_t> distVowelPool = WRandGen::distribute( 0,  ShortVowelPool.size() -1);
            
            std::string tempVal = value.replace(indexOfVowel, selectedVowel.size(), ShortVowelPool[distVowelPool(LangSeed::rng)]);
            
            return Word(tempVal, meaning, vowels); 
        }
        
    }
    
    Word Word::DeleteVowel()
    {
        std::uniform_int_distribution<uint_least32_t> distVowel = WRandGen::distribute( 0,  (vowels.size()-1));
        std::string selectedVowel = vowels[distVowel(LangSeed::rng)];
        size_t indexOfVowel = value.find(selectedVowel);
    
        std::string tempVal = value.erase(indexOfVowel, selectedVowel.size());
        return Word(tempVal, meaning, vowels);
    }
    
    Word Word::AddSuffix(std::vector<std::string> suffixPool)
    {
        std::uniform_int_distribution<uint_least32_t> distSuffix = WRandGen::distribute( 0,  (suffixPool.size() -1));
    
        std::string selectedSuffix = suffixPool[distSuffix(LangSeed::rng)];
        std::string tempVal = value + selectedSuffix;
    
        return Word(tempVal, meaning, vowels);
    
    }
    
    Word Word::AddPreffix(std::vector<std::string> preffixPool)
    {
        std::uniform_int_distribution<uint_least32_t> distPreffix = WRandGen::distribute( 0,  preffixPool.size());
    
        std::string selectedPreffix = preffixPool[distPreffix(LangSeed::rng)];
        std::string tempVal = selectedPreffix + value;
        
        return Word(tempVal, meaning, vowels);
    
    }
    
    void Word::Kill()
    {
        //call deconstructor maybe, this isn't really c++ kosher, but I will  call this already in the simHandler to just
        //delete this word from the dictionary
        //~Word();
        
    }
    
    Word Word::Shrink(int start, int end)
    {   
        if(start - end == 0)
        {
            Word word = *this;
            return word;
        }
        std::string tempVal = value.substr(start, start - end);
    
        return Word(tempVal, meaning, vowels);
    }
    
    //idk how to set up MIX yet, I don't know what I want the mixed word to look like yet
    Word Word::Mix(int start, int end, int otherWordStart, int otherWordEnd, Word otherWord)
    {
        if(start - end == 0)
        {
            Word word = *this;
            return word;
        }
        std::string tempVal = value.replace(start, end - start, otherWord.getValue().substr(otherWordStart, otherWordEnd - otherWordStart));
        return Word(tempVal, meaning, vowels);
    
    }
    
    Word Word::Negate(std::vector<std::string> negatePool)
    {
        //I want this to be a bit different, but currently it will be very similar to the prefix
        //adding a negating prefix and changing the meaning to the opposite of the meaning
        //after a bit of research I will come back and fix this
    
        std::uniform_int_distribution<uint_least32_t> distNegate = WRandGen::distribute( 0,  negatePool.size());
    
        std::string selectedNegation = negatePool[distNegate(LangSeed::rng)];
        std::string tempVal = selectedNegation + value;
    
        std::string tempMeaning = "the opposite of " + meaning;
        
        return Word(tempVal, tempMeaning, vowels);
    
    }
    
    Word Word::Subsitute(int start, int end, Word otherWord, bool replace)
    {   
        std::string tempVal;
        if(replace)
        {
            tempVal = value.replace(start, end - start, otherWord.getValue());
        }else{
          tempVal = value.replace(start, end - start, otherWord.getValue().substr(start, end - start));  
        }
        return Word(tempVal, meaning, vowels);
    }
    
    Word Word::CreateNew(int size, bool structuredCreation)
    {
        std::string tempVal = "";
        std::string tempMeaning;
        std::vector<std::string> vowels;
    
        if(!structuredCreation)
        {
            std::uniform_int_distribution<uint_least32_t> dist = WRandGen::distribute( 0,  DEFAULT_CHAR_SET_LENGTH - 1);
            for(int i = 0; i < size; i++)
            {
                tempVal += DEFAULT_CHAR_SET[dist(LangSeed::rng)];
            }
    
            //create a random bit mask of the word to create where the vowels are
            std::vector<int> bitMask;
    
            std::uniform_int_distribution<uint_least32_t> maskDist = WRandGen::distribute( 0,  PERCENT_CHANCE_VOWEL - 1);
            for(auto character : tempVal)
            {
                bitMask.push_back((maskDist(LangSeed::rng) < 1) ? 1 : 0);
            }
    
            for(int i = 0; i < bitMask.size(); i++)
            {
                if(bitMask[i])
                {
                    std::string tempStr;
                    tempStr.push_back(tempVal[i]);
                    vowels.push_back(tempStr);
                }
            }
    
            //just take the same meaning from this word
            //I will probably change this with an API call that creates a 100% different meaning
            tempMeaning = meaning;
    
        } else {
    
        }
    
        return Word(tempVal, tempMeaning, vowels);
    }
    
    Word Word::ClipEnd(int start)
    {
       
        std::string tempVal = value.erase(start);
    
        return Word(tempVal, meaning, vowels);
    }
    
    Word Word::ClipFront(int end)
    {
        std::string tempVal = value.erase(0, end);
    
        return Word(tempVal, meaning, vowels);
    }
    
    Word Word::Compound(Word otherWord)
    {
        std::uniform_int_distribution<uint_least32_t> dist = WRandGen::distribute( 0,  1);
        std::string tempVal = (dist(LangSeed::rng) > 1) ? otherWord.getValue() + value : value + otherWord.getValue();
    
        return Word(tempVal, meaning, vowels);
    }
    
    
    /* this is the code that is suspect, but I can't find anything wrong */
    
    std::vector<std::string> Word::rebuildVowelList(std::vector<std::string> inVowelList)
    {
        std::vector<std::string> rebuiltVowelList = inVowelList;
    
        auto i = std::begin(rebuiltVowelList);
        while( i != std::end(rebuiltVowelList))
        {
            //convert iterator into index to grab correct value
            std::size_t found=value.find(rebuiltVowelList[std::distance(rebuiltVowelList.begin(), i)]);
            if(found == std::string::npos)
            {
                i = rebuiltVowelList.erase(i);
            } else 
            {
                ++i;
            }
        }
    
        if(rebuiltVowelList.size() < 1)
        {
            //replace with a max of 4 vowels repeats will be removed anyway
            //(int)std::ceil((float)((value.size() -1) * NEW_VOWELS_PRECENT_OF_WORD))
            std::uniform_int_distribution<uint_least32_t> charDist = WRandGen::distribute( 1, STATIC_AMOUNT_OF_VOWELS);
            int vowelAmount = charDist(LangSeed::rng);
            
            for(int charIndex = 0; charIndex < vowelAmount; charIndex++)
            {
                if(value.size() > 2)
                {
                    std::uniform_int_distribution<uint_least32_t> charPick = WRandGen::distribute(1, value.size() - 2 );
                    int index = charPick(LangSeed::rng);
                    std::string strVowel = this->value.substr(index, 1);
                    rebuiltVowelList.push_back(strVowel);
                }else {
                    std::string strVowel = value.substr(0, 1);
                    rebuiltVowelList.push_back(strVowel);
                }
              
       
            }
        }
        return rebuiltVowelList;
    }
    speaker.cpp

    Code:
    #include "Speaker.hpp"
    
    
    Speaker::Speaker(int x, int y, std::vector<Word> dictionary)
    {
        //makes sure the Speaker is within the bounds of the map
        if(x > MAX_X)
        {
            //set x to NULL and throw assert
            this->x = NULL;
        }
    
        if(x > MAX_Y)
        {
            //set y to NULL and throw assert
            this->y = NULL;
        }
        this->x = x;
        this->y = y;
    
        this->dictionary = dictionary;
    
        //WRandGen::setUpgenerator();
    
    }
    
    bool Speaker::compare(Speaker &otherSpeaker)
    {
        int similarityCount = 0;
        if(otherSpeaker.getX() == x && otherSpeaker.getY() == y)
        {
            for(Word word : dictionary)
            {
                for(Word otherWord : otherSpeaker.getDict())
                {
                    if(word.getValue() == otherWord.getValue())
                    {
                        similarityCount++;
                    }
                }
            }
            
            return (similarityCount == dictionary.size())? true : false;
    
        } else{
            return false;
        }
    }
    
    std::vector<Word> Speaker::speakToOtherPerson(Speaker & otherPerson)
    {
        std::vector<Word> sharedDictionary;
        
        //make sure other person is not self, the speakers can't exchange dictionaries and words with themselves
        //std::abs(otherPerson.getX() - x) > 4 || std::abs(otherPerson.getY() - y) > 4  remove distance parameter for now
        if(this->compare(otherPerson))
        {
            return sharedDictionary; 
        }
    
        //set the percent shared with the other speaker
       
        std::uniform_int_distribution<uint_least32_t> distributePercentShare( 0,  99);
    
        int amntWordShared = dictionary.size() * (float)((distributePercentShare(LangSeed::rng) + 1) / 100);
    
        //shuffle the dictionary to share different words each time
        std::shuffle(std::begin(dictionary), std::end(dictionary), std::default_random_engine());
    
        //remove blank strings words from dictionaries
        for(int i = 0; i < dictionary.size(); i++)
        {
            if(dictionary[i].getValue() == "")
            {
                dictionary.erase(dictionary.begin() + i);
            }
        }
    
        for(int i = 0; i < amntWordShared; i++)
        {
            std::uniform_int_distribution<uint_least32_t> distPercentMutate( 0,  99);
    
            if(distPercentMutate(LangSeed::rng) + 1 <= PERCENT_SHARED_MUTATION)
            {
                // seed for the random different mutation methods ( 13 are currently defined )
                std::uniform_int_distribution<uint_least32_t> distWhichMutation( 0,  13); 
    
                //for debugging purposes I will be replacing this with a magic number
                //distWhichMutation(LangSeed::rng)
                switch(1)
                {
                    case 0:
                        {
                        //lengthen the vowels
                        std::vector<std::string> vowelList = {};
                        //grab the vowel pool from the dictionary
                        for(Word word : dictionary)
                        {
                            
                            //vowelList.insert(vowelList.end(), word.getVowels().begin(), word.getVowels().begin() + word.getVowels().size());
                            //insert was broken so I reverted to the naive way and just used a simple loop
                            for(std::string vowel : word.getVowels())
                            {
                                vowelList.push_back(vowel);
                            }
                        }
    
                        Word sharedWord = dictionary[i].LengthenVowel(vowelList);
                        sharedDictionary.push_back(sharedWord);
    
                        }
                        break;
                
                    case 1:
                        {
                        //I have forced the code to only run here for debugging purposes
                        
                        //shorten the vowels 
                        std::vector<std::string> vowelList = {};
                        //grab the vowel pool from the dictionary
                        for(Word word : dictionary)
                        {
                            //vowelList.insert(vowelList.end(), word.getVowels().begin(), word.getVowels().begin() + word.getVowels().size());
                            //insert was broken so I reverted to the naive way and just used a simple loop
                            for(std::string vowel : word.getVowels())
                            {
                                vowelList.push_back(vowel);
                            }
                        }
    
                        Word sharedWord = dictionary[i].ShortenVowel(vowelList);
                        sharedDictionary.push_back(sharedWord);
                        }
                        break;
    
                    default:
                        {
                        
                         //create pesudo null object to check in the learning function
                         std::vector<std::string> dummyList = {"DEFAULT"};
                         Word sharedWord = Word("DEFAULT", "DEFAULT", dummyList);
    
                         sharedDictionary.push_back(sharedWord);
                        }
                        break;
                }
    
            } else {
                sharedDictionary.push_back(dictionary[i]);
            }
        }
    
        return sharedDictionary; 
    
    }
    
    
    void Speaker::learnWords(std::vector<Word> sharedWords)
    {
        //remove complete repeat words 
        for(int i = 0; i < dictionary.size(); i++)
        {
            for(int b = 0; b < sharedWords.size(); b++)
            {
                if(dictionary[i].Equal(sharedWords[b]) || !sharedWords[b].isDummyWord())
                {
                    //b--;
                    sharedWords.erase(sharedWords.begin() + b);
                    continue;
                }
    
            }
    
        }
        
        if(sharedWords.size() > 0)
        {
            //put filtered words into the dictionary
            dictionary.insert( dictionary.end(), sharedWords.begin(), sharedWords.end() ); 
            
        }
    
    }
    The rest of the code can be found on github: https://github.com/crabster15/EvolveLanguageSim2

  2. #2
    2kaud's Avatar
    2kaud is offline Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    7,822

    Re: debugging help for string manipulation program

    I have also ran the code under a debugger and still have no idea why this behavior is happening.
    Using the debugger on debug build code is definitely your friend here. You should be able to trace the error to a particular statement and obtain the variable values prior to the error. This should then inform as to what is causing the error and then you can trace through watching the values of suspect variables etc until you locate the source of the problem. And if I had to find the problem with this code that's how I would approach it. However at the moment I haven't got that time.

    As I only use MS VS I can't hep with debugging with g++.

    PS. Does the same input always give the same output/error? I notice that you use random numbers in part. For debug purposes I'd temporarily replace random numbrrs with specified numbers so that for every time with the same input you get the same result.

    If the same input sometimes fails and sometimes not then that could point to memory issues (access out of range etc).
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++23 Compiler: Microsoft VS2022 (17.6.5)

  3. #3
    2kaud's Avatar
    2kaud is offline Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    7,822

    Re: debugging help for string manipulation program

    I also note that this code isn't as efficient as it could be. You're passing std::vector by value which means they are copied. If you pass by const ref (const std::vector<std::string>&) then no copy is undertaken.

    Also pass std::string as const std::string& to again avoid an unnecessary copy.
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++23 Compiler: Microsoft VS2022 (17.6.5)

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured