Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid long switch statements? C++

I am working on a "dictionary" for my class. I have an int array called NumOfWordsInFile[] where NumOfWordsInFile[0] corresponds to how many words are in A.txt and NumOfWordsInFile[25] corresponds to Z.txt

As it is now I have a huge switch for the 26 different conditions of letters. I have a function called AddWord(string word). AddWord gets the first letter of the word passed to it and inserts it into the appropriate .txt file. Now here is the problem. Everytime a word is added to A.txt I must increment NumOfWordsInFile[0] by 1. The only way I can think of to do this is with these huge switches. I also have a deleteWord function which conversely decrements NumOfWordsInFile[] if the word is deleted. Now I dont want to have two 26 case swithes but the problem is I dont know how else to do it. Now I could just do the same thing for the delete function but I really dont want to have hundreds of more lines of code to go through. Is there a better way to do this?

Sample of the switch in the AddWord function:

case 'w':
    if (numOfWordsInFile[22] < maxWordsPerFile) {
        fout.open(fileName.data(), ios::app);
        fout << word << " " << endl;
        numOfWordsInFile[22]++;
        if (totalWordsInDict < maxWordsInDict) {
            totalWordsInDict++;
        }
        return(Dictionary::success);
    } else {
        return(Dictionary::failure);
    }

case 'x':
    if (numOfWordsInFile[23] < maxWordsPerFile) {
        fout.open(fileName.data(),ios::app);
        fout << word << " " << endl;
        numOfWordsInFile[23]++;
        if (totalWordsInDict < maxWordsInDict) {
            totalWordsInDict++;
        }
        return(Dictionary::success);
    } else {
        return(Dictionary::failure);
    }

Delete function.

bool Dictionary::DeleteAWord(string word)
{
    ofstream fout;
    ifstream fin;
    string x;
    string fileName="#.txt";
    int count=0;
    vector <string> words;
    bool deleted=false;

    fileName[0]=toupper(word[0]);
    fin.open(fileName.data()); //makes the file depending on the first letter of the argument "word"

    while (fin >> x)
    {
        words.push_back(x);
        count++;//number of elements in vector
    }
    if (SearchForWord(x))
    {
        for ( ;count > 0; count--)
        {
            if (words[count-1] == word)
            {
                // cout << "Found word " << word << " during search, now deleting" << endl;
                words.erase(words.begin()+(count-1));
                deleted = true;

                /*
                    This clearly doesn't work and is what I need help with, I know why it
                    doesn't work but I don't know how to make it better than having another
                    huge switch.
                */
                numOfWordsInFile[toupper(word[0])]--;
                /*

                */

                totalWordsInDict--;
                fin.close();
            }
        }

        if (deleted)
        {
            fout.open(fileName.data());
            for (int i = 0; i < words.size(); i++)
                fout << words[i] << endl;
            return(Dictionary::success);
        }
        return(Dictionary::failure);
    }
    return(Dictionary::failure);
}
like image 886
Tyler Pfaff Avatar asked Apr 03 '11 23:04

Tyler Pfaff


People also ask

What can I use instead of a switch statement in C?

You could use a series of if else statements or you could lookup a set of values in a table. You could use an array of function pointers indexed by the character.

Why do people hate switch statements?

Last but not least, because a switch statement requires us to modify a lot of classes, it violates the Open-Closed Principle from the SOLID principles. To conclude, switch statement are bad because they are error-prone and they are not maintainable.

How do you break out of a switch statement?

You can use the break statement to end processing of a particular labeled statement within the switch statement. It branches to the end of the switch statement. Without break , the program continues to the next labeled statement, executing the statements until a break or the end of the statement is reached.

Is it necessary to use break in switch in C?

5) The break statement is optional. If omitted, execution will continue on into the next case. The flow of control will fall through to subsequent cases until a break is reached. 6) Nesting of switch statements is allowed, which means you can have switch statements inside another switch.


2 Answers

Just taking a very quick look, it seems like you're using the position of the letter in the alphabet to do stuff.

You could replace all your switch statements with one statement that looks like:

int letter = (int)(ActualLetter - 'a');

if(numOfWordsInFile[letter]<maxWordsPerFile){
 fout.open(fileName.data(),ios::app);
 fout<<word<<" "<<endl;
 numOfWordsInFile[letter]++;
 if(totalWordsInDict<maxWordsInDict){
   totalWordsInDict++;
 }
 return(Dictionary::success);
}else{
 return(Dictionary::failure);
}

ActualLetter is something like, 'a', for example.

On a related note, in the future if you actually have large switch statements, consider encapsulating the code in functions:

switch (letter)
{
    case 'a':
      LetterA();
      break;

    case 'b':
      LetterB();
      break;

    ...
}

Or even better, you can use polymorphism to have C++ dispatch to the method you want based on the specific derived class:

class BaseLetter
{
   ...
public:
   virtual void DoStuff() = 0;
};

class LetterA : public BaseLetter
{
public:
   void DoStuff();
};

class LetterB : public BaseLetter
{
public:
    void DoStuff();
};

void Foo(BaseLetter *letter)
{
    // Use dynamic dispatch to figure out what to do
    letter->DoStuff();
}

Just note, dynamic dispatch does have a (slight) performance hit, and the above is a very bad place to actually use it. The solution I, RedX, and others have posted is much better suited to your specific example.

like image 99
Mike Bailey Avatar answered Sep 19 '22 17:09

Mike Bailey


struct FileInfo {
  int NumWords;
  std::string Filename;
};

std::map<char, FileInfo> TheFiles; 

FileInfo & FI = TheFiles[letter];
// Work with FI.NumWords and FI.Filename

Alternatively:

std::vector<FileInfo> TheFiles;
FileInfo & FI = TheFiles[std::tolower(Letter) - 'a'];
like image 25
Erik Avatar answered Sep 22 '22 17:09

Erik