使用字符串作为条件时减少 else if 语句的方法

skr*_*_CZ 2 c++ string if-statement

我正在制作一个类似终端的程序(用于计算货币),并使用自定义命令作为输入,但我遇到了问题。每次执行新命令时,我都必须添加新else if语句。这不是问题,但对于类似终端的程序来说可能有很多命令。

这是我的代码:

#include <iostream>
#include <string>
#include <Windows.h>
#include <math.h>

float user_balance = 0.0f;
float eur_in_czk = 24.0f;   //ammount of czk in single euro
std::string currency = "czk";
bool czk_to_eur_enabled = true;
bool eur_to_czk_enabled = false;

//------------------START method definition---------------------------------------------------------
void czk_to_eur()
{
    if (czk_to_eur_enabled) //to prevent using twice in a row
    {
        user_balance /= eur_in_czk;
        user_balance = floorf(user_balance * 100) / 100;    //limit to two decimal numbers
        currency = "eur";
        czk_to_eur_enabled = false;
        eur_to_czk_enabled = true;
    }
    else
    {
        std::cout << "Your savings are already converted to  " << currency << "!" << std::endl;
    }
}

void eur_to_czk()
{
    if (eur_to_czk_enabled) //to prevent using twice in a row
    {
        user_balance *= eur_in_czk;
        user_balance = floorf(user_balance * 100) / 100;    //limit to two decimal numbers
        currency = "czk";
        eur_to_czk_enabled = false;
        czk_to_eur_enabled = true;
    }
    else
    {
        std::cout << "Your savings are already converted to " << currency << "!" << std::endl;
    }
}

void set_balance(float new_balance)
{
    user_balance = new_balance;
}

void add_balance(float new_balance)
{
    user_balance += new_balance;
}
//------------------END method definition-----------------------------------------------------------


