python struct.pack在c++中的等效项

tpgth1q7  于 2022-12-20  发布在  Python
关注(0)|答案(5)|浏览(163)

我想从一个数字中得到一个固定长度的字符串,就像python中的struct.pack一样,但是在c++中。我想到了itoa (i,buffer,2),但是问题是它的长度取决于平台。有没有办法让它独立于平台?

mspsb9vt

mspsb9vt1#

如果你正在寻找一个类似于Python的struct包的完整解决方案,你可以看看Google's Protocol Buffers Library,使用它可以解决很多问题(例如字节序、语言可移植性、跨版本兼容性)。

ve7v8dk2

ve7v8dk22#

这是一个开始:

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));
}

我会认真考虑Jeremy的建议,我希望在我编写现在所有的二进制打包代码时它就已经存在了。

h9a6wy2h

h9a6wy2h3#

您需要通过typedef定义一个精确宽度的整数类型;如果使用C99,则int16_t是在<stdint.h>中预定义的。然后可以强制转换为该类型,并键入变量的内存表示:

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

注意,您仍然需要处理字节序。
如果你没有C99,你可以使用编译时或运行时大小测试。对于编译时测试,考虑使用autoconf,它已经计算了各种基元类型的大小,这样你就可以在编译时选择一个好的类型。在运行时,只需进行一系列sizeof测试。注意,这在运行时有点不合适。因为测试总是得到相同的结果。作为autoconf的替代方法,您也可以使用编译器/系统标识宏进行编译时测试。

8qgya5xd

8qgya5xd4#

C++的方法是使用stringstream

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

要获得缓冲区,可以使用ss.str().c_str()

62o28rlo

62o28rlo5#

我在c/c中进行了此实现,以比较python/php/dart/chttps://github.com/dart-lang/sdk/issues/50708之间的pack函数的执行时间

#include <span>
#include <vector>
#include <cstdio>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include "time.h"
#include <map>

#define STRUCT_ENDIAN_NOT_SET 0
#define STRUCT_ENDIAN_BIG 1
#define STRUCT_ENDIAN_LITTLE 2

static int myendian = STRUCT_ENDIAN_NOT_SET;

void debug_print2(const char *str, std::vector<unsigned char> vec)
{
    std::cout << str;
    for (auto i : vec)
        std::cout << i;

    std::cout << "\r\n";
}

int struct_get_endian(void)
{
    int i = 0x00000001;
    if (((char *)&i)[0])
    {
        return STRUCT_ENDIAN_LITTLE;
    }
    else
    {
        return STRUCT_ENDIAN_BIG;
    }
}

static void struct_init(void)
{
    myendian = struct_get_endian();
}

static void pack_int16_t(unsigned char **bp, uint16_t val, int endian)
{
    if (endian == myendian)
    {
        *((*bp)++) = val;
        *((*bp)++) = val >> 8;
    }
    else
    {
        *((*bp)++) = val >> 8;
        *((*bp)++) = val;
    }
}

static void pack_int32_t(unsigned char **bp, uint32_t val, int endian)
{
    if (endian == myendian)
    {
        *((*bp)++) = val;
        *((*bp)++) = val >> 8;
        *((*bp)++) = val >> 16;
        *((*bp)++) = val >> 24;
    }
    else
    {
        *((*bp)++) = val >> 24;
        *((*bp)++) = val >> 16;
        *((*bp)++) = val >> 8;
        *((*bp)++) = val;
    }
}

static void pack_int64_t(unsigned char **bp, uint64_t val, int endian)
{
    if (endian == myendian)
    {
        *((*bp)++) = val;
        *((*bp)++) = val >> 8;
        *((*bp)++) = val >> 16;
        *((*bp)++) = val >> 24;
        *((*bp)++) = val >> 32;
        *((*bp)++) = val >> 40;
        *((*bp)++) = val >> 48;
        *((*bp)++) = val >> 56;
    }
    else
    {
        *((*bp)++) = val >> 56;
        *((*bp)++) = val >> 48;
        *((*bp)++) = val >> 40;
        *((*bp)++) = val >> 32;
        *((*bp)++) = val >> 24;
        *((*bp)++) = val >> 16;
        *((*bp)++) = val >> 8;
        *((*bp)++) = val;
    }
}

static int pack(void *b, const char *fmt, long long *values, int offset = 0)
{
    unsigned char *buf = (unsigned char *)b;

    int idx = 0;

    const char *p;
    unsigned char *bp;
    int ep = myendian;
    int endian;

    bp = buf + offset;
    auto bpp = &bp;

    if (STRUCT_ENDIAN_NOT_SET == myendian)
    {
        struct_init();
    }

    for (p = fmt; *p != '\0'; p++)
    {       
        auto value = values[idx];
        switch (*p)
        {
        case '=': // native 
            ep = myendian;
            break;
        case '<': // little-endian 
            endian = STRUCT_ENDIAN_LITTLE;
            ep = endian;
            break;
        case '>': // big-endian 
            endian = STRUCT_ENDIAN_BIG;
            ep = endian;
            break;
        case '!': // network (= big-endian) 
            endian = STRUCT_ENDIAN_BIG;
            ep = endian;
            break;
        case 'b':
            *bp++ = value;
            break;
        case 'c':
            *bp++ = value;
            break;
        case 'i':
            if (ep == STRUCT_ENDIAN_LITTLE)
            {
                *bp++ = value;
                *bp++ = value >> 8;
                *bp++ = value >> 16;
                *bp++ = value >> 24;
            }
            else
            {
                *bp++ = value >> 24;
                *bp++ = value >> 16;
                *bp++ = value >> 8;
                *bp++ = value;
            }
            break;
        case 'h':
            if (ep == STRUCT_ENDIAN_LITTLE)
            {
                *bp++ = value;
                *bp++ = value >> 8;
            }
            else
            {
                *bp++ = value >> 8;
                *bp++ = value;
            }
            break;
        case 'q':
            if (ep == STRUCT_ENDIAN_LITTLE)
            {
                *bp++ = value;
                *bp++ = value >> 8;
                *bp++ = value >> 16;
                *bp++ = value >> 24;
                *bp++ = value >> 32;
                *bp++ = value >> 40;
                *bp++ = value >> 48;
                *bp++ = value >> 56;
            }
            else
            {
                *bp++ = value >> 56;
                *bp++ = value >> 48;
                *bp++ = value >> 40;
                *bp++ = value >> 32;
                *bp++ = value >> 24;
                *bp++ = value >> 16;
                *bp++ = value >> 8;
                *bp++ = value;
            }
            break;
        }
        idx++;
    }
    return (bp - buf);
}

int main()
{

    time_t start, end;
    time(&start);
    // std::ios_base::sync_with_stdio(false);

    std::vector<unsigned char> myVector{};
    myVector.reserve(100000000 * 16);

    for (int i = 0; i < 100000000; i++) // 100000000
    {
        char bytes[BUFSIZ] = {'\0'};
        long long values[4] = {64, 65, 66, 67};
        pack(bytes, "iiii", values);

        for (int j = 0; j < 16; j++)
        {
            myVector.push_back(bytes[j]);
        }       
    }

    time(&end);
    auto v2 = std::vector<unsigned char>(myVector.begin(), myVector.begin() + 16);
    debug_print2("result: ", v2);

    double time_taken = double(end - start);
    std::cout << "pack time: " << std::fixed
              << time_taken << std::setprecision(5);
    std::cout << " sec " << std::endl;
    return 0;
}

相关问题