Question

I'm trying to create a std::unordered_map, using a user-defined hash function and equality predicate, for matrix rows of integral built-in types. I use std::bind, since I need the hashing and equality functors to work for variable ranges. How would I get the following code to compile and work as intended? I'm guessing that my mistake is towards the bottom of the listing, where I use std::bind and instantiate the std::unordered_map.

Some clarifications:

I can't use boost::hash_combine because I care about individual bits in the integers stored in the matrix row, so unless I create my own iterator, boost::hash_combine would combine whole integers, leading to erroneous results. Also, I need the hash and equality functors to work on ranges from 1 to ~200,000, so specifying the range in template parameters would not be a reasonable option.


template <class T,class F,class A>
struct row_hash_right : std::function<size_t(
        ublas::matrix_row<ublas::matrix<T,F,A>>,
        unsigned, unsigned)>
{
        typedef ublas::matrix_row<ublas::matrix<T,F,A>> row_type;

        // I want to use std::bind to bind the last two arguments.
        size_t operator()(row_type& a, unsigned s, unsigned e)
        {
                // Implementation of hash function.
        }
};

template <class T,class F,class A>
struct row_equal_right : std::function<bool(
        ublas::matrix_row<ublas::matrix<T,F,A>>,
        ublas::matrix_row<ublas::matrix<T,F,A>>,
        unsigned, unsigned)>
{
        typedef ublas::matrix_row<ublas::matrix<T,F,A>> row_type;

        bool operator()(row_type& a, row_type& b, unsigned s, unsigned e)
        {
                // Implementation of equality predicate.
        }
};

// Inside a function.
for (unsigned i = 0; i < len; ++i) {
        for (unsigned j = i + 1; j < len; ++j) {
                auto x = std::bind(r_hash, _1, i, j);
                auto y = std::bind(r_equal, _1, _2, i, j);
                // ERROR:
                std::unordered_map<row_type, unsigned,
                        decltype(x), decltype(y)> m(256, x, y);
        }
}

The error:

Here is (what I think) the most important part of the error produced upon attempted compilation:

/usr/include/c++/4.6/bits/stl_pair.h:92:11: error: ‘std::pair<_T1, _T2>::first’ has incomplete type /usr/include/boost/numeric/ublas/fwd.hpp:73:11: error: declaration of ‘const struct boost::numeric::ublas::matrix_row, boost::numeric::ublas::unbounded_array > > >’

If you want the see the whole thing, I've dumped it all here:


In file included from /usr/include/c++/4.6/bits/stl_algobase.h:65:0,
                 from /usr/include/c++/4.6/bits/char_traits.h:41,
                 from /usr/include/c++/4.6/ios:41,
                 from /usr/include/c++/4.6/ostream:40,
                 from /usr/include/c++/4.6/iostream:40,
                 from src/test/read_test.cpp:1:
/usr/include/c++/4.6/bits/stl_pair.h: In instantiation of ‘std::pair, boost::numeric::ublas::unbounded_array > > >, unsigned int>’:
/usr/include/c++/4.6/bits/stl_function.h:486:12:   instantiated from ‘std::_Select1st, boost::numeric::ublas::unbounded_array > > >, unsigned int> >’
/usr/include/c++/4.6/bits/hashtable_policy.h:789:20:   instantiated from ‘std::__detail::_Hash_code_base, boost::numeric::ublas::unbounded_array > > >, std::pair, boost::numeric::ublas::unbounded_array > > >, unsigned int>, std::_Select1st, boost::numeric::ublas::unbounded_array > > >, unsigned int> >, std::_Bind, boost::numeric::ublas::unbounded_array > >(std::_Placeholder, std::_Placeholder, unsigned int, unsigned int)>, std::_Bind, boost::numeric::ublas::unbounded_array > >(std::_Placeholder, unsigned int, unsigned int)>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, false>’
/usr/include/c++/4.6/bits/hashtable.h:105:11:   instantiated from ‘std::_Hashtable, boost::numeric::ublas::unbounded_array > > >, std::pair, boost::numeric::ublas::unbounded_array > > >, unsigned int>, std::allocator, boost::numeric::ublas::unbounded_array > > >, unsigned int> >, std::_Select1st, boost::numeric::ublas::unbounded_array > > >, unsigned int> >, std::_Bind, boost::numeric::ublas::unbounded_array > >(std::_Placeholder, std::_Placeholder, unsigned int, unsigned int)>, std::_Bind, boost::numeric::ublas::unbounded_array > >(std::_Placeholder, unsigned int, unsigned int)>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, false, false, true>’
/usr/include/c++/4.6/bits/unordered_map.h:44:11:   instantiated from ‘std::__unordered_map, boost::numeric::ublas::unbounded_array > > >, unsigned int, std::_Bind, boost::numeric::ublas::unbounded_array > >(std::_Placeholder, unsigned int, unsigned int)>, std::_Bind, boost::numeric::ublas::unbounded_array > >(std::_Placeholder, std::_Placeholder, unsigned int, unsigned int)>, std::allocator, boost::numeric::ublas::unbounded_array > > >, unsigned int> >, false>’
/usr/include/c++/4.6/bits/unordered_map.h:256:11:   instantiated from ‘std::unordered_map, boost::numeric::ublas::unbounded_array > > >, unsigned int, std::_Bind, boost::numeric::ublas::unbounded_array > >(std::_Placeholder, unsigned int, unsigned int)>, std::_Bind, boost::numeric::ublas::unbounded_array > >(std::_Placeholder, std::_Placeholder, unsigned int, unsigned int)>, std::allocator, boost::numeric::ublas::unbounded_array > > >, unsigned int> > >’
./sal/alg/ehh.hpp:144:31:   instantiated from ‘sal::ehh_results sal::compute_ehh(boost::numeric::ublas::matrix&, unsigned int) [with FloatType = double, T = unsigned int, F = boost::numeric::ublas::basic_row_major, A = boost::numeric::ublas::unbounded_array >]’
src/test/read_test.cpp:11:51:   instantiated from here
/usr/include/c++/4.6/bits/stl_pair.h:92:11: error: ‘std::pair::first’ has incomplete type
/usr/include/boost/numeric/ublas/fwd.hpp:73:11: error: declaration of ‘const struct boost::numeric::ublas::matrix_row, boost::numeric::ublas::unbounded_array > > >’

Was it helpful?

Solution

If you want to hash a range of things, you need something like hash_combine(). I usually lift this function from Boost (surprisingly, it wasn't included in the standard!). Here's how I'd use it on std::arrays, and I trust you can manipulate this into something to work on matrix rows:

#include <array>

template <class T>
inline void hash_combine(std::size_t & seed, const T & v)
{
    std::hash<T> hasher;
    seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}

namespace std
{
    template<typename T, size_t N> struct hash<array<T, N>>
    {
        inline size_t operator()(const array<T, N> & a) const
        {
            size_t seed = 0;
            for (size_t i = 0; i != N; ++i)
            {
                ::hash_combine(seed, a[i]);
            }
            return seed;
        }
    };
}

(The above specialisation allows you to use std::unordered_set<std::array<int, 10>> etc.)

If the matrix rows don't come with an equality predicate, you could also add a specialization of std::equal_to for matrix rows.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top