将指向结构的指针转换为具有较少字段的另一种结构类型

Casting a pointer to a struct into another struct type with a smaller number of fields

基本问题

我遇到了一个棘手的情况,需要获取一个指向结构 mainset 的指针并将其转换为一个指向结构 subset 的指针,其字段是mainset,从第一个开始。 well-defined 这种行为可能吗?我意识到这是一件非常糟糕的事情,但我有充分且令人沮丧的理由去做[在底部为耐心的读者解释]。

我尝试一个实现 似乎 工作,在 OS X 上使用 clang 编译器:

#include <iostream>

struct mainset {
    size_t size;
    uint32_t reflex_size;
};

struct subset {
    size_t size;
};

using namespace std;
int main(int argc, char *argv[]) {
    mainset test = {1, 1};
    subset* stest = reinterpret_cast<subset*>(&test);
    std::cout << stest->size << std::endl;
}

如我所料,输出确实是1。但是,我想知道:我只是幸运地使用了特定的编译器和一个简单的案例(实际上我的结构更复杂),还是这通常会起作用?

此外,一个 follow-up 问题:由于其他烦人的原因,我担心我可能需要制作更大的结构

struct mainset {
    uint32_t reflex_size;
    size_t size;
};

相反,额外的字段位于前面。我的实施可以扩展到这种情况下吗?我尝试用 &test+sizeof(test.reflex_size) 替换 &test 但这没有用; cout 语句的输出为 0。

解释为什么我必须这样做

我的项目使用线性代数的 GSL 库。该库使用

形式的结构
struct gsl_block {
   size_t size;
   double* data;
}

和类似的结构,如 gsl_vectorgsl_matrix。因此,我将这些结构用作我的 C++ 类 的成员;没问题。然而,我的项目最近需要的一个功能是使用 Reflex 工具(ROOT 生态系统的一部分)启用对我的 类 的反射。要在 Reflex 中为这样的结构启用反射,我必须添加一个注释,如

struct gsl_block {
   size_t size;
   double* data; //[size]
}

这个注解告诉 Reflex 数组的长度是由同一个结构体的字段 size 提供的。通常情况下, Reflex 和 ROOT 有一个非常不幸的限制:长度字段必须是 32 位。得知此限制不会很快修复,并且没有 time/resources 自行修复,我正在寻找解决方法。我的想法是以某种方式在更大的结构中嵌入结构 bit-compatible 和 gsl_block

struct extended_gsl_block {
   size_t size;
   double* data; //[reflex_size]
   uint32_t reflex_size;
}

以及 gsl_vectorgsl_matrix 的类似内容;我可以确保 reflex_sizesize 始终相等(都不大于 ~50)并且 Reflex 将能够正确解析此 header(我希望;如果 reflex_size 需要在 data 之前,因为需要一些更困难的字段)。由于 GSL 例程使用指向这些结构的指针,我的想法是:给定一个指针 extended_gsl_block*,以某种方式获得一个指向字段 sizedata 以及 reinterpret_cast 的指针进入 gsl_block*.

你走运了。

您展示的类符合标准布局类型的要求。

您可以在此处阅读更多内容:

http://en.cppreference.com/w/cpp/language/data_members#Standard_layout

您可以在编译器中测试这个前提:

static_assert(std::is_standard_layout<gsl_block>::value, "not a standard layout");