I have encountered this problem quite often and did not find a solution.
I have written a program which benchmarks different algorithms. The program selects a specific algorithm based on command line arguments, like
./my_program algorithm_specific_name1
Now in the code I am doing this:
//define all algorithm names here on which we want to dispatch below
const std::string ALGORITHM_SPECIFIC_NAME1 = "algorithm_specific_name1";
const std::string ALGORITHM_SPECIFIC_NAME2 = "algorithm_specific_name2";
...
//define data structure for dynamic lookup of algorithm name based on command-line-argument
const std::vector<std::string> ALL_ALGORITHMS{ALGORITHM_SPECIFIC_NAME1, ALGORITHM_SPECIFIC_NAME2, ... };
//usage of command-line-arguments
const std::vector<std::string> arguments{argv + 1, argv + argc};
const std::string algorithm_argument = arguments[0];
//check if given argument is valid choice of algorithm
if (std::find(ALL_ALGORITHMS.begin(), ALL_ALGORITHMS.end(), algorithm_argument) == ALL_ALGORITHMS.end()) {
std::cerr << "No valid algorithm selected: " << algorithm_argument << std::endl;
return -1;
}
std::cout << "Using " << algorithm_argument << std::endl;
//dispatch on specific name of algorithm because ALL_ALGORITHMS[0] would not be very descriptive
if (algorithm_argument == ALGORITHM_SPECIFIC_NAME1) {
//call algorithm one
}
if (algorithm_argument == ALGORITHM_SPECIFIC_NAME2) {
//call algorithm two
}
...
As you can see there are two big problems with this code:
Also I can not enforce the exhaustive checking of all variants. Of course there are gcc extensions like Stringification but that only solves the first problem and not the second and it is not standard.
I would like to know if there is a solution to this problem. So I want to have named variables with their contents also in a data structure for lookup, something like an enum of strings with a "contains"-method and exhaustive switch.
Of course the code above works but it is not a perfect solution for this kind of problem, less redundance would be nice.
You don't have to do any double booking when using an associative container like a std::map
or a std::unordered_map
. You can store lambdas directly in the container.
A map from algorithm name to a function/functor could look like this:
const std::map<std::string_view, int (*)(const std::vector<std::string_view>&)>
algomap{
{"algo1",
[](const std::vector<std::string_view>& args) -> int {
std::cout << "Got arguments:";
for (auto& arg : args) std::cout << ' ' << arg;
std::cout << '\n';
return 0;
}
},
{"algo2",
[](const std::vector<std::string_view>& args) -> int {
if (args.size() != 2) // throwing a user-defined exception:
throw algo_error("requires two arguments: first-name last-name");
return 0;
}
}
};