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

Implementation

Boost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 8. Boost.Circular Buffer

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

PrevUpHomeNext

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

Thread-Safety

Безопасность потока<circular_buffer>такая же, как и безопасность потока контейнеров в большинстве реализаций STL. Это означает, что<circular_buffer>не является полностью безопасным. Безопасность потока гарантируется только в том смысле, что одновременный доступ к отдельным экземплярам<circular_buffer>безопасен, а одновременный доступ к совместному<circular_buffer>безопасен.

Если несколько потоков получают доступ к одному<circular_buffer>, и, по меньшей мере, один из потоков может потенциально записывать, то пользователь несет ответственность за обеспечение взаимного исключения между потоками во время доступа к контейнеру. Взаимное исключение между нитями может быть достигнуто за счет операций обертывания<circular_buffer>с приобретением и выпуском замка. (См. примерный код Bounded Buffer по адресуcircular_buffer_bound_example.cpp)

Overwrite Operation

Операция перезаписи происходит, когда элемент вставляется в полный<circular_buffer>— старый элемент перезаписывается новым. Была дискуссия о том, что именно означает «перезапись элемента» во время формального обзора. Это может быть либо разрушение исходного элемента и последующее строительство нового элемента, либо присвоение нового элемента старому.<circular_buffer>выполняет задание, потому что оно более эффективно.

С точки зрения бизнес-логики хранимого элемента операция разрушения / строительства и назначение обычно означают одно и то же. Однако в очень редких случаях (если таковые имеются) они могут отличаться. Если существует требование о том, чтобы элементы разрушались/конструировались вместо назначения, рассмотрите возможность реализации обертки элемента, которая будет реализовывать оператора назначения, и вместо этого храните обертки. Необходимо отметить, что хранение таких оберток имеет недостаток. Разрушение/конструкция будет вызываться при каждом назначении обертки - не только когда обертка перезаписывается (когда буфер заполнен), но и когда хранимые обертки смещаются (например, в результате вставки в середину контейнера).

Writing to a Full Buffer

Существует несколько вариантов, как справиться, если источник данных производит больше данных, чем может поместиться в буфер фиксированного размера:

  • Сообщите источнику данных подождать, пока в буфере не появится место (например, путем исключения переполнения).
  • Если самые старые данные являются наиболее важными, игнорируйте новые данные из источника, пока в буфере снова не появится место.
  • Если последние данные являются наиболее важными, запишите самые старые данные.
  • Пусть производитель несет ответственность за проверку размера буфера перед его записью.

Очевидно, что<circular_buffer>реализует третий вариант. Но менее очевидно, что он не реализует никакой другой вариант, особенно первые два. Может сложиться впечатление, что<circular_buffer>должны реализовать первые три варианта и предложить механизм выбора среди них. Это впечатление неправильное.

<circular_buffer>был разработан и оптимизирован для круговой записи (что означает перезапись самых старых данных при заполнении). Если бы такой механизм управления был включен, это только усложнило бы ситуацию, и использование<circular_buffer>было бы, вероятно, менее простым.

Кроме того, первые два варианта (а также четвертый вариант) не требуют, чтобы буфер был круговым. Если есть необходимость в первом или втором варианте, рассмотрите возможность реализации адаптера, например, std::vector. В этом случае<circular_buffer>не подходит для адаптации, потому что, в отличие от std::vector, он несет накладные расходы за его круговое поведение.

Reading/Removing from an Empty Buffer

При считывании или удалении элемента из пустого буфера буфер должен быть в состоянии уведомить потребителя данных (например, путем исключения из потока данных), что в нем не хранятся элементы.<circular_buffer>не осуществляет такое поведение по двум причинам:

  • Это приведет к повышению производительности.
  • Ни один другой контейнер не реализует его таким образом.

Считается ошибкой читать или удалять элемент (например, позвонив<front()>или<pop_back()>) из пустого контейнера и из пустого<circular_buffer>. Потребитель данных должен проверить, не является ли контейнер пустым, прежде чем считывать/удалять его путем тестирования<empty()>. Однако при чтении из<circular_buffer>есть возможность полагаться на метод<at()>, который бросает исключение, когда индекс находится вне диапазона.

Iterator Invalidation

Итератор обычно считается недействительным, если элемент, на который указывает итератор, был удален или перезаписан другим элементом. Это определение обеспечивается поддержкой отладки и документируется для каждого метода. Однако некоторые приложения, использующие<circular_buffer>, могут требовать менее строгого определения: итератор недействителен только в том случае, если он указывает на неинициализированную память.

Рассмотрим следующий пример:

#define BOOST_CB_ENABLE_DEBUG 0 // The Debug Support has to be disabled, otherwise the code produces a runtime error.
#include <boost/circular_buffer.hpp>
#include <boost/assert.hpp>
#include <assert.h>
int main(int /*argc*/, char* /*argv*/[])
{
  boost::circular_buffer<int> cb(3);
  cb.push_back(1);
  cb.push_back(2);
  cb.push_back(3);
  boost::circular_buffer<int>::iterator it = cb.begin();
  assert(*it == 1);
  cb.push_back(4);
  assert(*it == 4); // The iterator still points to the initialized memory.
  return 0;
}

Итератор больше не указывает на исходный элемент (и считается недействительным с «строгой» точки зрения), но он по-прежнему указывает на то же действительное место в памяти. Это «мягкое» определение недействительности итератора поддерживается<circular_buffer>, но должно рассматриваться как деталь реализации, а не как полноценная функция. Правила, когда итератор все еще действителен, могут быть выведены из кода вsoft_iterator_invalidation.cpp.

Move emulation and rvalues

