【发布时间】:2010-07-05 09:17:40
【问题描述】:
让我们有一个类型 T 和一个只有 T 类型的统一元素的结构。
struct Foo {
T one,
T two,
T three
};
我想以休闲方式访问它们:
struct Foo {
T one,
T two,
T three
T &operator [] (int i)
{
return *(T*)((size_t)this + i * cpp_offsetof(Foo, two));
}
};
其中cpp_offsetof宏(被认为是正确的)是:
#define cpp_offsetof(s, m) (((size_t)&reinterpret_cast<const volatile char&>((((s*)(char*)8)->m))) - 8)
C++ 标准不保证这一点,但我们可以假设成员之间的距离为固定偏移量及以上是正确的跨平台解决方案吗?
100% 兼容的解决方案是:
struct Foo {
T one,
T two,
T three
T &operator [] (int i) {
const size_t offsets[] = { cpp_offsetof(Foo, one), cpp_offsetof(Foo, two), cpp_offsetof(Foo, three) };
return *(T*)((size_t)this + offsets[i]);
}
};
[edit]标准、合规和更快的版本由 snk_kid using pointers to data members[/edit]
但它需要额外的查找表,我试图避免。
//编辑
还有一个。我不能只使用数组和常量来索引这些字段,它们必须被命名为结构的字段(某些宏需要这样做)。
//EDIT2
为什么这些必须命名为结构的字段?什么是宏?它是一个更大项目的设置系统。简化它是这样的:
struct Foo {
int one;
int two;
}
foo;
struct Setting { void *obj, size_t filed_offset, const char *name, FieldType type }
#define SETTING(CLASS, OBJ, FIELD, TYPE) { OBJ, cpp_offsetof(CLASS, FIELD), #OBJ #FIELD, TYPE }
Setting settings[] = {
SETTING(Foo, foo, one, INT_FIELD),
SETTING(Foo, foo, two, INT_FIELD)
};
再说一次:我不是在寻找 100% 兼容的解决方案,而是 99%。我在问我们是否可以预期某些编译器会在统一字段之间放置非统一填充。
【问题讨论】:
-
与 OP 无关,但有谁知道为什么那个 offsetof 宏使用 8 作为基础? MSVC 使用 0,GCC 使用内置函数,甚至在非零基数上存在 ICE 错误。
-
即使我有答案,我也会避开这个问题,看看一切都被否决了......:S(开玩笑,自然)
-
不,我自己在检查。阅读我的评论。现在我取消了我的下注。如果你不能证明我错了,就会被否决。为了清楚起见,我写道我已经知道使用表格查找的解决方案,这就是我投反对票的原因。
-
为什么使用
int作为索引类型?您是否期望负索引值? -
上次我们在工作中遇到这个问题,我们只是写了一个Perl脚本来解析出结构字段名称并生成一堆打印字段名称的
printfs和offsetof每个字段。我们没有奢侈的所有字段都是相同类型或对齐的。如今,我们最多只使用std::map<std::string, T>,然后继续我们的生活。
标签: c++ cross-platform memory-alignment