2

I have a template class(CrMultiIndex) that receive as template parameter a definition of boost multi index(GlobalHash).

I need :

  1. To add statistics to my template class according to Index used. So i need a way to resize the vector(m_StatsByIndex) at init with the number of existing indices.
  2. I still want the user to search according to tag and not index number. So i need a way to convert from tag to index number so i can update statistics in vector according to index in vector.

I have template class

template <typename KeysType, typename MultiIndexType>
class CrMultiIndex
{

std::vector<SrStatisticsByIndex> m_StatsByIndex;

public:
MultiIndexType *m_pMultiIndex=NULL; 

CrMultiIndex()
{
    m_pMultiIndex = new MultiIndexType(typename 
    MultiIndexType::ctor_args_list());
}

Here is the definition of boost multi index container:

typedef boost::multi_index::multi_index_container<
  CrUsersKeys,
  UsersKey_hash_indices/*,
  bip::allocator<CrUsersKeys,bip::managed_shared_memory::segment_manager>*/
> GlobalHash;

with a search function according to Tag

template <typename TagType,typename SearchingKey>
typename MultiIndexType::template index<TagType>::type::iterator  
GetIteratorBy(SearchingKey & key)
{
    return  m_pMultiIndex->template get<TagType>().find(key) ;
}

Code is at http://coliru.stacked-crooked.com/a/d97195a6e4bb7ad4

yaron
  • 439
  • 6
  • 16

2 Answers2

3

You'd need to query the embedded index type lists:

typedef typename MultiIndexType::index_type_list::size NumberOfIndexes;

template <typename Tag> constexpr static size_t IndexOfTag() {
    namespace mpl = boost::mpl;
    using tl = typename MultiIndexType::index_type_list;
    using B  = typename mpl::begin<tl>::type;
    using helper = typename MultiIndexType::template index<Tag>;
    static_assert(helper::index_found, "index not found");
    auto N = mpl::distance<B, typename helper::iter>::value;
    return N;
}

Or, using Boost Mpl all the way:

typedef typename MultiIndexType::index_type_list::size NumberOfIndexes;

template <typename Tag> constexpr static size_t IndexOfTag() {
    namespace mpl = boost::mpl;
    using tl = typename MultiIndexType::index_type_list;
    using B  = typename mpl::begin<tl>::type;
    using E  = typename mpl::end<tl>::type;
    using It = typename mpl::find_if<tl, bmi::detail::has_tag<Tag> >::type;
    static_assert(not std::is_same<E, It>(), "index not found");
    auto N = mpl::distance<B, It>::value;
    return N;
}

You can use it like so:

template <typename TagType, typename SearchingKey>
    typename MultiIndexType::template index<TagType>::type::iterator 
GetIteratorBy(SearchingKey &key) {
    auto& idx   = m_pMultiIndex.template get<TagType>();
    auto& stats = GetStats<TagType>();

    auto it = idx.find(key);
    ++(it == idx.end()? stats.searchedNotFound : stats.searchedSuccessfully);

    return it;
}

DEMO

Note the code has been simplified:

Live On Coliru

#include <iostream>
#include <boost/multi_index/member.hpp>           // for member
#include <boost/multi_index/hashed_index.hpp>     // for hashed_unique
#include <boost/multi_index/ordered_index.hpp>    // for ordered_non_unique
#include <boost/multi_index_container.hpp>        // for multi_index_container

namespace bmi = boost::multi_index;

struct SrStatisticsByIndex {
    int deleted;
    int searchedSuccessfully;
    int searchedNotFound;
};

template <typename MultiIndexType, typename ValueType = typename MultiIndexType::value_type> 
class CrMultiIndex {

    typedef typename MultiIndexType::index_type_list::size NumberOfIndexes;

    template <typename Tag> constexpr static size_t IndexOfTag() {
        using tl = typename MultiIndexType::index_type_list;
        using B  = typename boost::mpl::begin<tl>::type;
        using helper = typename MultiIndexType::template index<Tag>;
        static_assert(helper::index_found, "index not found");

        return boost::mpl::distance<B, typename helper::iter>::value;
    }

