Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Boost.MultiIndex Documentation - Tutorial - Key extraction

Boost , , Boost.MultiIndex Documentation - Tutorial

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Boost.MultiIndex Tutorial: Key extraction



Contents

Introduction

Ассоциативные контейнеры STL имеют понятие ключа, хотя и в несколько начальной форме. Так, ключи таких контейнеров идентифицируются по вложенному типу.key_type; дляstd::setиstd::multisets,key_typeсовпадает сvalue_type, т.е. ключ является самим элементом.std::mapиstd::multimapУправление элементами типаstd::pair, где ключом является первый член. В любом случае процесс получения ключа от данного элемента неявно фиксируется и не может быть настроен пользователем.

Механизмы извлечения фиксированных ключей, подобные тем, которые выполняются ассоциативными контейнерами STL, плохо масштабируются в контексте Boost. MultiIndex, где несколько индексов разделяют их определениеvalue_type, но могут иметь совершенно другую семантику поиска. По этой причине Boost.MultiIndex формализует понятие а.Key Extractorдля того, чтобы сделать его явным и контролируемым в определении ключевых индексов.

Интуитивно говоря, экстрактор ключа является функциональным объектом, который принимает ссылку на элемент и возвращает связанный с ним ключ. Формальная концепция также накладывает некоторые разумные ограничения на стабильность процесса, в том смысле, что предполагается, что экстракторы возвращают один и тот же ключ при прохождении одного и того же элемента: это в соответствии с неформальным пониманием того, что ключи на самом деле являются некоторой «частью» элемента и не зависят от внешних данных.

Read/write key extractors

Экстрактор ключа называетсяread/write, если он возвращает непостоянную ссылку на ключ при прохождении непостоянного элемента, и он называетсяread-onlyв противном случае. Повышаю. MultiIndex требует, чтобы экстрактор ключа был прочитан/записан при использованиимодифицируемой_ключафункции члена упорядоченных и хешированных индексов. Во всех других ситуациях достаточно только экстракторов для чтения.Ветхий Завет. Экстракторы ключей MultiIndexподробно описывают, какие из предопределенных экстракторов ключей читаются/записываются.

Predefined key extractors

identity

Идентификатор ключавозвращает весь базовый объект в качестве связанного ключа:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
multi_index_container<
  int,
  indexed_by<
    ordered_unique<
      identity<int> // the key is the entire element
    >
  >
> cont;

member

элементключевых экстракторов возвращает ссылку на заданное поле данных базового объекта. Например, в следующей версии нашего знакомого контейнера для сотрудников:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
typedef multi_index_container<
  employee,
  indexed_by<
    ordered_unique<identity<employee> >,
    ordered_non_unique<member<employee,std::string,&employee::name> >,
    ordered_unique<member<employee,int,&employee::ssnumber> >
  >
> employee_set;

второй и третий индексы используютчленэкстракторы насотрудника::имяисотрудник::числосоответственно. Спецификация инстанциацииэлементапроста, но немного надумана:

member<(base type),(key type),(pointer to member)>

Может показаться, что первый и второй параметры излишни, поскольку тип базового объекта и связанного с ним поля данных уже подразумеваются в указателе на аргумент участника: к сожалению, извлечь эту информацию с помощью современных механизмов C++ невозможно, что делает синтаксисучастниканемного слишком многословным.

const_mem_fun and mem_fun

Иногда ключом индекса является не конкретный элемент данных элемента, а значение, возвращаемое конкретной функцией-членом. Это напоминает понятие 48 вычисленных индексов 49, поддерживаемых некоторыми реляционными базами данных. Boost.MultiIndex поддерживает этот вид извлечения ключей черезconst_mem_fun. Рассмотрим следующий контейнер, где сортировка по третьему индексу основана на длине поля имени:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/mem_fun.hpp>
struct employee
{
  int         id;
  std::string name;
  employee(int id,const std::string& name):id(id),name(name){}
  bool operator<(const employee& e)const{return id<e.id;}
  // returns the length of the name field
  std::size_t name_length()const{return name.size();}
};
typedef multi_index_container<
  employee,
  indexed_by<
    // sort by employee::operator<
    ordered_unique<identity<employee> >,
    
    // sort by less<string> on name
    ordered_non_unique<member<employee,std::string,&employee::name> >,
    
    // sort by less<int> on name_length()
    ordered_non_unique<
      const_mem_fun<employee,std::size_t,&employee::name_length>
    >
  >
