I recently had the problem that I wanted a hashmap where there would be a variable amount of distinct classes which each could be used to look up each other. I couldn't find any implementation online so I wrote it myself:
// Compile with c++20 because this code uses concepts
#include <concepts>
#include <vector>
#include <utility>
#include <iostream>
#include <sstream>
#include <string>
#include <cstdio>
#include <tuple>
template <typename T> concept Streamable = requires(std::ostream &os, T value) {
{ os << value } -> std::convertible_to<std::ostream &>;
};
template<typename T> concept nMapValidType = std::default_initializable<T> && std::equality_comparable<T> && Streamable<T>;
template<nMapValidType ...T>
class nMap {
public:
nMap(std::initializer_list<std::tuple<T...>> iList) :
vec(iList) {}
~nMap() = default;
void add(std::tuple<T...> tuple) {
this->vec.push_back(tuple);
}
nMap<T...>& operator=(std::initializer_list<std::tuple<T...>> iList) {
this->vec = iList;
return *this;
}
template<nMapValidType Gettee, nMapValidType Getter>
Gettee get(Getter getVal, bool throwError = true) const {
if constexpr (!std::disjunction_v<std::is_same<Gettee, T>...>) {
std::perror("Return type for nMap::get not found.");
} else {}
if constexpr (!std::disjunction_v<std::is_same<Getter, T>...>) {
std::perror("Getter type for nMap::get not found.");
} else {}
for (const auto& tuple: this->vec) {
if (std::get<Getter>(tuple) == getVal) {
return std::get<Gettee>(tuple);
}
}
if (throwError) {
std::stringstream sstr;
sstr << "No keys to value `" << getVal << "` found.";
std::perror(sstr.str().c_str());
}
return {};
}
template<nMapValidType Type>
bool contains(Type item) const {
if constexpr (!std::disjunction_v<std::is_same<Type, T>...>) {
std::perror("Type passed to nMap::contains not found.");
} else {}
for (const auto &tuple: this->vec) {
if (std::get<Type>(tuple) == item) {
return true;
}
}
return false;
}
private:
std::vector<std::tuple<T...>> vec;
};
template<nMapValidType A, nMapValidType B, nMapValidType C>
using triMap = nMap<A, B, C>;
template<nMapValidType A, nMapValidType B>
using biMap = nMap<A, B>;
int main() {
const nMap<int, char, bool> boolMap = {
{ 0, 'f', false},
{ 1, 't', true}
};
bool testBool = boolMap.get<bool>('f');
int testInt = boolMap.get<int, bool>(true);
return testInt;
}
While functioning quite well, I feel that the lookup for both get and contains could be improved.
Another feature that this implementation is missing, is both the get and contains function. I haven't had a use for non-const nMap's yet, so this is not a dealbreaker but I just don't want to copy-paste those huge chunks of code.
Something that I feel is impossible is the separation of declaration and definition of these functions, as they are included in many files in a larger project I'm working on and compile-times have proven to be quite slow (I don't know if it is because of this class, this just may be a factor).