24 Ekim 2017 Salı

multi_index multi_index_container Sınıfı

Giriş
Şu satırı dahil ederiz.
#include <boost/multi_index_container.hpp>
Tanımlama
Şöyle yaparız
typedef multi_index_container<
  Foo,
  indexed_by<
    ...
  >
> ContainerType;
Tag ile Tanımlama
Şöyle yaparız.
struct id_tag { };
struct name_tag { };

typedef multi_index_container<  Foo,
  boost::multi_index::indexed_by<
    boost::multi_index::ordered_unique<
      boost::multi_index::tag<id_tag>, ... >,
    boost::multi_index::ordered_unique<
      boost::multi_index::tag<name_tag>, ... >
    >
  >;
Kalıtım
Önce bir template tanımlarız. Şöyle yaparız
template <class D, class E>

classa MyContainer : public boost::multi_index_container<D, E>
{...};
Sonra template için parametreleri şöyle tanımlarız.
// Requires -std=c++11    
using FooContainer = MyContainer<
  Foo,
  boost::multi_index::indexed_by<...>
>;
Sonra bu sınıfı ilklendiririz.
FooContainer items;
Constructor
Şöyle yaparız
ContainerType items;
Constructor - iterator + iterator
Şöyle yaparız.
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>

namespace bmi = boost::multi_index; 

using Index = bmi::multi_index_container<char, 
      bmi::indexed_by<
          bmi::sequenced<>,
          bmi::ordered_unique<bmi::tag<struct unique>, bmi::identity<char> >
      > > ;


auto const str = ...;
Index idx(str.begin(), str.end());
Constructor - variadic template
Elimizde shared_ptr içeren bir multi_index olsun
namespace bmi = boost::multi_index;
using Table   = bmi::multi_index_container<moveonly,
    bmi::indexed_by<
        bmi::random_access<bmi::tag<struct _ra> >
    > >;
Bu sınıfı variadic olarak ilklendiren bir metod olsun.
template <typename V, typename... Init>
auto make_bmi_set(Init&&... values) {
  return Table<std::string> { 
    std::make_shared<V>(std::forward<Init>(values))...
  };
}
Şöyle yaparız.
auto items = make_bmi_set<std::string>("one", "two", "three", "four", "Hungary");
erase metodu
Örnek
Şöyle yaparız.
auto it = table.begin();
if (it == table.end())
  throw std::logic_error("...");

table.erase(it);
erase metodu - it + it
Örnek
Şöyle yaparız.
it begin = ...; it end = ...;
c.erase(begin,end);
get metodu - int sıra numarası
İndekslere get() metodu ile erişilir. Şöyle yaparız.
itemContainer::nth_index<0>::type & sequentialItems = items.get<O>();

itemContainer::nth_index<1>::type & associativeItems = items.get<1>();
get metodu - tag
İndekslere tag ile erişilir. Şöyle yaparız.
auto& by_id_tag = items.get<id_tag>();
insert metodu
Şöyle yaparız.
container.insert(container.end(),foo);
modify metodu
Hem indekslenen hem de indekslenmeyen veriyi değiştirmek için kullanılır.

Boost 1.66'dan önceki açıklaması şöyle. Yani modify() içinde exception fırlatmamak lazım.
Modify expects the user-provided modifier not to throw unless it doesn't change the keys of the elements, in which case undefined behavior ensues.
Sonrasında eğer exception fırlatılırsa da eleman silinir hale getirildi.

Örnek
Şöyle yaparız.
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <iostream>

struct moveonly {
  ...
};

namespace bmi = boost::multi_index;
using Table   = bmi::multi_index_container<moveonly,
  bmi::indexed_by<
    bmi::random_access<bmi::tag<struct _ra> >
  > >;

Table table = ...;


auto it = table.begin();

if (table.modify(it, [&](moveonly& v) { ... })) {
  table.erase(it);
}
Örnek
İndeksi mevcut bir indeks ile aynı hale getirince elemanın silinmesi gerekir. Şöyle yaparız.
struct employee {
  int id;
  std::string name;
}

typedef multi_index_container<
  employee,
  indexed_by<
    ordered_unique<BOOST_MULTI_INDEX_MEMBER(employee,int,id)>>
  >
> employee_set;

employee_set es;    
es.insert(employee{0,"Joe"});
es.insert(employee{1,"Carl"});
es.insert(employee{2,"Robert"});
es.insert(employee{4,"John"});

try {
  auto it = es.find(4); // John
  es.modify(it, [](auto& employee) {
    employee.id = 1; // Same ID of Carl, should be erased
    
  });
} catch (const std::runtime_error& err) {
    // handle error...
}
İşlemden sonra çıktı olarak şunu alırız.
0 Joe
1 Carl
2 Robert
project
Bir index iteratörünü bir başka indeks iteratörüne çevirir. Örnek bir LRU cache. Önce eleman hash_index ile bulunuyor. Daha sonra hash_index iterator, container.project() ile sequenced_iterator tipine çevriliyor. sequenced_index elemanı kendi içinde relocate() ile en sona taşıyor.

using namespace boost::multi_index;

typedef boost::multi_index_container
<
  item_t,
  boost::multi_index::indexed_by
  <
    hashed_unique<boost::multi_index::member<item_t,Key,&item_t::key> >,
    sequenced<>
  >
> cache_t;

enum index_idx
{
  e_map = 0,
  e_seq = 1,
};

cache_t cache_;
bool find(const Key& key, T& t)
{
  typename cache_t::nth_index<e_map>::type& hash_index = cache_.get<e_map>();
  auto itr = hash_index.find(key);
  if (itr != hash_index.end())
  {
    return false;
  }
  t = itr->t;
  typename cache_t::nth_index<e_seq>::type& sequenced_index = cache_.get<e_seq>();
  auto itr2 = cache_.project<e_seq>(itr);
  sequenced_index.relocate(itr2,sequenced_index.end());
  return true;
}
size metodu
Index'ten bağımsızdır. Şöyle yaparız.
std::cout << "size=" << items.size() << "\n";



Hiç yorum yok:

Yorum Gönder