> employee_set;

const_mem_funИспользование синтаксиса аналогичночлен:

const_mem_fun<(base type),(key type),(pointer to member function)>

Упомянутая функция члена должна бытьconst, не принимать аргументов и возвращать значение указанного ключевого типа. Почти всегда вы захотите использовать функциюconst, поскольку элементы вmulti_index_containerрассматриваются как постоянные, так же как элементыstd::set. Однако для использования с непостоянными функциями-членами предусмотренmem_fun, применимость которого обсуждается в пунктерасширенных функций Boost. Ключевые экстракторы MultiIndex.

Пример 2в разделе примеров предоставляет полную программу, показывающую, как использоватьconst_mem_fun.

global_fun

В то время какconst_mem_funиmem_funоснованы на заданной членской функции базового типа, из которой извлекается ключ,global_funпринимает глобальную функцию (или статическую членскую функцию), принимая базовый тип в качестве своего параметра и возвращая ключ:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/global_fun.hpp>
struct rectangle
{
  int x0,y0;
  int x1,y1;
};
unsigned long area(const rectangle& r)
{
  return (unsigned long)(r.x1-r.x0)*(r.x1-r.x0)+
         (unsigned long)(r.y1-r.y0)*(r.y1-r.y0);
}
typedef multi_index_container<
  rectangle,
  indexed_by<
    // sort by increasing area
    ordered_non_unique<global_fun<const rectangle&,unsigned long,&area> >
  >
> rectangle_container;

Спецификацияglobal_funподчиняется следующему синтаксису:

global_fun<(argument type),(key type),(pointer to function)>

где тип аргумента и тип ключа должны соответствоватьточнотем, которые указаны в подписи используемой функции; например, в примере выше типа аргументаconst rectangle &, без опущения частей «const» и «&». Таким образом, хотя в большинстве случаев базовый тип будет принят путем постоянной ссылки,global_funтакже готов принять функции, принимающие их аргумент по значению или по непостоянной ссылке: этот последний случай обычно не может использоваться непосредственно в спецификацииmulti_index_containers, поскольку их элементы рассматриваются как постоянные, но раздел орасширенных функциях Boost. Экстракторы ключей MultiIndexописывают допустимые случаи использования извлечения ключей на основе таких функций с непостоянным эталонным аргументом.

Пример 2в разделе примеров используетgobal_fun.

Определенные пользователем ключевые экстракторы

Хотяпредопределенные ключевые экстракторыпредоставлены Boost. MultiIndex предназначен для обслуживания большинства случаев, пользователь также может предоставить свои собственные ключевые экстракторы в более экзотических ситуациях, если они соответствуют.Ключевой экстракторконцепция.

// some record class
struct record
{
  boost::gregorian::date d;
  std::string            str;    
};
// extracts a record's year
struct record_year
{
  // result_type typedef required by Key Extractor concept
  typedef boost::gregorian::greg_year result_type; 
  
  result_type operator()(const record& r)const // operator() must be const
  {
    return r.d.year();
  }
};
// example of use of the previous key extractor
typedef multi_index_container<
  record,
  indexed_by<
    ordered_non_unique<record_year> // sorted by record's year
  >
> record_log;

Пример 6в разделе примеров применяет некоторые определяемые пользователем экстракторы ключей в сложном сценарии, где ключи доступны через указатели.

Composite keys

