3

I'm new to c++ and I'm writing a program that executes certain commands. My program is supposed to have about 200 commands and using strcmp to check if the string is one among the command seems to be slow and inaccurate to me. I'm wondering if there's a function that could call the given input directly as a command.

For example:

void main() {    
    char test[60], param[10];
    std::cin >> test >> param;
    callFunction(test, param); 
}

NOTE : I've done some search already and found a way using maps, but what if the number of arguments for each function differs? Any help would be appreciated, thanks!

1
  • 1
    You can bind a function to each word in a map with a function pointer and call the function accordingy, this does require your arguments to be the same though. Just pass the given args to your function and extract them there. Commented Jun 27, 2015 at 7:46

5 Answers 5

1

It would be a good coding practice to create a class for each command and inherit those classes from a common base class with a virtual function taking a vector of arguments. In your case the arguments are strings, so the command processing methods can take a vector of strings as arguments and return e.g. program exit code. Then comes a map, more specifically a hash table which is unordered_map in C++ because ordered iteration doesn't seem needed here. In that unordered_map the keys are lowercase command names and values are pointers to the instance of the class processing that command. The source code example is below:

#include <unordered_map>
#include <string>
#include <cstdint>
#include <vector>
#include <iostream>
#include <memory>

enum class ExitCode : int32_t
{
    OK = 0,
    WRONG_USAGE = 1,
    // Change the values below to your specific error (exit) codes
    SOME_ERROR = 2,
    OTHER_ERROR = 3
};

class CommandProcessor
{
public:
    virtual ExitCode Go(const std::vector<std::string>& parameters) = 0;
};

class FooCommandProcessor : public CommandProcessor
{
public:
    virtual ExitCode Go(const std::vector<std::string>& parameters) override
    {
        // Implement processing of Foo command here
        return ExitCode::OK;
    }
};

class BarCommandProcessor : public CommandProcessor
{
    virtual ExitCode Go(const std::vector<std::string>& parameters) override
    {
        // Implement processing of Bar command here
        return ExitCode::OK;
    }
};

// Implement classes processing the other commands here

class CommandSelector
{
    typedef std::unordered_map<std::string, std::shared_ptr<CommandProcessor>> 
        StringCommandProcessorMap;
    StringCommandProcessorMap _scpm;
    template <class CP> void RegisterCommand(const std::string& command)
    {
        _scpm.insert(StringCommandProcessorMap::value_type(
            command, std::shared_ptr<CommandProcessor>(new CP())));
    }
public:
    CommandSelector()
    {
        RegisterCommand<FooCommandProcessor>("foo");
        RegisterCommand<BarCommandProcessor>("bar");
        // Register the rest of your commands here
    }
    ExitCode InvokeCommand(const std::string& command, 
        const std::vector<std::string>& parameters)
    {
        std::string lowercaseCommand;
        for (int i = 0; i < int(command.size()); i++)
        {
            lowercaseCommand.push_back(::tolower(command[i]));
        }
        StringCommandProcessorMap::iterator it = _scpm.find(lowercaseCommand);
        if (it == _scpm.end())
        {
            std::cout << "Unknown command: " << lowercaseCommand << std::endl;
            return ExitCode::WRONG_USAGE;
        }
        return it->second->Go(parameters);
    }
};

int main(int argc, char* argv[])
{
    if (argc < 2)
    {
        std::cout << "Usage: <your_exe_name> <command> [arguments]" << std::endl;
        return int(ExitCode::WRONG_USAGE);
    }
    std::string command(argv[1]);
    std::vector<std::string> parameters;
    for (int i = 2; i < argc; i++)
    {
        parameters.push_back(std::string(argv[i]));
    }
    CommandSelector cs;
    ExitCode ec = cs.InvokeCommand(command, parameters);
    return int(ec);
}
Sign up to request clarification or add additional context in comments.

Comments

0

You can call exec methods with the required argument to run your commands

Checkout: http://linux.die.net/man/3/exec

Comments

0

Check out the Command pattern:

Encapsulate all commands/functions in their own objects. Most probably, you don't need 200 different Command classes but only a few, grouping similar function calls with same purpose and argument count and type.

Then make a map of strings to these command objects. Command objects all have the same interface and the differences in the argument count and type of the original fnctions is encapsulated within.

Comments

0

A table of function calls (or similar). If speed is important, using std::unordered_map to do something like:

std::unordered_map<std::string, function> cmds;
...
cmds["mycommand"] = myCommandFunction();

I personally have written a dozen different programs with static array tables of string + function pointer as well, and just using a plain loop to iterate over the array - it's usually not the slowest part of the design [if speed matters, profile your code to see where it's taking time, then optimise, but start by writing clear and simple code, do not make the code more complex simply because you think it may be a large portion of time, before you have measured it]

An example of using std::map and function pointers [in this case lambda functions] can be found here: https://github.com/Leporacanthicus/lacsap/blob/master/builtin.cpp#L972

Comments

0

Example of my comment:

#include <string>
#include <unordered_map>
#include <iostream>

typedef void(*commandPtr)(const char* args);

std::unordered_map <std::string, commandPtr> commands;

void someCommand(const char* args)
{
    std::cout << "some command with args : " << args << std::endl;
}

int main()
{
    commands.insert(std::make_pair("someCommand", someCommand)); // add a command to the map
    std::string command, args;
    std::cin >> command >> args;
    if (commands.find(command) == commands.end()) // the command doesn't exist
        std::cout << "Command doesn't exist";
    else
        commands.find(command)->second(args.c_str()); // call the command with args
    std::cin.get();
    return 0;
}

This allows for just one abitrary argument though.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.