Безопасно ли удалять постоянство результата stringstream при выполнении двоичной сериализации? - PullRequest
1 голос
/ 11 августа 2011

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

int SerializeToBuffer(unsigned char* buffer)
{
    stringstream ss;
    vector<Serializable> items = GetSerializables();
    string serializedItem("");
    short len = 0;
    for(int i = 0; i < items.size(); ++i)
    {
        serializedItem = items[i].Serialize();
        len = serializedItem.length();

        // Write the bytes to the stream
        ss.write(*(char*)&(len), 2);
        ss.write(serializedItem.c_str(), len);

    }
    buffer = reinterpret_cast<unsigned char*>(
                const_cast<char*>(ss.str().c_str()));
    return items.size();
}

Безопасно ли удалять const -Ness из ss.str().c_str(), а затем reinterpret_cast результат как unsigned char*, затем назначить его в буфер?

Примечание: код только для того, чтобы дать вам представление о том, что я делаю, он нене обязательно компилировать.

Ответы [ 5 ]

3 голосов
/ 11 августа 2011

Отсутствие удаления константы изначально присущей строки приведет к Неопределенное поведение .

const char * c_str () const;
Получить строковый эквивалент C

Генерирует завершенную нулем последовательность символов (c-string) с тем же содержимым, что и у строкового объекта, и возвращает его в качестве указателя на массив символов.
Завершающий нулевой символ добавляется автоматически.
Возвращенный массив указывает на внутреннее местоположение с требуемым пространством хранения для этой последовательности символов плюс его завершающий нулевой символ, , но значения в этом массиве не должныбыть измененным в программе и гарантированно останется неизменным до следующего вызова функции-константы неконстантного строкового объекта.

1 голос
/ 12 августа 2011

Поскольку кажется, что вашим основным потребителем этой функции является приложение на C #, хорошая подпись для C # - хорошее начало.Вот что я бы сделал, если бы я был действительно ограничен во времени и не успел сделать что-то «Правильный путь»; -]

using System::Runtime::InteropServices::OutAttribute;

void SerializeToBuffer([Out] array<unsigned char>^% buffer)
{
    using System::Runtime::InteropServices::Marshal;

    vector<Serializable> const& items = GetSerializables();
    // or, if Serializable::Serialize() is non-const (which it shouldn't be)
    //vector<Serializable> items = GetSerializables();

    ostringstream ss(ios_base::binary);
    for (size_t i = 0u; i != items.size(); ++i)
    {
        string const& serializedItem = items[i].Serialize();
        unsigned short const len =
            static_cast<unsigned short>(serializedItem.size());

        ss.write(reinterpret_cast<char const*>(&len), sizeof(unsigned short));
        ss.write(serializedItem.data(), len);
    }

    string const& s = ss.str();
    buffer = gcnew array<unsigned char>(static_cast<int>(s.size()));
    Marshal::Copy(
        IntPtr(const_cast<char*>(s.data())),
        buffer,
        0,
        buffer->Length
    );
}

Для кода C # это будет иметь подпись:

void SerializeToBuffer(out byte[] buffer);
1 голос
/ 11 августа 2011

Это небезопасно, отчасти потому, что вы удаляете const, но что более важно, потому что вы возвращаете указатель на массив, который будет возвращен при возврате функции.

Когда вы пишете

ss.str().c_str()

Возвращаемое значение c_str() действительно только до тех пор, пока существует объект string, для которого вы его вызвали.Подпись stringstream::str() является

string stringstream::str() const;

. Это означает, что она возвращает временный string объект.Следовательно, как только строка

ss.str().c_str()

завершает выполнение, временный объект string восстанавливается.Это означает, что выдающийся указатель, полученный вами через c_str(), больше не действителен, и любое его использование приводит к неопределенному поведению.

Чтобы это исправить, если вы действительно должны вернуть unsigned char*, вынеобходимо вручную скопировать строку в стиле C в собственный буфер:

/* Get a copy of the string that won't be automatically destroyed at the end of a statement. */
string value = ss.str();

/* Extract the C-style string. */
const char* cStr = value.c_str();

/* Allocate a buffer and copy the contents of cStr into it. */
unsigned char* result = new unsigned char[value.length() + 1];
copy(cStr, cStr + value.length() + 1, result);

/* Hand back the result. */
return result;

Кроме того, как указал @Als, удаление const является плохой идеей, если вы планируетеизменение содержимого.Если вы не изменяете содержимое, все должно быть в порядке, но тогда вы должны вернуть const unsigned char* вместо unsigned char*.

Надеюсь, это поможет!

1 голос
/ 11 августа 2011

Короткий ответ: Нет

Длинный ответ: Нет. Вы действительно не можете этого сделать.Внутренний буфер этих объектов принадлежит объектам.Ссылка на внутреннюю структуру определенно запрещена и нарушает инкапсуляцию.В любом случае эти объекты (с их внутренним буфером) будут уничтожены в конце функции, а ваша переменная buffer будет указывать на неинициализированную память.

Использование const_cast<> обычно является признаком того, что что-то в вашем проектеэто неправильно.
Использование reinterpret_cast<> обычно означает, что вы делаете это неправильно (или делаете что-то очень низкое).

Вы, вероятно, хотите написать что-то вроде этого:

std::ostream& operator<<(std::ostream& stream, Data const& serializable)
{
    return stream << serializable.internalData;

    // Or if you want to write binary data to the file:

    stream.write(static_cast<char*>(&serializable.internalData), sizeof(serializable.internalData);
    return stream;

}
0 голосов
/ 11 августа 2011

Вот основная проблема:

buffer = ... ;
return items.size();

Во второй-последней строке вы присваиваете новое значение локальной переменной, которая использовалась (вплоть до этой точки) для хранения указателя вашей функциибыл дан в качестве аргумента.Затем, сразу после этого, вы возвращаетесь из функции, забывая все о переменной, которую вы только что присвоили.Это не имеет смысла!

Что вы, вероятно, хотите сделать, это скопировать данные из памяти, на которую указывает ss_str().c_str(), в память , на которую указывает указатель, хранящийся в buffer,Что-то вроде

memcpy(buffer, ss_str().s_str(), <an appropriate length here>)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...