Kuvaitt
Kuvaitt

Reputation: 37

How to use a string to find and execute a function with matching name?

I'm fairly new to C++ and just learning it on my own, so please go somewhat easy on me. What I am trying to do:

The code I wrote functions as I want it to, yet the errors and warnings in the build terminal indicate that something is not correct. I have tried using different data types but I have run out of ideas. By using a char * array for the program names I got it down to one warning: "ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]". I would love to get some clarity on what I am doing wrong that causes this warning.

This is the code:

   #include <stdio.h>
   #include <conio.h>
   #include <iostream>
   #include <string.h>
   #include <windows.h>
   using namespace std;



   void keyControl(), randomNumber();

   // What data type for array?
   string programList[99] = {"keyControl", "randomNumber"};
   char writeWord[18] = "";

   int main(){

       // Is there an equivalent of strcmp() for string datatype?
       while(strcmp(writeWord, "quit") != 0){

           system("@cls");

           cout << ("\n\tProgram list:\n\t");
           for(int x = 0; x < 99; x++){cout << (programList[x]) << (", ");}
           cout << ("\n\n\tType a program to run or 'quit' to exit:\n\n<");

           // Is there an equivalent of gets() for string datatype?
           gets(writeWord);

           system("@cls");

           if(strcmp(writeWord, programList[1]) == 0){
               //keyControl();
           }

           if(strcmp(writeWord, programList[2]) == 0){
               //randomNumber();
           }

       }

       return 0;
   }

And yes, I have read everywhere that gets() is a no-no but this is only a quickly thrown together test program and I would use something else if I was actually writing code seriously or for work, etc. I'm only doing this for fun and learning purposes currently, and want to focus on one thing at a time :-)

Thanks

Upvotes: 0

Views: 351

Answers (1)

David C. Rankin
David C. Rankin

Reputation: 84541

Fast forward a couple of decades in C++ and find the STL container std::map which maintains a unique set of key/value pairs. In your case you want to associate (map) and string with a function pointer so that when "keyControl" is entered by the user, the keyControl() function is called, and so on.

In your case you can populate a std::map with your key/values pairs by creating a map that associates a std::string and void(*)(void) pointer as a pair, e.g.

    /* std::map with string as key and function pointer as value */
    std::map<std::string, void(*)(void)> proglist {{"keyControl", keyControl},
                                                   {"randomNumber", randomNumber}};

Then it is simply a matter of looping continually, prompting the user for input and checking if the input matches one of the keys in the std::map, if it does, you executed the mapped function.

getline() is used for input into a std::string (read again the page on why gets() should NEVER be used). C++ provides an overload of == for comparing string equality. You can set various exit conditions based on the stream-state after your call to getline() or based on the content of the input filled. For example:

    std::string line {};    /* line for input */
    
    while (1) { /* loop continually */
        std::cout << "\nenter program name: ";  /* prompt for input */
        /* read line, break on manual EOF, empty input or "quit" */
        if (!getline (std::cin, line) || line.length() == 0 || line == "quit")
            break;

If the user generates a manual EOF (Ctrl + d Linux or Ctrl + z windows), if the string is empty (e.g. the user simply pressed Enter) or the string read is "quit" -- adjust to meet your needs.

All you need to do to check if the input matches a key in your std::map is to use the .find() member function that will return a pointer to the mapped pair if found or will return the end of the std::map if not found. The value part of the key/value pairt held in the map is accessed through the member name second (key is first, value is second) So you can do:

        /* read line, break on manual EOF, empty input or "quit" */
        if (!getline (std::cin, line) || line.length() == 0 || line == "quit")
            break;
        auto found = proglist.find (line);  /* search for input in map */
        if (found == proglist.end()) {      /* if not found, show error */
            std::cerr << "  error: no matching program name.\n";
            continue;
        }
        found->second();        /* otherwise execute the function */

Putting it altogether, you would have:

#include <iostream>
#include <string>
#include <map>

void keyControl()
{
    std::cout << "keyControl() called.\n";
}

void randomNumber()
{
    std::cout << "randumNumber() called.\n";
}

int main() {
    
    /* std::map with string as key and function pointer as value */
    std::map<std::string, void(*)(void)> proglist {{"keyControl", keyControl},
                                                   {"randomNumber", randomNumber}};
    std::string line {};    /* line for input */
    
    while (1) { /* loop continually */
        std::cout << "\nenter program name: ";  /* prompt for input */
        /* read line, break on manual EOF, empty input or "quit" */
        if (!getline (std::cin, line) || line.length() == 0 || line == "quit")
            break;
        auto found = proglist.find (line);  /* search for input in map */
        if (found == proglist.end()) {      /* if not found, show error */
            std::cerr << "  error: no matching program name.\n";
            continue;
        }
        found->second();        /* otherwise execute the function */
    }
}

Example Use/Output

$ ./bin/map_str_fn

enter program name: foo
  error: no matching program name.

enter program name: keyControl
keyControl() called.

enter program name: randomNumber
randumNumber() called.

enter program name: quit

Getting familiar with the STL containers will save you no end of grief using C++. The library has more than a decade and a half of validation and efficiency tweaks and will be far more robust than whatever you "whip-up" on the fly.

Look things over and let me know if you have questions.

Upvotes: 1

Related Questions