вторник, 26 ноября 2024 г.

[prog.c++] Чего мне не хватило в boost::dynamic_bitset

Довелось немного попользоваться boost::dynamic_bitset. В принципе, отличная штука. И написана так, что можно заглянуть в потроха и понять что к чему. В общем, авторам решпект и уважуха.

Но я не был бы настоящим велосипедостроителем, если бы не отметил несколько моментов, которые могли бы упростить жизнь пользователю dynamic_bitset в моем сценарии.

Disclaimer: возможно, что-то из описанного ниже на самом деле в dynamic_bitset есть, просто я этого, несмотря на штудирование документации, не нашел 🙁 В таком случае прошу понять и простить... 😎

Во-первых, очень утомляет то, что для операций test и set нужно гарантировать, что индекс бита меньше текущего размера множества. Для меня это оказалось неудобно. Мне нужно фиксировать в bitset-е обработанные целочисленные индексы в диапазоне от нуля до N. Не всегда они идут последовательно, да и само N изначально неизвестно. Т.е. сперва мне нужно обработать, например, индекс 42, затем 24, затем 1, затем 100500.

Приходится перед вызовом set проверять, а есть ли i в bitset-е. Если нет, то сперва нужно сделать resize, а лишь затем вызывать set.

Аналогично, перед вызовом test нужно проверить, что i меньше size и только затем вызывать test.

В общем, неудобно. Было бы лучше, раз уж у нас dynamic_bitset, чтобы индексы больше или равные size обрабатывались должным образом без участия пользователя:

  • для set чтобы происходило автоматическое расширение множества;
  • для test чтобы возвращался false (ведь бита все равно нет, значит он точно не выставлен в единицу).

Во-вторых, операции AND и OR требуют, чтобы оба операнда были одинакового размера. Что неудобно, когда у меня оказываются два множества разного размера, а я хочу либо слить их в одно, либо вычесть одно из другого.

Из-за того, что операции AND/OR могут применяться только к множествам одного размера, приходится вручную разбираться с тем, какой кусок откуда вырезать, чтобы получить допустимые операнды.

Как по мне, было бы гораздо удобнее, если бы operator& сам бы определил результирующий размер итогового множества и выполнил AND только для фрагментов этого размера. Например, если мы делаем L & R, где L больше R, то имеет смысл выполнять AND только для первых R.size элементов из L.

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

В-третьих, идет в догонку к "во-вторых". Иногда нужно понять, а есть ли непустое пересечение между L и R. Т.е. мне не нужен точный результат пересечения, нужно просто понимать пусто ли такое пересечение или нет. Но если L и R имеют разные размер, то просто так я этого не узнаю. Мне сперва нужно получить два множества одинакового размера, потом уже для них делать intersect. Т.е. мне требуется создать новый dynamic_bitset, который нужен только для выполнения одного intersect-а и все.

Было бы круто, если бы был какой-то dynamic_bitset_view, похожий на std::string_view. Чтобы я мог в этот dynamic_bitset_view поместить только часть исходного dynamic_bitset-а. Причем чтобы при этом dynamic_bitset_view оставался бы легковесным объектом, который бы ничего никуда бы не копировал.

И тогда intersect вообще можно было бы делать не с исходными множествами L и R, а с их дешевыми фрагментами в виде dynamic_bitset_view.

Комментариев нет: