Как сбросить std::vector‹bool› в бинарный файл?

Я пишу инструменты для создания дампа и загрузки общих объектов в двоичный файл. В первой быстрой реализации я написал следующий код для std::vector<bool>. Работает, но явно не оптимизирован в памяти.

template <>
void binary_write(std::ofstream& fout, const std::vector<bool>& x)
{
    std::size_t n = x.size();
    fout.write((const char*)&n, sizeof(std::size_t));
    for(std::size_t i = 0; i < n; ++i)
    {
        bool xati = x.at(i);
        binary_write(fout, xati);
    }
}

template <>
void binary_read(std::ifstream& fin, std::vector<bool>& x)
{
    std::size_t n;
    fin.read((char*)&n, sizeof(std::size_t));
    x.resize(n);
    for(std::size_t i = 0; i < n; ++i)
    {
        bool xati;
        binary_read(fin, xati);
        x.at(i) = xati;
    }
}

Как я могу скопировать внутреннюю память std::vector<bool> в свой поток?

Примечание. Я не хочу заменять std::vector<bool> другим.


person Caduchon    schedule 14.04.2015    source источник
comment
Даже если вы уже используете std::vector<bool> в другом месте кода, я настоятельно рекомендую вам перейти к чему-то вроде std::bitset или boost::dynamic_bitset и использовать их функциональность to_string или их ostream перегрузки operator<<.   -  person rubenvb    schedule 14.04.2015
comment
to_string для бинарного хранилища? Действительно ? ^^   -  person Caduchon    schedule 14.04.2015
comment
Право, не самый умный мой комментарий ;). Тем не менее, после изучения функциональности std::bitset это кажется единственным выходом (bitset->string->integer какой-то). Это или извлечение битов один за другим. Мне любопытно, что будет быстрее... Хм, если подумать, просто придерживайтесь std::vector<bool> (см., например, этот вопрос)   -  person rubenvb    schedule 14.04.2015
comment
Сделать данные постоянными — задача сериализатора. Не нужно это делать своими руками.   -  person Klaus    schedule 03.06.2019
comment
@Klaus: написать сериализатор с конкретными потребностями - моя работа. Мне не нужно судить об актуальности вопроса. Мне нужны решения. ;-)   -  person Caduchon    schedule 04.06.2019
comment
в первом комментарии этого ответа, как указал этот пользователь, vector‹bool› не имеет непрерывного хранение логических значений в памяти)   -  person Y00    schedule 15.10.2019


Ответы (2)


Отвечаю на свой вопрос, который в настоящее время признан лучшим ответом, но он может измениться, если кто-то предложит что-то лучше.

Способ сделать это следующий. Требуется доступ к каждому значению, но это работает.

template <>
void binary_write(std::ofstream& fout, const std::vector<bool>& x)
{
    std::vector<bool>::size_type n = x.size();
    fout.write((const char*)&n, sizeof(std::vector<bool>::size_type));
    for(std::vector<bool>::size_type i = 0; i < n;)
    {
        unsigned char aggr = 0;
        for(unsigned char mask = 1; mask > 0 && i < n; ++i, mask <<= 1)
            if(x.at(i))
                aggr |= mask;
        fout.write((const char*)&aggr, sizeof(unsigned char));
    }
}

template <>
void binary_read(std::ifstream& fin, std::vector<bool>& x)
{
    std::vector<bool>::size_type n;
    fin.read((char*)&n, sizeof(std::vector<bool>::size_type));
    x.resize(n);
    for(std::vector<bool>::size_type i = 0; i < n;)
    {
        unsigned char aggr;
        fin.read((char*)&aggr, sizeof(unsigned char));
        for(unsigned char mask = 1; mask > 0 && i < n; ++i, mask <<= 1)
            x.at(i) = aggr & mask;
    }
}
person Caduchon    schedule 14.04.2015
comment
Написание такого размера небезопасно. Также размер вектора‹bool› представляет собой std::vector‹bool›::size_type, который не обязательно совпадает с беззнаковым целым числом. - person Tom Tanner; 18.05.2016
comment
Вы правы, но кажется совершенно невозможным иметь размер существующего вектора, который переполняет unsigned long int, потому что unsigned long int >= void* >= size of the RAM >= size of the vector. В этом конкретном случае подсчитываются биты, тогда количество элементов может быть больше, чем ОЗУ, но ограничение шаблона позволяет считать, что это один и тот же целочисленный тип, используемый для всех векторов. В моем случае size_type — это size_t, который является uint на 64 бита, что совпадает с unsigned long int. Ошибка может возникнуть только для гипотетических компиляторов с действительно огромным вектором логических значений. - person Caduchon; 18.05.2016
comment
Я изменил ответ в соответствии с вашим комментарием. Я ненавижу использовать типы размеров, когда они влияют на код, выходящий за рамки (например, при сопоставлении с данными от пользователя), потому что разработчику становится действительно неясно управлять. Но это не тот случай. - person Caduchon; 18.05.2016

Извините, но ответ: вы не можете сделать это переносимо.

Чтобы сделать это не переносимым, вы можете написать функцию, специфичную для итераторов реализации вашей стандартной библиотеки для vector<bool>.

Если вам повезет, соответствующие поля будут общедоступными внутри итераторов, поэтому вам не нужно менять приватное на общедоступное.

person user541686    schedule 14.04.2015
comment
На самом деле, я могу сделать это переносимо, объединив 8 значений в 1 байт и сохранив этот байт в своем файле. Но я предпочитаю красивое решение. :-) - person Caduchon; 14.04.2015
comment
@Caduchon: Однако вам нужно получить доступ к битам вектора по отдельности. Я хотел сказать, что вы не можете избежать этого. - person user541686; 14.04.2015