int main()
{
    bool main_loop = true;  //main loop enabler
    float input_money;
    std::string user_command = "";

    std::cout << "This is currency converter v1.0 (czk to eur and back)\n\n\n" << std::endl;

    while (main_loop)   //main loop for currency converter
    {
        std::cout << "Input: ";
        std::cin >> user_command;
        std::cout << std::endl;

        if ((user_command == "setbal") || (user_command == "SETBAL"))
        {
            std::cout << "Your balance is " << user_balance << " " << currency << ".\n";
            std::cout << "Please enter desired value (" << currency << "): ";
            std::cin >> input_money;
            set_balance(input_money);
            std::cout << "\n" << std::endl;
        }
        else if ((user_command == "addbal") || (user_command == "ADDBAL"))
        {
            std::cout << "Your balance is " << user_balance << " " << currency << ".\n";
            std::cout << "Please enter desired value (" << currency << "): ";
            std::cin >> input_money;
            add_balance(input_money);
            std::cout << "\n" << std::endl;
        }
        else if ((user_command == "balance") || (user_command == "BALANCE"))
        {
            std::cout << "Your balance is " << user_balance << " " << currency << "." << std::endl;
        }
        else if ((user_command == "curstat") || (user_command == "CURSTAT"))
        {
            std::cout << "Currency status is " << eur_in_czk << " czk in 1 eur." << std::endl;
        }
        else if ((user_command == "toeur") || (user_command == "TOEUR"))
        {
            czk_to_eur();
        }
        else if ((user_command == "toczk") || (user_command == "TOCZK"))
        {
            eur_to_czk();
        }
        else if ((user_command == "cheuv") || (user_command == "CHEUV"))
        {
            std::cout << "Change eur value (" << eur_in_czk << "): ";
            std::cin >> eur_in_czk;
            std::cout << std::endl;
        }
        else if ((user_command == "help") || (user_command == "HELP"))
        {
            std::cout << "SETBAL        Sets balance.\n"
                      << "ADDBAL        Adds balance.\n"
                      << "BALANCE       Shows current balance.\n"
                      << "CURSTAT       Shows currency status.\n"
                      << "TOEUR         Converts czk to eur.\n"
                      << "TOCZK         Converts eur to czk.\n"
                      << "CHEUV         Changes eur currency value.\n"
                      << "CLS           Cleans terminal history.\n"
                      << "EXIT          Exits program.\n" << std::endl;
        }
        else if ((user_command == "cls") || (user_command == "CLS"))
        {
            system("CLS");  //funtion from Windows.h library
        }
        else if ((user_command == "exit") || (user_command == "EXIT"))
        {
            main_loop = false;
        }
        else
        {
            std::cout << "'" << user_command << "'"
                      << "is not recognized as an internal or external command!\n";
            std::cout << "Type 'HELP' to see available commands.\n" << std::endl;
        }
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

循环中代码的底部部分while就是问题所在。

一切正常,但我想知道是否还有其他方法。据switch我所知,不支持字符串值作为条件/依赖项。(而且我目前没有使用任何自定义类和/或自定义头文件,因为这只是实验。)

还有其他方法吗?

Cap*_*ous 5

通常,我建议使用std::map带有字符串作为键和函数作为值的 a ,以便您可以在映射中搜索命令,然后调用与其关联的函数。然而,由于评论中已经提到了这一点,我想我会变得很奇特并提供一个完全古怪的解决方案,你可能不应该使用

switch这个奇怪的解决方案允许您在/语句中使用字符串文字case。这是可以通过利用现代 C++ 的一项称为用户定义文字的功能来实现的,该功能允许您通过定义用户定义后缀来生成用户定义类型的对象,就像附加U到整数文字以指定无符号值一样。 。

我们要做的第一件事是定义一个用户定义的文字,它生成一个在编译时计算的哈希值。由于这会从字符串生成哈希值,因此可能会遇到冲突,但这取决于所使用的哈希算法的质量。对于我们的示例,我们将使用一些简单的东西。下面的代码片段定义了一个字符串文字,其后缀_C可生成哈希值。

constexpr uint32_t djb2Hash(const char* str, int index = 0)
{
    return !str[index]
        ? 0x1505
        : (djb2Hash(str, index + 1) * 0x21) ^ str[index];
}

// Create a literal type for short-hand case strings
constexpr uint32_t operator"" _C(const char str[], size_t /*size*/)
{
    return djb2Hash(str);
}
Run Code Online (Sandbox Code Playgroud)

现在,每当编译器看到其格式的字符串文字时"Hello World"_C,都会生成一个哈希值并使用它来代替字符串。

现在我们将把它应用到您现有的代码中。首先,我们将分离接受用户命令的代码cin,并使给定的命令全部小写。

std::string get_command()
{
    std::cout << "Input: ";

    std::string user_command;
    std::cin >> user_command;
    std::cout << std::endl;

    std::transform(
        user_command.begin(),
        user_command.end(),
        user_command.begin(),
        [](char ch) { return static_cast<char>(std::tolower(ch)); });

    return user_command;
}
Run Code Online (Sandbox Code Playgroud)

现在,我们可以从用户处获取一个全小写的命令,我们需要处理该命令,因此我们将采用原始的if/else语句集并将它们转换为简单的switch/case语句。现在,由于我们实际上无法在switch/case语句中使用字符串文字,因此我们必须稍微修改一下并为switch代码部分生成用户命令的哈希值。我们还将获取您的所有命令并向_C其添加后缀,以便编译器自动为我们生成哈希值。

int main()
{
    bool main_loop = true;  //main loop enabler

    std::cout << "This is currency converter v1.0 (czk to eur and back)\n\n\n" << std::endl;

    while (main_loop)   //main loop for currency converter
    {
        const auto user_command(get_command());
        switch(djb2Hash(user_command.c_str()))
        {
        case "setbal"_C:
            std::cout << "Set balance command\n";
            break;

        case "addbal"_C:
            std::cout << "Add balance command\n";
            break;

        case "balance"_C:
            std::cout << "Get balance command\n";
            break;

        case "curstat"_C:
            std::cout << "Get current status command\n";
            break;

        case "help"_C:
            std::cout << "Get help command\n";
            break;

        case "exit"_C:
            main_loop = false;
            break;

        default:
            std::cout
                << "'" << user_command << "'"
                << "is not recognized as an internal or external command!\n"
                << "Type 'HELP' to see available commands.\n" << std::endl;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你就得到了它。一个完全奇怪的解决方案!现在请记住,我们并没有真正在switch/case语句中使用字符串,我们只是隐藏了生成随后使用的哈希值的大部分细节。