V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
ck65
V2EX  ›  问与答

有办法用 Go 输出与 C++ 的 reinterpret_cast<char*> 一致的结果吗?

  •  
  •   ck65 · 2022-05-16 17:45:48 +08:00 · 898 次点击
    这是一个创建于 682 天前的主题,其中的信息可能已经有所发展或是发生改变。

    问题描述

    在读一段 C++ 代码,并想要用 Go 重写其逻辑。C++ 的相关代码是这样的:

    std::stringstream buffer;
    valhalla::baldr::TrafficTileHeader header = {}; // 这是一个 struct
    
    // 此处省略若干字段赋值
    header.last_update = last_update_timestamp;
    
    buffer.write(reinterpret_cast<char*>(&header), sizeof(header));
    

    这段代码的作用简单来说是创建一个 header 对象(不熟 C++,不知道这么称呼是否正确?),对其中的字段进行一些赋值后写到一个 stringstream 里。后续会把这个 buffer 写入文件。

    我在尝试用 Go 重写这个过程,目的是产生相同格式的文件,让另外一个程序读取。我的关键代码如下:

    buf := &bytes.Buffer{}
    
    header := TrafficTileHeader{
    	// 省略若干字段赋值
    	LastUpdate: uint64(time.Now().Unix()),
    }
    
    binary.Write(buf, binary.BigEndian, header)
    

    然后把 buf 的内容写入一个文件。在文本编辑器中比较 C++ 和 Go 输出的文件,虽然内容是二进制,但字符结构看起来结构差异非常大,目标程序无法识别 Go 输出的文件。


    疑问

    1. 用 Go 实现的思路是否合理?
    2. Go 是否有和 reinterpret_cast<char*>(&header) 等价的操作,从而得到一致的结果?

    参考

    上文 C++ 代码段所在的源文件:

    https://github.com/alinmindroc/valhalla_traffic_poc/blob/main/valhalla_code_overwrites/src/mjolnir/valhalla_traffic_demo_utils.cc#L110


    谢谢各位耐心阅读!🙏

    第 1 条附言  ·  2022-05-16 18:34:49 +08:00

    感谢各位大佬的指点,思路清楚一些了。

    原 C++ 项目里使用了 Memory Map,会沿着这个思路深挖一下。

    参考:

    13 条回复    2022-05-17 13:25:03 +08:00
    7v9TEc53
        1
    7v9TEc53  
       2022-05-16 17:52:43 +08:00   ❤️ 1
    用 protobuf ,因为一旦结构体里面包含指针,即使你通过同一个 c++程序去读取文件也是达不到你的效果的
    7v9TEc53
        2
    7v9TEc53  
       2022-05-16 17:55:12 +08:00
    看了下代码,看来原作者就是这么干的,对 go 不太熟悉,用 cgo 应该是可以的,主要是要保证结构体在内存中的表现要与 go 一致
    sunny352787
        3
    sunny352787  
       2022-05-16 18:03:54 +08:00   ❤️ 1
    C++是内存拷贝,Go 这是按字段序列化,你这怎么可能一致

    内存拷贝是要考虑对齐和数据在内存中的排布的,大端还是小端需要跟着 CPU 走
    Go 这边 binary 的 write 是给定了大端模式,正常 x86CPU 应该都是小端

    话说,这个 C++代码居然直接把内存 dump 出来存成文件好像也确实没考虑用其他类型的 CPU 啊
    dbskcnc
        4
    dbskcnc  
       2022-05-16 18:03:55 +08:00   ❤️ 1
    reinterpret_cast<char*>只是拿到结构体的内存地址,并没有什么魔法。

    你的问题其实是结构体的内存布局不一定相同。内存布局 /对齐是一个比较 hack 的事情,不同的平台,不同的编译器,不同的编译参数都有可能出现不同的结果。
    ysc3839
        5
    ysc3839  
       2022-05-16 18:12:10 +08:00 via Android   ❤️ 1
    去找 golang 如何读写 C 结构体
    changnet
        6
    changnet  
       2022-05-16 18:14:32 +08:00   ❤️ 1
    不懂 go ,但那段 C++的代码,你得保证 header 那个结构体是 POD 数据类型,然后去掉对齐,再写入。那这样写入的就是结构体里的各个字段的值,和手动把各个字段的值取出来再写进文件是一样的。

    大小端这个有处理了那就不说了

    这样其他程序读出来应该是没问题的,我们的协议有些就是这么发,对方不是 C++
    sunny352787
        7
    sunny352787  
       2022-05-16 18:16:43 +08:00
    想了一下,C++自身应该是小端存储,因为默认左移操作等于 x2 ,那你这个问题可以把 Go 代码的大端改成小端试一下,不过即便改成小端模式,也应该会有字节对齐的问题,还是按 5 楼的说法先去搜一下吧
    GuuJiang
        8
    GuuJiang  
       2022-05-16 18:42:37 +08:00 via iPhone   ❤️ 1
    @sunny352787 左移操作等于 x2 和大小端存储之间没有任何联系
    Buges
        9
    Buges  
       2022-05-16 18:57:21 +08:00 via Android   ❤️ 1
    你这样要求内存表示完全相同啊,不知道 go 能不能声明 repr(C)的结构体。最稳妥的方式还是序列化。
    lysS
        10
    lysS  
       2022-05-17 09:53:46 +08:00   ❤️ 1
    go 的结构体就是一段内存,考虑到内存对其,字段中间可能有“空隙”
    sunny352787
        11
    sunny352787  
       2022-05-17 11:26:14 +08:00
    @GuuJiang 你确定?再想想呢?
    GuuJiang
        12
    GuuJiang  
       2022-05-17 11:41:26 +08:00 via iPhone
    @sunny352787 你自己再想想
    sunny352787
        13
    sunny352787  
       2022-05-17 13:25:03 +08:00   ❤️ 1
    @GuuJiang 查了一圈,发现了一些东西

    对于左移操作等于 x2 这个说法,简单地说,左移操作是将低位转移到高位,也就是说无论高位在前还是高位在后都无所谓,所以确实和大小端没关系,这是我理解错误了

    由此我也比较好奇查了一下小端 CPU 和大端 CPU 的一些区别,日常使用的 x86 或者 arm 都可以当做小端处理器,当然 arm 处理器也支持大端,PowerPC 是大端处理器,但这些大端小端的区别也只是体现在寄存器当中,对于整形的左移右移操作来说是没有区别的

    不过确实有一个特例情况,就是在处理向量位移操作的时候,PowerPC 的处理是字节序相关的,具体就是 vec_sld 这个指令,https://stackoverflow.com/questions/46341923/is-vec-sld-endian-sensitive/46342218#46342218 在这条回答当中给出了 IBM 的文档 https://www.ibm.com/docs/en/zos/2.2.0?topic=rs-vec-sld-vector-shift-left-double-by-byte 说明这个左移操作是字节序相关的,但这个是处理两个寄存器数据,对于单一寄存器的数据处理还是遵循标准的左移定义
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4804 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 01:09 · PVG 09:09 · LAX 18:09 · JFK 21:09
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.