Code:
#include <iostream>
#include <string>
#include <tuple>
#include <unordered_map>
// http://stackoverflow.com/questions/7110301/generic-hash-for-tuples-in-unordered-map-unordered-set
size_t hash_combiner(size_t left, size_t right) //replacable
{ return left^right;}
template<int index, class...types>
struct hash_impl {
size_t operator()(size_t a, const std::tuple<types...>& t) const {
typedef typename std::tuple_element<index, std::tuple<types...>>::type nexttype;
hash_impl<index-1, types...> next;
size_t b = std::hash<nexttype>()(std::get<index>(t));
return next(hash_combiner(a, b), t);
}
};
template<class...types>
struct hash_impl<0, types...> {
size_t operator()(size_t a, const std::tuple<types...>& t) const {
typedef typename std::tuple_element<0, std::tuple<types...>>::type nexttype;
size_t b = std::hash<nexttype>()(std::get<0>(t));
return hash_combiner(a, b);
}
};
namespace std {
template<class...types>
struct hash<std::tuple<types...>> {
size_t operator()(const std::tuple<types...>& t) const {
const size_t begin = std::tuple_size<std::tuple<types...>>::value-1;
return hash_impl<begin, types...>()(1, t); //1 should be some largervalue
}
};
}
// END of stack overflow code
template <typename TupleT, typename T>
struct AppendTupleElement;
template <typename... Types, typename T>
struct AppendTupleElement<std::tuple<Types...>, T>
{
typedef std::tuple<Types..., T> type;
};
template <unsigned int N>
struct GenerateSparseMatrixKey
{
typedef typename AppendTupleElement
<
typename GenerateSparseMatrixKey<N - 1>::type,
unsigned int
>::type type;
};
template <>
struct GenerateSparseMatrixKey<0>
{
typedef std::tuple<> type;
};
template <typename T, unsigned int N>
class SparseMatrix
{
public:
typedef T MappedType;
static constexpr unsigned int Rank = N;
// This is the actual indexing operator.
// Passing arguments of wrong types, or passing their wrong number,
// will result in compile-time error.
template <typename... Dimensions>
MappedType& operator()(Dimensions... dimensions)
{
return map[std::forward_as_tuple(dimensions...)];
}
private:
typedef typename GenerateSparseMatrixKey<N>::type KeyType;
// std::unordered_map has tuple as its key. I took tuple hashing
// from stack overflow which specializes std::hash, but I would
// personally do it by defining a separate hashing class which then
// I would explicitly pass as a third template argument.
std::unordered_map<KeyType, MappedType> map;
};
int main()
{
SparseMatrix<std::string, 4> matrix;
matrix(0, 0, 1, 2) = "AA";
matrix(0, 0, 1, 3) = "BB";
matrix(0, 0, 4, 4) = "CC";
std::cout << matrix(0, 0, 1, 2) << std::endl;
std::cout << matrix(0, 0, 1, 3) << std::endl;
std::cout << matrix(0, 0, 1, 4) << std::endl; // doesn't exist
std::cout << matrix(0, 0, 4, 4) << std::endl;
}
Compiles with GCC 4.8.1.