В реляционных базах данных составные ключи зависят от двух или более полей данной таблицы. Аналогичная концепция в Boost. MultiIndex моделируется с помощьюcomposite_key, как показано в примере:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/composite_key.hpp>
struct phonebook_entry
{
  std::string family_name;
  std::string given_name;
  std::string phone_number;
  phonebook_entry(
    std::string family_name,
    std::string given_name,
    std::string phone_number):
    family_name(family_name),given_name(given_name),phone_number(phone_number)
  {}
};
// define a multi_index_container with a composite key on
// (family_name,given_name)
typedef multi_index_container<
  phonebook_entry,
  indexed_by<
    //non-unique as some subscribers might have more than one number
    ordered_non_unique< 
      composite_key<
        phonebook_entry,
        member<phonebook_entry,std::string,&phonebook_entry::family_name>,
        member<phonebook_entry,std::string,&phonebook_entry::given_name>
      >
    >,
    ordered_unique< // unique as numbers belong to only one subscriber
      member<phonebook_entry,std::string,&phonebook_entry::phone_number>
    >
  >
> phonebook;

Композитный_ключпринимает два или более ключевых экстракторов по одному и тому же значению (здесьphonebook_entry). Поисковые операции на композитном ключе выполняются путем передачи кортежей с искомыми значениями:

phonebook pb;
...
// search for Dorothea White's number
phonebook::iterator it=pb.find(std::make_tuple("White","Dorothea"));
std::string number=it->phone_number;

Композитные ключи сортируются лексикографическим порядком, т.е. сортировка выполняется первым ключом, затем вторым ключом, если первый равен и т.д. Этот порядок позволяет осуществлять частичный поиск, где указаны только первые ключи:

phonebook pb;
...
// look for all Whites
std::pair<phonebook::iterator,phonebook::iterator> p=
  pb.equal_range(std::make_tuple("White"));

В качестве удобства для обозначения, когда указан только первый ключ, можно передать аргумент напрямую, не включив его в кортеж:

phonebook pb;
...
// look for all Whites
std::pair<phonebook::iterator,phonebook::iterator> p=pb.equal_range("White");

С другой стороны, не допускаются частичные поиски без указания первых ключей.

По умолчанию для каждого подключа композитного ключа используется соответствующийstd::lessпредикат. Альтернативные сравнительные предикаты могут быть определены сcomposite_key_compare:

// phonebook with given names in reverse order
typedef multi_index_container<
  phonebook_entry,
  indexed_by<
    ordered_non_unique<
      composite_key<
        phonebook_entry,
        member<phonebook_entry,std::string,&phonebook_entry::family_name>,
        member<phonebook_entry,std::string,&phonebook_entry::given_name>
      >,
      composite_key_compare<
        std::less<std::string>,   // family names sorted as by default
        std::greater<std::string> // given names reversed
      >
    >,
    ordered_unique<
      member<phonebook_entry,std::string,&phonebook_entry::phone_number>
    >
  >
> phonebook;

См.пример 7в разделе примеров для примененияcomposite_key.

Composite keys and hashed indices

Композитные ключи также можно использовать с хешированными индексами простым способом:

struct street_entry
{
  // quadrant coordinates
  int x;
  int y;
  std::string name;
  street_entry(int x,int y,const std::string& name):x(x),y(y),name(name){}
};
typedef multi_index_container<
  street_entry,
  indexed_by<
    hashed_non_unique< // indexed by quadrant coordinates
      composite_key<
        street_entry,
        member<street_entry,int,&street_entry::x>,
        member<street_entry,int,&street_entry::y>
      >
    >,
    hashed_non_unique< // indexed by street name
      member<street_entry,std::string,&street_entry::name>
    >
  >
> street_locator;
street_locator sl;
...
void streets_in_quadrant(int x,int y)
{
  std::pair<street_locator::iterator,street_locator::iterator> p=
    sl.equal_range(std::make_tuple(x,y));
  while(p.first!=p.second){
    std::cout<<p.first->name<<std::endl;
    ++p.first;
  }
}

