使用数组下标运算符访问结构成员
假设有一个类型 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]);
}
};
[编辑]标准、兼容且更快的版本由 snk_kid 使用指向数据成员的指针[/edit]
但它需要额外的查找表,我试图避免。
//编辑
还有一个。我不能只使用数组和常量来索引这些字段,它们必须被命名为结构的字段(某些宏需要这样做)。
//编辑2
为什么这些必须被命名为结构体的字段?什么是宏?这是一个更大项目的设置系统。简化起来是这样的:
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% 的。我问我们是否可以期望某些编译器会在统一字段之间放置非统一填充。
Let have a type T and a struct having ONLY uniform elements of T type.
struct Foo {
T one,
T two,
T three
};
I'd like to access them in fallowing way:
struct Foo {
T one,
T two,
T three
T &operator [] (int i)
{
return *(T*)((size_t)this + i * cpp_offsetof(Foo, two));
}
};
where cpp_offsetof
macro (it is considered to be correct) is:
#define cpp_offsetof(s, m) (((size_t)&reinterpret_cast<const volatile char&>((((s*)(char*)8)->m))) - 8)
The C++ standard doesn't guarantee it, but can we assume that members are distanced by a fixed offset and above is correct, cross-platform solution?
100% compatible solution would be:
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]standard, compliant and faster version was presented by snk_kid using pointers to data members[/edit]
but it requires extra lookup table which I'm trying to avoid.
//EDIT
And one more. I cannot use just an array and constants to index these fields, they have to be named fields of a struct (some macro requires that).
//EDIT2
Why those have to be named fields of a struct? What is the macro? It is settings system of a bigger project. Simplifying it's sth like this:
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)
};
And once again: I'm not looking form 100% compatible solution but 99%. I'm asking if we can expect that some compilers will put non-uniform padding between uniform fields.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您的代码不适用于非 POD 类型,例如使用虚拟成员函数的类型。有一种符合标准(且有效)的方法可以使用指向数据成员的指针来实现您想要做的事情:
只需确保将 const every 与指向数据成员的指针表一起使用获得优化。请查看此处了解我在说什么。
Your code doesn't work with NON-POD types such those which using virtual member functions. There is a standard compliant (and efficient) way to achieve what you're trying to do, using pointer to data members:
Just make sure you use const every with the table of pointer to data-members to get optimizations. Check here to see what I'm talking about.
如果您想要实现的仍然是编译时功能,另一种方法是使用模板专业化。
将 get 定义为成员函数会很好,但你不能
专门化模板成员函数。现在如果这只是一个编译时间
您正在寻找的扩展那么这将避免查找表
先前帖子之一的问题。如果您需要运行时解析
那么显然你需要一个查找表。
--
布拉德·费兰
http://xtargets.heroku.com
Another way is with template specialization if what you are trying to achieve is still a compile time feature.
It would be nice to define get as a member function but you cannot
specialize template member functions. Now if this is only a compile time
expansion you are looking for then this will avoid the lookup table
issue of one of the previous posts. If you need runtime resolution
then you need a lookup table obviously.
--
Brad Phelan
http://xtargets.heroku.com
您也许能够使用数组来保存数据(这样您就可以在不使用查找表的情况下获得索引访问)并引用各种数组元素(这样您就可以拥有“命名”元素以供您使用)来实现您想要的目标宏)。
我不确定你的宏需要什么,所以我不能 100% 确定这会起作用,但它可能会起作用。另外,我不确定查找表方法的轻微开销是否值得跳过太多的环节来避免。另一方面,我认为我在这里建议的方法并不比指针表方法更复杂,所以这里供您考虑:
You might be able to achieve what you want using an array to hold the data (so you can get indexed access without using a lookup table) and having references to the various array elements (so you can have 'named' elements for use by your macros).
I'm not sure what your macros require, so I'm not 100% sure this will work, but it might. Also, I'm not sure that the slight overhead of the lookup table approach is worth jumping through too many hoops to avoid. On the other hand, I don't think the approach I suggest here is any more complex than the table-of-pointers approach, so here it is for your consideration:
不能,因为编译器可以在成员之间添加死字节以允许填充。
有两种方法可以做你想做的事。
第一种是使用特定于编译器的关键字或编译指示宏,这将强制编译器不添加填充字节。但这是不可移植的。
也就是说,这可能是满足您的宏要求的最简单方法,因此我建议您探索这种可能性,并准备在使用不同编译器时添加更多编译指示。
另一种方法是首先确保您的成员对齐,然后添加访问器:
You can't because the compiler can add dead bytes between members to allow padding.
There is two ways to do what you want.
The first is to use your compiler-specific keyword or pragma macro that will force the compiler to not add padding bytes. But that is not portable.
That said it might be the easiest way to do it with your macro requirements, so I suggest you explore this possibility and prepare for adding more pragma when using different compilers.
The other way is to first make sure your members are aligned, then add accessors :
如果您确定您使用的编译器将为此生成正确的代码(我想他们会的,假设 T 无论如何都不是引用类型),最好的办法就是放入某种检查结构是否按照您的想法布局。我想不出在同一类型的相邻成员之间插入不均匀填充的任何特殊原因,但如果您手动检查结构布局,那么您至少会知道它是否会发生。
例如,如果结构体 (S) 恰好有 N 个类型为 T 的成员,您可以在编译时简单地使用
sizeof
检查它们是否紧密包装:如果可以编译,则它们是紧密包装的,因为没有空间放其他东西了。
如果您恰好有 N 个成员,并且想要确保它们直接一个接一个地放置,您可以使用
offsetof
执行类似的操作:根据编译器的不同,这可能必须成为运行时检查,可能不使用
offsetof
——无论如何,您可能希望对非 POD 类型执行此操作,因为没有为它们定义offsetof
。这并没有说明如果断言开始失败该怎么办,但您至少应该收到一些警告,表明假设不正确...
(顺便说一句,在适当的情况下插入适当的转换到 char 引用等)为简洁起见,我将它们省略了。)
If you're sure the compilers you're using are going to generate the right code for this (and I'd imagine they would, assuming T isn't a reference type anyway) the best thing to do is put in some kind of check that the struct is laid out as you think. I can't think of any particular reason to insert non-uniform padding between adjacent members of the same type, but if you check the struct layout by hand then you'll at least know if it happens.
If the struct (S) has exactly N members of type T, for example, you can check at compile time that they are tightly packed simply using
sizeof
:If this compiles, then they're tightly packed, as there's no space for anything else.
If you just happen to have N members, that you want to ensure are placed directly one after the other, you can do something similar using
offsetof
:Depending on the compiler, this might have to become a runtime check, possibly not using
offsetof
-- which you might want to do for non-POD types anyway, becauseoffsetof
isn't defined for them.This doesn't say anything about what to do if the asserts start failing, but you should at least get some warning that the assumptions aren't true...
(By the way, insert appropriate casts to char references and so on where appropriate. I left them out for brevity.)