Является ли хорошей идеей основывать несобственный битовый контейнер на std :: vector & lt; bool & gt ;? станд :: пролет?

В нескольких моих проектах мне все чаще приходилось работать с последовательными последовательностями битов в памяти — эффективно (*). До сих пор я написал набор встроенных автономных функций, настроенных на выбор типа «битовый контейнер» (например, uint32_t), для получения и установки битов, применения ‘или’ и ‘и’ к их значениям, определения местоположения контейнера, преобразования длин в битах в размеры в байтах или длины в контейнерах и т. д. … похоже, что это время написания классов ,

Я знаю, что стандартная библиотека C ++ имеет специализацию std::vector<bool>, который, как полагают многие, является недостатком проекта — поскольку его итераторы не раскрывают фактические bools, а скорее прокси-объекты. Является ли это хорошей идеей или плохой для специализации, это определенно то, что я рассматриваю — явный битовый прокси-класс, который, мы надеемся, «всегда» будет оптимизирован (с хорошей смазкой constexpr, noexcept а также inline). Итак, я думал о возможной адаптации std::vector код из одной стандартной реализации библиотеки.

С другой стороны, мой предполагаемый класс:

  • Никогда не будет владеть данными / битами — он получит адрес контейнера начальных битов (при условии выравнивания) и длину в битах и ​​не будет выделяться или освобождаться.
  • Он не сможет изменить размер данных динамически или иным образом — даже при сохранении того же объема пространства, что и std :: vector :: resize (); его длина будет фиксирована в течение срока службы / объема.
  • Он ничего не должен знать о куче (и работать, когда нет кучи)

В этом смысле это больше похоже на класс span для битов. Так может тогда начать с пролета? Я не знаю, промежутки все еще не стандартны; и в пролетах нет прокси …

Так что было бы хорошей основой (редактировать: НЕ базовый класс) для моей реализации? std::vector<bool>? std::span? И то и другое? Никто? Или — может быть, я заново изобретаю колесо, и это уже решенная проблема?

Заметки:

  • Длина битовой последовательности известна во время выполнения, а не во время компиляции; в противном случае, как предполагает @SomeProgrammerDude, я мог бы использовать std::bitset,
  • Моему классу не нужен диапазон «be-a» или вектор «be-a», поэтому я не собираюсь специализироваться ни на одном из них.

(*) — Пока что не SIMD-эффективно, но это может произойти позже. Кроме того, это может быть использовано в коде CUDA, где мы не используем SIMDize, а притворяемся, что дорожки являются правильными потоками.

1

Решение

Скорее, чем std::vector или же std::span Я подозреваю, что реализация вашего класса будет иметь больше общего с std::bitset, поскольку это почти то же самое, за исключением (фиксированного) размера, определенного во время выполнения.

На самом деле, вы могли бы взять типичный std::bitset реализация и переместить <size_t N> Параметр шаблона в класс как size_t size_ член (или любое другое имя), и у вас будет класс динамического набора битов почти без изменений. Вы можете захотеть избавиться от всего, что вы считаете грубым, как от конструкторов, std::string и друзья.

Затем последним шагом будет удаление владельца базовых данных: в основном вы удалите создание базового массива в конструкторе и сохраните представление существующего массива с некоторыми указателями.

Если ваши клиенты не согласны с тем, какой базовый целочисленный тип без знака использовать для хранения (то, что вы называете «битовым контейнером»), вам также может понадобиться сделать свой класс шаблоном для этого типа, хотя было бы проще, если бы все согласились сказать uint64_t,

Так далеко как std::vector<bool> идет, вам не нужно много от этого: все, что vector ты этого хочешь, std::bitset наверное тоже так: главное что vector добавляет, это динамичный рост — но вы сказали, что не хотите этого. vector<bool> имеет концепцию прокси-объекта для представления одного бита, но так же std::bitset,

От std::span Вы берете идею отсутствия прав собственности на базовые данные, но я не думаю, что на самом деле это представляет собой большой объем базового кода. Вы можете рассмотреть std::span подход иметь или известный во время компиляции размер или же предоставленный во время выполнения размер (обозначенный Extent == std::dynamic_extent) если это будет полезно для вас (в основном, если вы иногда используете размеры во время компиляции и можете специализировать некоторые методы, чтобы быть более эффективными в этом случае).

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]