Обратите внимание, что хеширование автоматически обрабатывается:бустер::hashспециализируется на хешировании композитного ключа в качестве функции бустера::hashзначения его элементов. Если нам нужно указать различные хеш-функции для элементов композитного ключа, мы можем явно сделать это, используя утилитуcomposite_key_hash:

struct tuned_int_hash
{
  int operator()(int x)const
  {
    // specially tuned hash for this application
  }
};
typedef multi_index_container<
  street_entry,
  indexed_by<
    hashed_non_unique< // indexed by quadrant coordinates
      composite_key<
        street_entry,
        member<street_entry,int,&street_entry::x>,
        member<street_entry,int,&street_entry::y>
      >,
      composite_key_hash<
        tuned_int_hash,
        tuned_int_hash
      >
    >,
    hashed_non_unique< // indexed by street name
      member<street_entry,std::string,&street_entry::name>
    >
  >
> street_locator;

Кроме того, равенство композитных ключей можно настроить с помощьюcomposite_key_equal_to, хотя в большинстве случаев предикат равенства по умолчанию (исходя изstd::equal_toдля типов элементов) будет правильным выбором.

В отличие от упорядоченных индексов, мы не можем выполнять частичные поиски, определяющие только первые элементы составного ключа:

// try to locate streets in quadrants with x==0
// compile-time error: hashed indices do not allow such operations
std::pair<street_locator::iterator,street_locator::iterator> p=
  sl.equal_range(std::make_tuple(0));

Причина этого ограничения вполне логична: поскольку хеш-значение композитного ключа зависит от всех его элементов, вычислить его из частичной информации невозможно.

Advanced features of Boost.MultiIndex key extractors

КонцепцияKey Extractorпозволяет одному и тому же объекту извлекать ключи из нескольких различных типов, возможно, через соответствующим образом определенные перегрузки оператора:

// example of a name extractor from employee and employee *
struct name_extractor
{
  typedef std::string result_type;
  const result_type& operator()(const employee& e)const{return e.name;}
  result_type&       operator()(employee* e)const{return e->name;}
};
// name_extractor can handle elements of type employee...
typedef multi_index_container<
  employee,
  indexed_by<
    ordered_unique<name_extractor>
  >
> employee_set;
// ...as well as elements of type employee *
typedef multi_index_container<
  employee*,
  indexed_by<
    ordered_unique<name_extractor>
  >
> employee_ptr_set;

Эта возможность полностью используется предопределенными ключевыми экстракторами, предоставляемыми Boost. MultiIndex упрощает определениеmulti_index_containers, где элементы являются указателями или ссылками на фактические объекты. Далее указываетсяmulti_index_containerуказателей на сотрудников, отсортированных по их именам.

typedef multi_index_container<
  employee *,
  indexed_by<
    ordered_non_unique<member<employee,std::string,&employee::name> > >
> employee_set;

Обратите внимание, что это указано точно так же, какmulti_index_containerфактическогосотрудникаобъектов:члензаботится о дополнительной отсылке, необходимой для получения доступа ксотруднику::имя. Аналогичная функциональность предусмотрена для взаимодействия со справочными обертками отBoost.Ref:

typedef multi_index_container<
  boost::reference_wrapper<const employee>,
  indexed_by<
    ordered_non_unique<member<employee,std::string,&employee::name> > >
> employee_set;

На самом деле, поддержка указателей дополнительно расширена, чтобы принять то, что мы называемцепными указателями. Такой цепной указатель определяется индукцией как сырой или умный указатель или итератор к фактическому элементу, к обертке отсчета элемента илик другому цепному указателю; то есть, цепные указатели являются произвольными композициями указательных типов, в конечном счете относящимися к элементу, из которого должен быть извлечен ключ. Примерами цепных указателей насотрудникаявляются:

  • сотрудника *,
  • ,
  • std::unique_ptr,
  • std::list>:::iterator,
  • сотрудника **,
  • boost::shared_ptr
