将指向结构的指针转换为具有较少字段的另一种结构类型
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_vector
和 gsl_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_vector
和 gsl_matrix
的类似内容;我可以确保 reflex_size
和 size
始终相等(都不大于 ~50)并且 Reflex 将能够正确解析此 header(我希望;如果 reflex_size 需要在 data 之前,因为需要一些更困难的字段)。由于 GSL 例程使用指向这些结构的指针,我的想法是:给定一个指针 extended_gsl_block*
,以某种方式获得一个指向字段 size
和 data
以及 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");
基本问题
我遇到了一个棘手的情况,需要获取一个指向结构 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_vector
和 gsl_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_vector
和 gsl_matrix
的类似内容;我可以确保 reflex_size
和 size
始终相等(都不大于 ~50)并且 Reflex 将能够正确解析此 header(我希望;如果 reflex_size 需要在 data 之前,因为需要一些更困难的字段)。由于 GSL 例程使用指向这些结构的指针,我的想法是:给定一个指针 extended_gsl_block*
,以某种方式获得一个指向字段 size
和 data
以及 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");