Начиная с Boost 1.54.0 поддержка семантики движения была реализована с помощьюBoost. Перейдитев библиотеку. Если ссылки на значение r доступны<circular_buffer>, они будут использоваться, но если нет, то используется близкая, но несовершенная эмуляция. На таких компиляторах:

  • Некопируемые объекты могут храниться в контейнерах. Они могут быть построены на месте с использованием<emplace>или если они поддерживают Boost. Двигайся, двигайся.
  • Сами контейнеры не являются подвижными.
  • Пересылка аргументов не идеальна.

<circular_buffer>будет использовать значения r и эмуляции перемещения для типов значений только в том случае, если конструктор перемещения и оператор назначения перемещения типа значения не бросают; или если тип значения не имеет конструктора копии.

Некоторые методы не будут использовать конструктор движения для типа значения вообще, если конструктор бросает. Это необходимо для согласованности данных и избежания ситуаций, когда в афтерере исключение<circular_buffer>содержит отошедшие объекты вместе с хорошими.

См. документацию для<is_copy_constructible>,<is_nothrow_move_assignable>и<is_nothrow_move_constructible>типов триат. Там вы найдете информацию о том, как сделать конструктор класса, и как сделать некопируемый класс в C++03 и C++98.

Производительность<circular_buffer>значительно улучшится, если тип значения не имеет ничего, кроме конструктора хода и назначения хода.

Exceptions of move_if_noexcept(T&)

Справочная документация<circular_buffer>содержит примечания типа «Броски: см. Исключения<move_if_noexcept(T&)>». Это примечание означает следующее:<move_if_noexcept(T& value)>вообще не делает исключений, но оно возвращает<value>в качестве ссылки на значение r, только если класс<T>имеет конструктор ходов и оператор назначения ходов; или если у него нет конструктора копий. В противном случае<move_if_noexcept(T& value)>возвращается<value>в качестве ссылки.

Это приводит нас к следующей ситуации:

  • Если<value>имеет не иначе как конструктор хода и не иначе как оператор назначения хода, то никакие исключения не будут брошены вообще.
  • Если<value>имеет конструктор метательных движений и некоторый конструктор копий, то метод может выбрасывать исключения из конструктора копий.
  • Если<value>не имеет конструктора копий, то метод может выбросить исключения из конструктора движений.

<move_if_noexcept(T&)>используетBoost.Move,<is_copy_constructible>,<is_nothrow_move_assignable>и<is_nothrow_move_constructible>тип триат.

Caveats

<circular_buffer>не следует использовать для хранения указателей на динамически выделяемые объекты. Когда кольцевой буфер становится полным, дальнейшая вставка перезаписывает сохраненные указатели, что приводит к утечке памяти. Одним из рекомендуемых альтернатив является использование интеллектуальных указателей, напримерУсильте интеллектуальные указатели.

std::auto_ptr

[Caution] Caution

Особенно опасным считается любой контейнер<std::auto_ptr>.

[Tip] Tip

Никогда не создавайте круговой буфер<std::auto_ptr>. См. превосходную книгу Скотта Мейерса «Эффективный STL» для подробного обсуждения. (Мейерс С., Эффективный STL: 50 конкретных способов улучшить использование стандартной библиотеки шаблонов). Эддисон-Уэсли, 2001.

В то время как внутренние части<circular_buffer>являются круговыми,итераторы не являются. Итераторы<circular_buffer>действительны только для диапазона<\[begin(),end()\]>, поэтому, например: итераторы<(begin()-1)>и<(end()+ 1)>недействительны.

Debug Support

Чтобы помочь программисту избежать и найти распространенные ошибки, можно включить<circular_buffer>, чтобы обеспечить своего рода поддержку отладки.

Когда функция отладки включена,<circular_buffer>поддерживает список действительных итераторов. Как только какой-либо элемент будет уничтожен, все итераторы, указывающие на этот элемент, будут удалены из этого списка и явно признаны недействительными. Поддержка отладки также состоит из множества утверждений<BOOST_ASSERT>макросов, которые обеспечивают правильное использование<circular_buffer>и его итераторов во время выполнения. В случае использования недействительного итератора утверждение сообщит об ошибке. Связь явной итерации и утверждений делает очень надежный метод отладки, который улавливает большинство ошибок.

Кроме того, неинициализированная память, выделенная<circular_buffer>, заполнена значением<0xcc>в режиме отладки. При отладке кода это может помочь программисту распознать инициализированную память из неинициализированной. Подробно см. исходный кодcircular_buffer/debug.hpp.

[Caution] Caution

Поскольку код отладки делает<circular_buffer>и его итераторы более взаимосвязанными, гарантии безопасности потоков<circular_buffer>отличаются при включенной поддержке отладки. В дополнение к самому контейнеру все итераторы, отслеживаемые контейнером (включая любые его копии), должны быть защищены от одновременного доступа. В частности, это включает в себя копирование, уничтожение или получение итераторов из контейнера, даже если для доступа только для чтения.

Поддержка отладки отключена по умолчанию. Чтобы включить его, необходимо определить<BOOST_CB_ENABLE_DEBUG>макрос со значением 1 при компиляции кода с использованием<circular_buffer>.

Compatibility with Interprocess library

<circular_buffer>совместим с библиотекойBoost.Interprocess, используемой для межпроцессной связи. Учитывая, что поддержка отладки circular_buffer основана на «сырых» указателях (что не допускается библиотекой Interprocess), код должен быть составлен с отключенной поддержкой отладки (т.е. с макросом<BOOST_CB_ENABLE_DEBUG>, не определенным или не определенным до 0). Если этого не сделать, компиляция потерпит неудачу.


PrevUpHomeNext

Статья Implementation раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 8. Boost.Circular Buffer может быть полезна для разработчиков на c++ и boost.




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



:: Главная :: Chapter 8. Boost.Circular Buffer ::


реклама


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

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