c++boostboost-multi-index

How to using boost::multi_index with struct in struct?


I have a vector containing information called ST_ThepInfo.

My problem is when using struct ST_ThepInfo in struct Infovalue_t.

struct ST_ThepInfo
{
    int length;
    string ex;
    int weight;
};

struct Infovalue_t {
    ST_ThepInfo s;
    int i;
};
struct ST_ThepInfo_tag {};

   typedef boost::multi_index_container<
    Infovalue_t,
    boost::multi_index::indexed_by<
    boost::multi_index::random_access<>, // this index represents insertion order
    boost::multi_index::hashed_unique<
        boost::multi_index::tag<ST_ThepInfo_tag>,
        boost::multi_index::member<Infovalue_t, ST_ThepInfo,&Infovalue_t::s>>
    >
> myvalues_t;

then i call these code:

myvalues_t s;
ST_ThepInfo k;
....
auto t = count.emplaceback(k, 0); 

However I get an error like this:

enter image description here

How do I fix it?


Solution

  • Hashed indexes require the key type to be hashable and equality-comparable.

    You need to provide these for the info struct:

    Live On Coliru

    #include <boost/multi_index/hashed_index.hpp>
    #include <boost/multi_index/member.hpp>
    #include <boost/multi_index/random_access_index.hpp>
    #include <boost/multi_index_container.hpp>
    namespace bmi = boost::multi_index;
    
    struct STInfo {
        int         length;
        std::string ex;
        int         weight;
    
        auto key_fields() const { return std::tie(length, ex, weight); }
    
        friend size_t hash_value(STInfo const& info) {
            using boost::hash_value;
            return hash_value(info.key_fields());
        }
    
        bool operator==(STInfo const& other) const {
            return key_fields() == other.key_fields();
        }
    };
    
    struct Infovalue_t {
        STInfo s;
        int    i;
    };
    
    using Table = bmi::multi_index_container< //
        Infovalue_t,                          //
        bmi::indexed_by<                      //
            bmi::random_access<>,             // represents insertion order
            bmi::hashed_unique<               //
                bmi::tag<struct byInfo>,      //
                bmi::member<Infovalue_t, STInfo, &Infovalue_t::s>> //
            >>;
    
    #include <fmt/ranges.h>
    #include <fmt/ostream.h>
    struct Format : fmt::formatter<int> {
        auto format(STInfo const& info, auto& ctx) const {
            return fmt::format_to(ctx.out(), "{}", info.key_fields());
        }
        auto format(Infovalue_t const& iv, auto& ctx) const {
            return fmt::format_to(ctx.out(), "({}, {})", iv.s, iv.i);
        }
    };
    template<> struct fmt::formatter<Infovalue_t> : Format{};
    template<> struct fmt::formatter<STInfo>      : Format{};
    
    int main() {
        Table  count;
        STInfo k;
        count.push_back({STInfo{42, "LtUaE", 99}, 30});
        count.push_back({STInfo{43, "SomethingElse", 98}, 40});
        count.push_back({STInfo{44, "SomethingElse", 97}, 30});
    
        fmt::print("{}\n", count);
    }
    

    Prints:

    [((42, "LtUaE", 99), 30), ((43, "SomethingElse", 98), 40), ((44, "SomethingElse", 97), 30)]
    

    Note that it is very important that equality matches hashing. If they don't agree, there's going to be Undefined Behaviour. This is the main reason why I don't recommend defaulting the equality operator as you can in c++20:

    #ifdef __cpp_impl_three_way_comparison
        auto operator<=>(STInfo const&) const = default;
    #endif
    

    This makes it less explicit that hash_value needs to agree with the members and risks they go out of sync when you e.g. add a member.