Conditions are the most natural way to control your code flow in programming, it’s almost the first thing that every programmer learn at the beginning. Sometimes conditions, especially if you are conditioning strings in C++, the code become very complicated to understand and very lazy. Let’s break this pattern.
Legacy Code
You join a new team and start to read the code. After some time reading, you see the following function:
std::shared_ptr<interface> deduce_type(std::string &&type) {
std::shared_ptr<interface> res;
if (type == "1") res = std::make_shared<impl1>();
else if (type == "2") res = std::make_shared<impl2>();
else if (type == "3") res = std::make_shared<impl3>();
else if (type == "4") res = std::make_shared<impl4>();
else if (type == "5") res = std::make_shared<impl5>();
else if (type == "6") res = std::make_shared<impl6>();
else if (type == "7") res = std::make_shared<impl7>();
return res;
}
You start to think to yourself “There must be a better way of doing it… Why not using switch
statement?”, and then you remember that switch
statements can’t be applied to string
elements in C++. So, is there a better way?
Hash Tables
Hash table is a structure which builds from Key
and Value
, which can be the same or different types. The way hash table works is it takes the key you enter, applies a hash for it and stores it in the result of the hash function place. When you want to access the same place, it applies the same hash function for the key you want to access to and returns the value that exists there.
Lucky us, the standard library takes care of this structure for us – std::unordered_map.
Quick Note
Pay attention to the difference between std::map
to std::unordered_map
:
std::map
– Red-Black tree implementation, using std::less
by default (operator<
between keys).
std::unordered_map
– Hash table implementation. (For complicated keys, you’ll have to implement the hash function for yourself.)
Refactor Conditioning
Let’s replace conditioning using hash tables:
class type_hash {
public:
type_hash() {
hash = {
{"1", [] { return std::make_shared<impl1>(); }},
{"2", [] { return std::make_shared<impl2>(); }},
{"3", [] { return std::make_shared<impl3>(); }},
{"4", [] { return std::make_shared<impl4>(); }},
{"5", [] { return std::make_shared<impl5>(); }},
{"6", [] { return std::make_shared<impl6>(); }},
{"7", [] { return std::make_shared<impl7>(); }}
};
}
std::shared_ptr<interface> deduce_type(std::string &&type) {
return hash.at(type)(); }
private:
std::unordered_map<std::string, std::function<std::shared_ptr<interface>()>> hash;
};
std::shared_ptr<interface> core_function(std::string &&type, type_hash &deducer) {
std::shared_ptr<interface> res;
res = deducer.deduce_type(std::move(type));
return res;
}
Go Further!
Assume we want to deduce a function out of closed functions collection:
class functions_collection {
public:
void func1() { num = 1; print_number(); }
void func2() { num = 2; print_number(); }
void func3() { num = 3; print_number(); }
void func4() { num = 4; print_number(); }
void func5() { num = 5; print_number(); }
void func6() { num = 6; print_number(); }
void func7() { num = 7; print_number(); }
private:
int num = 0;
void print_number() {
std::cout << num << "\n";
}
};
We need to create a hash collection that is limited to functions only from this class:
class type_hash {
private:
using functions_collection_t = void (functions_collection::*)();
public:
type_hash() {
hash = {
{"1", &functions_collection::func1},
{"2", &functions_collection::func2},
{"3", &functions_collection::func3},
{"4", &functions_collection::func4},
{"5", &functions_collection::func5},
{"6", &functions_collection::func6},
{"7", &functions_collection::func7},
};
}
functions_collection_t deduce_type(std::string &&type) {
return hash.at(type);
}
private:
std::unordered_map<std::string, void (functions_collection::*)()> hash;
};
void core_function(std::string &&type, type_hash &deducer) {
functions_collection fc;
auto desired_func = deducer.deduce_type(std::move(type));
(fc.*desired_func)();
}
Share Your Ideas
Have new ways to solve this lazy conditions way? Share them in the comments!
Checkout the GitHub repository with examples: cppsenioreas-hash-your-conditions.