  public:
    MultiIndexType m_pMultiIndex;

    template <typename Tag> SrStatisticsByIndex& GetStats()
        { return m_StatsByIndex.at(IndexOfTag<Tag>()); }

    template <typename Tag> SrStatisticsByIndex const& GetStats() const
        { return m_StatsByIndex.at(IndexOfTag<Tag>()); }

    // All the protected function are non locking function
    template <typename TagType, typename SearchingKey>
        typename MultiIndexType::template index<TagType>::type::iterator 
    GetIteratorBy(SearchingKey &key) {
        auto& idx   = m_pMultiIndex.template get<TagType>();
        auto& stats = GetStats<TagType>();

        auto it = idx.find(key);
        ++(it == idx.end()? stats.searchedNotFound : stats.searchedSuccessfully);

        return it;
    }

    void Insert(ValueType const &key) {
        std::cout << (m_pMultiIndex.insert(key).second? "success":"failed") << std::endl;
    }

  private:
    std::vector<SrStatisticsByIndex> m_StatsByIndex { NumberOfIndexes() };
};

class CrUsersValue {
    int val1;
    int val2;
};

class CrUsersKeys {
  public:
    int IMSI;
    int TIMESTAMP;
    CrUsersValue val;
};

typedef boost::multi_index::multi_index_container<
        CrUsersKeys,
        bmi::indexed_by<
            bmi::ordered_non_unique<bmi::tag<struct TIMESTAMP_tag>,
                                    bmi::member<CrUsersKeys, int, &CrUsersKeys::TIMESTAMP> >,
            bmi::hashed_unique<bmi::tag<struct IMSI_tag>,
                               bmi::member<CrUsersKeys, int, &CrUsersKeys::IMSI> /*, boost::hash<int>, std::equal_to<int>*/>
        >
        /*, bip::allocator<CrUsersKeys,bip::managed_shared_memory::segment_manager>*/
    >
    GlobalHash;

int main() {
    CrMultiIndex<GlobalHash> multi;

    CrUsersKeys key;
    key.IMSI = 2;
    multi.Insert(key);

    int searchKey = 2;
    auto it = multi.GetIteratorBy<IMSI_tag>(searchKey);
    if (it != multi.m_pMultiIndex.get<IMSI_tag>().end())
        std::cout << "found " << std::endl;
}

Prints

success
found 
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Hi @sehe, you're relying on non-documented `index::iter` and `index::index_found`. – Joaquín M López Muñoz Mar 12 '18 at 16:34
  • @JoaquínMLópezMuñoz That's why I added the other approach too. Of course that still cheats by using `has_tag<>` but you know... Is there a documented way to do this without doing a lot of double work? I'd settle for this and accept that it might need a tweak with boost updates. – sehe Mar 12 '18 at 21:49
  • Hi @sehe, Thanks for your last answer. Do you also have a way to translate index to tag(n_to_tag)? My purpose is to loop on indexes and generate tags names string with typeid(T).name() – yaron Mar 13 '18 at 12:40
  • 1
    @sehe I posted an alternative `IndexOfTag` implementation. – Joaquín M López Muñoz Mar 13 '18 at 14:44
2

As a supplement to sehe's answer, this a rewrite of IndexOfTag that does not depend on undocumented Boost.MultiIndex features:

Live On Coliru

template<typename MultiIndexContainer,std::size_t N=0>
struct index_position:index_position<MultiIndexContainer,N+1>
{
  using index_type=typename boost::multi_index::nth_index<MultiIndexContainer,N>::type;
  using index_position<MultiIndexContainer,N+1>::case_of;
  static constexpr std::size_t case_of(std::in_place_type_t<index_type>){return N;}
};

template<typename MultiIndexContainer>
struct index_position<
  MultiIndexContainer,
  boost::mpl::size<typename MultiIndexContainer::index_type_list>::value
>
{
  static constexpr void case_of(...){}
};

template <typename MultiIndexContainer,typename Tag>
constexpr std::size_t IndexOfTag()
{
  using index_type=typename boost::multi_index::index<MultiIndexContainer,Tag>::type;
  return index_position<MultiIndexContainer>::case_of(std::in_place_type<index_type>);
}

Edit: In C++14:

Live On Coliru

template<typename MultiIndexContainer,std::size_t N=0>
struct index_position:index_position<MultiIndexContainer,N+1>
{
  using index_type=typename boost::multi_index::nth_index<MultiIndexContainer,N>::type;
  using index_position<MultiIndexContainer,N+1>::case_of;
  static constexpr std::size_t case_of(index_type*){return N;}
};

template<typename MultiIndexContainer>
struct index_position<
  MultiIndexContainer,
  boost::mpl::size<typename MultiIndexContainer::index_type_list>::value
>
{
  static constexpr void case_of(...){}
};

template <typename MultiIndexContainer,typename Tag>
constexpr std::size_t IndexOfTag()
{
  using index_type=typename boost::multi_index::index<MultiIndexContainer,Tag>::type;
  return index_position<MultiIndexContainer>::case_of((index_type*)(nullptr));
}
Joaquín M López Muñoz
  • 5,243
  • 1
  • 15
  • 20
  • Hi @JoaquínMLópezMuñoz , Thanks for your last answer. Do you also have a way to translate index to tag(n_to_tag)? My purpose is to loop on indexes and generate tags names string with typeid(T).name() at init. I work with c++14 – yaron Mar 13 '18 at 15:08
  • Ah. I wasn't sure that `index_type` would still include the tag (leaving the possibility of ambiguous results on identical matches). I'd say this is quite elegant, though using `std::inplace_type_t` strikes me as ... edgy, that also unnecessarily makes it require c++17. – sehe Mar 13 '18 at 15:08
  • @sehe rewritten in C++4 (I find `std::inplace_type_t` very self-explanatory, hence my using it initially; the `index_type*` alternative looks to me harder to grasp by the reader). I don't get your comment on `index_type` including the tag. – Joaquín M López Muñoz Mar 13 '18 at 15:28
  • 1
    @davidbobo rewritten in C++14. As for your additional request, could you please launch a new question for that? – Joaquín M López Muñoz Mar 13 '18 at 15:29
  • If it didn't include the tag as part of the type then it would possibly match the wrong index if the formulation of it happened to be identical, e.g.: [this might have returned index 0 twice](http://coliru.stacked-crooked.com/a/e51f8e7e73fb49e3) – sehe Mar 13 '18 at 15:31
  • Regarding `std::inplace_type_t`: that's obviously not self-explanatory, since it has literally nothing to do with inplace arguments nor std::optional, indeed. And there's nothing "special" about it. You'd usually include a simple `non_deduced` or `identity` struct instead, to avoid "abusing" random standard-library details (I know, they're documented, but your code didn't even include the relevant header :)). I agree that the pointer argument is less elegant, but that's false dichotomy IMO. – sehe Mar 13 '18 at 15:33
  • @JoaquínMLópezMuñoz The new question is at https://stackoverflow.com/questions/49260309/boost-multi-index-convert-index-to-tag – yaron Mar 13 '18 at 15:45
  • @sehe your observation on tags and index types is quite interesting. First, index specifiers with different tags have to produce *different* index types becasue of the existence of the nested `::tag_list` type. Second, actually all the index types of a given `multi_index_container` are mutually different even if their specifiers match exactly. – Joaquín M López Muñoz Mar 13 '18 at 15:57
  • 1
    @sehe I agree with you `identity` is cleaner than `in_place_type`. I whish there were some predefined `std` construct for passing types as function args. – Joaquín M López Muñoz Mar 13 '18 at 15:59
  • It appears we might get one in c++20 under the name `std::type_identity` http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0887r0.pdf – sehe Mar 20 '18 at 02:47