C++ How to use less conditional statements?

坚强是说给别人听的谎言 提交于 2019-12-24 11:23:32

问题


For my assignment, I'm storing user login infos. I'm taking in a string which is the command. The command can be create, login, remove, etc. There are 10 total options, i.e 10 different strings possible. Can anyone explain a more efficient way to write this instead of 10 if and else if statements? Basically how should I format/structure things besides using a bunch of if (string == "one"), else if (string == "two"). Thank you


回答1:


I expect that your lecturer would like you to extract function to another re-usable function:

string action;
command = CreateAction(action);
command.Do(...);

Ofcourse, inside you CreateAction class you still need to have the conditionals that determine which commands need to be created.

AbstractCommand CreateAction(action)
{
    if (action == "login")
        return LoginCommand();
    else if (action == "remove")
        return RemoveCommand();
    ..... etc etc
}

And if you really want to get rid of all the conditionals than you can create some self-registering commands but that involves a lot more code and classes......

You should look up things like Command Pattern and Factory Pattern




回答2:


You can use function pointers and a lookup table.

typedef void (*Function_Pointer)(void);
void Create(void);
void Login(void);
void Remove(void);

struct Function_Option_Entry
{
  const char * option_text;
  Function_Pointer p_function;
};

Function_Option_Entry option_table[] =
{
  {"one", Create},
  {"two", Login},
  {"three", Remove},
};
const unsigned int option_table_size =
  sizeof(option_table) / sizeof(option_table[0]);

//...
std::string option_text;
//...
for (i = 0; i < option_table_size; ++i)
{
  if (option_text == option_table[i].option_text)
  {
    option_table[i].p_function();
    break;
  }
}



回答3:


Use a switch, and a simple hash-function.
You need to use a hash-function, because C and C++ only allow switching on integral values.

template<size_t N> constexpr char myhash(const char &x[N]) { return x[0] ^ (x[1]+63); }
char myhash(const string& x) { return x.size() ? x[0] ^ (x[1]+63) : 0; }

switch(myhash(s)) {
case myhash("one"):
    if(s != "one") goto nomatch;
    // do things
    break;
case myhash("two"):
    if(s != "two") goto nomatch;
    // do things
    break;
default:
nomatch:
    // No match
}

Slight adjustments are needed if you are not using std::string.




回答4:


I would recommend you to create a function for every specific string. For example, if you receive a string "create" you will call function doCreate(), if you receive a string "login" then you call function doLogin()

The only restriction on these function is that all of them must have the same signature. In an example above it was smh like this:

typedef void (*func_t) ();

The idea is to create a std::map from strings to these functions. So you wouldn't have to write 10 if's or so because you will be able to simple choose the right function from the map by the name of a specific string name. Let me explain it by the means of a small example:

typedef void (*func_t) ();
void doCreate()
{
     std::cout << "Create function called!\n";
}

void doLogin()
{
     std::cout << "Login function called!\n";
}

std::map<std::string, func_t> functionMap;

void initMap()
{
    functionMap["create"] = doCreate;
    functionMap["login"] = doLogin;
}

int main()
{
     initMap();
     std::string str = "login";
     functionMap[str](); // will call doLogin()

     str = "create";
     functionMap[str](); // will call doCreate()

     std::string userStr;
     // let's now assume that we also can receive a string not from our set of functions
     std::cin >> userStr;
     if (functionMap.count(userStr))
     {
          functionMap[str](); // now we call doCreate() or doLogin()
     }
     else
     {
          std::cout << "Unknown command\n";
     }

     return 0;
 }

I hope it will help you in someway=)




回答5:


You can use a map which does the comparison for you.

Something like this:

Initialise map:

std::map<std::string, std::function<void(std::string&)>> map;
map["login"]  = std::bind(&Class::DoLogin,  this, std::placeholders::_1);
map["create"] = std::bind(&Class::DoCreate, this, std::placeholders::_1);

Receive message:

map.at(rx.msg_type)(rx.msg_data);

Handler:

void Class::DoLogin(const std::string& data)
{
   // do login
}



回答6:


Maybe you can create a std::map<std::string, int> and use map lookups to get the code of the command that was passed - you can later switch on that number. Or create an enum Command and have a std::map<std::string, Command> and use the switch.

Example:

enum Command
{
     CREATE,
     LOGIN,
     ...
};

std::map<std::string, Command> commandNameToCode;
// fill the map with appropriate values
commandNameToCode["create"] = Command::CREATE;

// somehow get command name from user and store in the below variable (not shown)    
std::string input;

// check if the command is in the map and if so, act accordingly
if(commandNameToCode.find(input) != commandNameToCode.end())
{
    switch(commandNameToCode[input])
    {
    case CREATE:
         // handle create
         break;
    ...
    }
}


来源:https://stackoverflow.com/questions/27139247/c-how-to-use-less-conditional-statements

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!