Подтвердить что ты не робот

Двоичная сериализация std:: bitset

std::bitset имеет метод to_string() для сериализации в качестве строки char на основе 1 и 0 s. Очевидно, что для каждого бита в битете используется один 8 бит char, что делает сериализованное представление в 8 раз дольше, чем необходимо.
Я хочу сохранить битовый набор в двоичном представлении, чтобы сэкономить место. Метод to_ulong() имеет значение только тогда, когда в моем битете есть меньше 32 бит. У меня сотни.
Я не уверен, что хочу использовать memcpy()/std::copy() для самого объекта (адреса), поскольку это предполагает, что объект является POD.

API, похоже, не предоставляет дескриптор представления внутреннего массива, из которого я мог бы принять адрес.

Мне также нужна опция десериализации битового набора из двоичного представления.

Как я могу это сделать?

4b9b3361

Ответ 1

Это возможный подход, основанный на явном создании std::vector<unsigned char> путем чтения/записи одного бита за раз...

template<size_t N>
std::vector<unsigned char> bitset_to_bytes(const std::bitset<N>& bs)
{
    std::vector<unsigned char> result((N + 7) >> 3);
    for (int j=0; j<int(N); j++)
        result[j>>3] |= (bs[j] << (j & 7));
    return result;
}

template<size_t N>
std::bitset<N> bitset_from_bytes(const std::vector<unsigned char>& buf)
{
    assert(buf.size() == ((N + 7) >> 3));
    std::bitset<N> result;
    for (int j=0; j<int(N); j++)
        result[j] = ((buf[j>>3] >> (j & 7)) & 1);
    return result;
}

Обратите внимание, что для вызова функции шаблона де-сериализации bitset_from_bytes в вызове функции должен указываться размер бита N, например

std::bitset<N> bs1;
...
std::vector<unsigned char> buffer = bitset_to_bytes(bs1);
...
std::bitset<N> bs2 = bitset_from_bytes<N>(buffer);

Если вы действительно заботитесь о скорости, то одно из решений, которое могло бы получить что-то, было бы разворачивать цикл, чтобы упаковка выполнялась, например, по одному байту за раз, но еще лучше просто написать собственную реализацию битового набора, которая не скрыть внутреннее двоичное представление вместо std::bitset.

Ответ 2

Как предложили парни на gamedev.net, можно попробовать использовать boost:: dynamic_bitset, поскольку он позволяет доступ к внутреннему представлению битпаков.

Ответ 3

изменить: Следующие действия не будут работать должным образом. По-видимому, "двоичный формат" на самом деле означает "ASCII-представление двоичного файла".


Вы должны записать их в std::ostream с помощью operator<<. Здесь говорится здесь:

[Bitsets] также может быть непосредственно вставлен и извлечен из потоков в двоичном формате.

Ответ 4

Отвечая на мой вопрос о полноте.

По-видимому, нет простого и портативного способа сделать это.

Для простоты (хотя и не эффективности) я закончил использование to_string, а затем создав последовательные 32-битные биты из всех 32-битных фрагментов строки (и остальной *) и используя to_ulong для каждого из них собирать биты в двоичный буфер.
Этот подход оставляет бит-twiddling для самого STL, хотя это, вероятно, не самый эффективный способ сделать это.

* Обратите внимание, что, поскольку std::bitset является шаблоном для общего количества бит, остаточный битсет должен использовать некоторую простую метаметрическую арифметику шаблона.

Ответ 5

Я не вижу очевидного способа, кроме преобразования в строку и выполнения вашей собственной сериализации строки, которая группирует фрагменты из 8 символов в один последовательный байт.

EDIT: лучше всего перебирать все биты с помощью operator[] и вручную сериализовать его.

Ответ 6

это может помочь вам, это маленький пример различных типов сериализации. Я добавил битовые и необработанные битовые значения, которые можно использовать, как показано ниже.

(все примеры на https://github.com/goblinhack/simple-c-plus-plus-serializer)

class BitsetClass {
public:
    std::bitset<1> a;
    std::bitset<2> b;
    std::bitset<3> c;

    unsigned int d:1; // need c++20 for default initializers for bitfields
    unsigned int e:2;
    unsigned int f:3;
    BitsetClass(void) { d = 0; e = 0; f = 0; }

    friend std::ostream& operator<<(std::ostream &out,
                                    Bits<const class BitsetClass & > const m
    {
        out << bits(my.t.a);
        out << bits(my.t.b);
        out << bits(my.t.c);

        std::bitset<6> s(my.t.d | my.t.e << 1 | my.t.f << 3);
        out << bits(s);

        return (out);
    }

    friend std::istream& operator>>(std::istream &in,
                                    Bits<class BitsetClass &> my)
    {
        std::bitset<1> a;
        in >> bits(a);
        my.t.a = a;

        in >> bits(my.t.b);
        in >> bits(my.t.c);
        std::bitset<6> s;
        in >> bits(s);

        unsigned long raw_bits = static_cast<unsigned long>(s.to_ulong());
        my.t.d = raw_bits & 0b000001;
        my.t.e = (raw_bits & 0b000110) >> 1;
        my.t.f = (raw_bits & 0b111000) >> 3;

        return (in);
    }
};