【问题标题】:Is FlatBuffers C++ reinterpret_cast access actually undefined behavior? Is it practically OK to do that?FlatBuffers C++ reinterpret_cast 访问实际上是未定义的行为吗?这样做实际上可以吗?
【发布时间】:2020-05-13 04:18:51
【问题描述】:

最近我尝试在 C++ 中使用FlatBuffers。我发现 FlatBuffers 似乎使用了很多类型双关语,比如 C++ 中的 reinterpret_cast。这让我有点不舒服,因为我学会了it's undefined behavior in many cases

例如Rect 在 fbs 文件中:

struct Rect {
    left:int;
    top:int;
    right:int;
    bottom:int;
}

变成这个 C++ 代码以便从表中读取它:

  const xxxxx::Rect *position() const {
    return GetStruct<const xxxxx::Rect *>(VT_POSITION);
  }

the definition of GetStruct 只是使用 reinterpret_cast。

我的问题是:

  1. 这真的是 C++ 中未定义的行为吗?
  2. 在实践中,这种用法真的会有问题吗?

更新:

缓冲区可以来自网络或磁盘。如果缓冲区实际上来自同一个 C++ 程序的编写者编写的同一个内存,我不知道是否不同。

但是作者自动生成的方法是:

  void add_position(const xxxxx::Rect *position) {
    fbb_.AddStruct(Char::VT_POSITION, position);
  }

这将使用this methodthis method,因此也使用reinterpret_cast。

【问题讨论】:

  • 这在一定程度上取决于VT_POSITION 的定义以及那里的数据是如何到达那里的。
  • @1201ProgramAlarm VT_POSITION 是一个自动生成的枚举常量,我认为它就像一个偏移量: enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_POSITION = 4, VT_CANDIDATE = 6 };
  • 根据发布的代码无法回答。如果该类型的对象存在于该位置,则该代码将是有效的。
  • @M.M 我只是想询问通常的 FlatBuffers 使用情况,比如从网络或磁盘读取缓冲区。

标签: c++ flatbuffers


【解决方案1】:

我没有分析整个 FlatBuffers 的源代码,但我没有看到这些对象是在哪里创建的:我没有看到新的表达式,它会在这里创建 P 对象:

template<typename P> P GetStruct(voffset_t field) const {
    auto field_offset = GetOptionalFieldOffset(field);
    auto p = const_cast<uint8_t *>(data_ + field_offset);
    return field_offset ? reinterpret_cast<P>(p) : nullptr;
  }

看来,这段代码确实有未定义的行为。

但是,这仅适用于 C++17(或 pre)。在 C++20 中,会有 implicit-lifetime 对象(例如,标量类型、聚合都是隐式生命周期类型)。如果P 具有隐式生命周期,则可以很好地定义此代码。前提是相同的内存区域总是被一个类型访问,这不违反类型双关规则(例如,它总是被相同的类型访问)。

【讨论】:

    【解决方案2】:

    我认为Flatbuffers: Use in C++ 页面回答了您的两个问题:

    直接内存访问

    从上面的例子可以看出,缓冲区中的所有元素都是通过生成的访问器访问的。这是因为所有平台上的所有内容都以小端格式存储(访问器在大端机器上执行交换操作),还因为用户通常不知道事物的布局。

    对于结构,布局是确定性的,并且保证跨平台相同(标量与它们自己的大小对齐,结构本身与其最大的成员对齐),您可以使用 sizeof() 和memcpy 指向一个结构的指针,甚至是一个结构数组。

    这些段落保证 - 给定一个有效的平面缓冲区 - 所有内存访问都是有效的,因为该特定位置的内存将匹配预期的布局。

    如果您正在处理不受信任的 flatbuffer,您首先需要使用验证器函数来确保 flatbuffer 有效:

    此验证程序将检查所有偏移量、所有字段大小和字符串的空终止,以确保在访问缓冲区时,所有读取都将在缓冲区内结束。

    【讨论】:

      猜你喜欢
      • 2019-05-28
      • 1970-01-01
      • 2011-02-14
      • 2021-12-17
      • 1970-01-01
      • 2018-07-31
      • 2018-03-16
      • 2014-05-30
      • 1970-01-01
      相关资源
      最近更新 更多