Python struct.pack эквивалент в C ++ - PullRequest
       48

Python struct.pack эквивалент в C ++

0 голосов
/ 11 октября 2009

Мне нужна строка фиксированной длины из числа, аналогичного struct.pack, присутствующего в python, но в c ++. Я думал о itoa (i,buffer,2), но проблема может заключаться в том, что его длина будет зависеть от платформы. Есть ли способ сделать его независимым от платформы?

Ответы [ 4 ]

3 голосов
/ 11 октября 2009

Если вы ищете полное решение, похожее на пакет Python для struct, вы можете проверить Библиотека буферов протокола Google . Использование этого позаботится о многих проблемах (например, о порядке байтов, переносимости языка, совместимости с различными версиями).

1 голос
/ 12 октября 2009

Вот начало:

typedef std::vector<uint8_t> byte_buffer;

template <std::size_t N>
void append_fixed_width(byte_buffer& buf, uintmax_t val) {
    int shift = ((N - 1) * 8);
    while (shift >= 0) {
        uintmax_t mask = (0xff << shift);
        buf.push_back(uint8_t((val & mask) >> shift));
        shift -= 8;
    }
}

template <typename IntType>
void append_bytes(byte_buffer& buf, IntType val) {
    append_fixed_width<sizeof(IntType)>(buf, uintmax_t(val));
}

int main() { // usage example
     byte_buffer bytes;
     append_bytes(bytes, 1);   // appends sizeof(int) bytes
     append_bytes(bytes, 1ul); // appends sizeof(unsigned long) bytes
     append_bytes(bytes, 'a'); // appends sizeof(int) bytes :p
     append_bytes(bytes, char('a')); // appends 1 byte
     return 0;
}

Append_bytes добавит любой целочисленный тип в байтовый буфер, представленный с помощью std::vector<uint8_t>. Значения упакованы в порядке байтов big endian * . Если вам нужно изменить это, то настройте append_fixed_width, чтобы просмотреть значение в другом порядке.

Эти функции создают необработанный байтовый буфер, поэтому, кто бы ни декодировал его, он должен знать, что там находится. IIRC, это то, что делает struct.pack; другими словами, вызывающий struct.unpack должен предоставить ту же строку формата. Вы можете написать вариант append_fixed_width для упаковки TLV вместо этого:

template <typename TagType, typename ValueType>
void append_tlv(byte_buffer& buf, TagType t, ValueType val) {
    append_fixed_width<sizeof(TagType)>(buf, uintmax_t(t));
    append_fixed_width<sizeof(std::size_t)>(buf, uintmax_t(sizeof(ValueType)));
    append_fixed_width<sizeof(ValueType)>(buf, uintmax_t(val));
}

Я бы серьёзно посмотрел на предложение Джереми . Я хотел бы, чтобы он существовал, когда я написал весь двоичный код упаковки, который у меня есть.

1 голос
/ 11 октября 2009

Вам необходимо определить целочисленный тип с точной шириной через typedef; Вы делаете это в зависимости от платформы. Если вы используете C99, int16_t предопределено в <stdint.h>. Затем вы можете привести к этому типу и ввести в память представление переменной:

int16_t val = (int16_t) orig_val;
void *buf = &val;

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

Если у вас нет C99, вы можете использовать тесты размера во время компиляции или во время выполнения. Для тестов во время компиляции рассмотрите возможность использования autoconf, который уже вычисляет размеры различных примитивных типов, чтобы вы могли выбрать хороший тип во время компиляции. Во время выполнения просто проведите серию тестов sizeof. Обратите внимание, что это несколько не подходит для времени выполнения, так как тест всегда будет давать один и тот же результат. В качестве альтернативы autoconf вы также можете использовать макросы идентификации компилятора / системы для теста во время компиляции.

0 голосов
/ 11 октября 2009

C ++ будет использовать stringstream:

stringstream ss;
int number=/*your number here*/;
ss<<number;

и для получения буфера вы бы использовали ss.str().c_str().

...