В нескольких моих проектах мне все чаще приходилось работать с последовательными последовательностями битов в памяти — эффективно (*). До сих пор я написал набор встроенных автономных функций, настроенных на выбор типа «битовый контейнер» (например, uint32_t
), для получения и установки битов, применения ‘или’ и ‘и’ к их значениям, определения местоположения контейнера, преобразования длин в битах в размеры в байтах или длины в контейнерах и т. д. … похоже, что это время написания классов ,
Я знаю, что стандартная библиотека C ++ имеет специализацию std::vector<bool>
, который, как полагают многие, является недостатком проекта — поскольку его итераторы не раскрывают фактические bool
s, а скорее прокси-объекты. Является ли это хорошей идеей или плохой для специализации, это определенно то, что я рассматриваю — явный битовый прокси-класс, который, мы надеемся, «всегда» будет оптимизирован (с хорошей смазкой constexpr
, noexcept
а также inline
). Итак, я думал о возможной адаптации std::vector
код из одной стандартной реализации библиотеки.
С другой стороны, мой предполагаемый класс:
В этом смысле это больше похоже на класс span для битов. Так может тогда начать с пролета? Я не знаю, промежутки все еще не стандартны; и в пролетах нет прокси …
Так что было бы хорошей основой (редактировать: НЕ базовый класс) для моей реализации? std::vector<bool>
? std::span
? И то и другое? Никто? Или — может быть, я заново изобретаю колесо, и это уже решенная проблема?
Заметки:
std::bitset
,(*) — Пока что не SIMD-эффективно, но это может произойти позже. Кроме того, это может быть использовано в коде CUDA, где мы не используем SIMDize, а притворяемся, что дорожки являются правильными потоками.
Скорее, чем 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
) если это будет полезно для вас (в основном, если вы иногда используете размеры во время компиляции и можете специализировать некоторые методы, чтобы быть более эффективными в этом случае).
Других решений пока нет …