. В общем, цепные указатели с расстоянием отсчета более 1 вряд ли будут использоваться в обычной программе, но они могут возникать в рамках, которые строят «просмотры» какmulti_index_containers из ранее существовавшегоmulti_index_containers.employee *,
  • const employee *,
  • std::unique_ptr<employee>,
  • std::list<boost::reference_wrapper<employee>>::iterator,
  • employee **,
  • boost::shared_ptr<const employee *>.
  • In general, chained pointers with dereferencing distance greater than 1 are not likely to be used in a normal program, but they can arise in frameworks which construct "views" as multi_index_containers from preexisting multi_index_containers. [ORIG_END] -->

    Для того, чтобы представить краткое изложение различных применений Boost. Ключевые экстракторы MultiIndex при наличии эталонных оберток и указателей учитывают следующий окончательный тип:

    struct T
    {
      int        i;
      const int  j;
      int        f()const;
      int        g();
      static int gf(const T&);
      static int gg(T&);
    };
    

    В таблице ниже перечислены соответствующие ключевые экстракторы, которые будут использоваться для различных типов указателей и эталонных оберток на основеTдля каждого из его членов.

    Use cases for Boost.MultiIndex key extractors.
    element type  key  key extractor applicable to
    const elements?
    read/write?
    T i Member yes yes
    j Member yes no
    f() const_mem_fun yes no
    g() mem_fun no no
    gf() global_fun yes no
    gg() global_fun no no
    reference_wrapper<T> i Member yes yes
    j Member yes no
    f() const_mem_fun yes no
    g() mem_fun yes no
    gf() global_fun yes no
    gg() global_fun yes no
    reference_wrapper<const T> i Member yes no
    j Member yes no
    f() const_mem_fun yes no
    g()  
    gf() global_fun yes no
    gg()  
    chained pointer to T
    or to reference_wrapper<T>
    i Member yes yes
    j Member yes no
    f() const_mem_fun yes no
    g() mem_fun yes no
    gf() global_fun yes no
    gg() global_fun yes no
    chained pointer to const T
    or to reference_wrapper<const T>
    i Member yes no
    j Member yes no
    f() const_mem_fun yes no
    g()  
    gf() global_fun yes no
    gg()  

    В колонке "применимо к элементамconst?" указывается, можно ли использовать соответствующий ключ-экстрактор при пропускании постоянных элементов (это относится к элементам, указанным в первой колонке, а не к упомянутым объектамT). Единственные отрицательные случаи относятся кT::gиT:gg, когда элементы являются сырымиTобъектами, которые имеют смысл, поскольку мы имеем дело с непостоянной функцией членаT::gи функцией, принимающейTнепостоянной ссылкой: это также подразумевает, чтоmulti_index_containers элементовT::gилиT::gg, поскольку элементы, содержащиеся вmulti_index_container, рассматриваются как постоянные.

    В колонке "read/write?" показано, какие комбинации даютэкстракторы ключей чтения/записи.

    Некоторая осторожность должна быть предпринята, чтобы сохранитьconst-правильность в спецификациичленаключевых экстракторов: в некотором смысле,constквалификатор переносится на членскую часть, даже если этот конкретный член не определен какconst. Например, если элементы имеют типconst T *, сортировка поT::iявляетсяне, указанным какчлен, а скорее какчлен.

    Для практических демонстраций использования этих ключевых экстракторов обратитесь кпримеру 2ипримеру 6в разделе примеров.




    Пересмотрено 25 марта 2015

    © Copyright 2003-2015 Joaquín M López Muñoz. Распространяется под лицензией Boost Software License, версия 1.0. (См. сопроводительный файлLICENSE_1_0.txtили копию на) http://www.boost.org/LICENSE_1_0.txt

    Статья Boost.MultiIndex Documentation - Tutorial - Key extraction раздела Boost.MultiIndex Documentation - Tutorial может быть полезна для разработчиков на c++ и boost.




    Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



    :: Главная :: Boost.MultiIndex Documentation - Tutorial ::


    реклама


    ©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
    Top.Mail.Ru

    Время компиляции файла: 2024-08-30 11:47:00
    2025-05-20 02:14:37/0.011533975601196/1