遍历位域成员

4
我们有这个例子:
struct X {
  int e0 : 6;
  int e1 : 6;
  int e2 : 6;
  ...
  int e10 : 6;
};

struct X c;

我该如何“自动”访问成员,类似于这样:
c.e{0-10} ?
如果我想读取 c.e0,那么 c.e1 ...
如果我的结构体有1000个元素,我不认为我应该写那么多代码,对吧?
你能帮我想出一个解决方法或者一个想法吗?
我提到我已经阅读了其他与此问题相关的帖子,但我没有找到解决方案。
非常感谢!

4
你真的需要使用6位整数成员吗?这将会影响问题的答案。 - Steve M
注意反射:C++希望拥有你。 - nmichaels
3
很抱歉告诉你,如果您的结构体有1000个元素,那么您的设计将非常糟糕。 - Eugen Constantin Dinca
1
结构体只需要三个值,多于四个可以使用这些结构体的数组。 - Gareth Davidson
1
谢谢大家的回答!这是一个应该模拟SET并集和交集的问题。我的想法是应该使用像这样的结构体。让我们说我应该在e0中拥有的最大数字是32。这就是为什么我有6位比特字段的原因。问题中没有指定SET将有多少个元素。即使我们有一个SET A和另一个SET B,A有5个元素,B有3个元素,这仍然不方便(因为我必须编写一些代码行)。 - bsd
显示剩余5条评论
5个回答

5

正如其他人所说,使用位域无法完全实现您想要的功能。看起来您想要以最大空间效率存储大量6位整数。我不会争论这是否是一个好主意。相反,我将介绍一种类似于旧式C语言的方法,使用C++封装功能来实现这一点(未经测试)。思路是4个6位整数需要24位或3个字符。

// In each group of 3 chars store 4 6 bit ints
const int nbr_elements = 1000;
struct X
{
    // 1,2,3 or 4 elements require 3 chars, 5,6,7,8 require 6 chars etc.
    char[ 3*((nbr_elements-1)/4) + 3 ] storage;
    int get( int idx );
};

int X::get( int idx )
{
    int dat;
    int offset = 3*(idx/4);      // eg idx=0,1,2,3 -> 0 idx=4,5,6,7 -> 3 etc.
    char a = storage[offset++];
    char b = storage[offset++];
    char c = storage[offset];
    switch( idx%4)  // bits lie like this; 00000011:11112222:22333333
    {
        case 0: dat = (a>>2)&0x3f;                    break;
        case 1: dat = ((a<<4)&0x30) + ((b>>4)&0x0f);  break;
        case 2: dat = ((b<<2)&0x3c) + ((c>>6)&0x03);  break;
        case 3: dat = c&0x3f;                         break;
    }   
    return dat;
}

我会将companion put()函数留作练习。


我会将其改为 template<std::size_t N> struct X { char[3*((N - 1) / 4) + 3] storage; }; 以使元素数量可调,但这可能或可能不是必要的。此外,我会添加一个检查以确保索引在范围内。 - Chris Lutz
@bsabin 不用谢。如果你想让我也写出put(),请在评论中提出。我刚刚才注意到已经有一个类似的答案了,而且有人评论说写(put)会很“可怕”。我相信其实也不会太糟糕。 - Bill Forster
@Chris。感谢您的建议,正如您所说,我更多地是一名C程序员,但我正在尝试使用更多的C++,并且可以从像您这样的评论中学习。 - Bill Forster
@Bill - 我通常更喜欢C而不是C ++,但是随着我越来越习惯(有时可怕的)语法,我越来越欣赏C ++的一些功能。我发现将老派的C黑客和宏与C ++运算符重载融合在一起非常有趣,并且可以产生一些漂亮的新语法(并且非常清晰):http://phimuemue.com/blog.php?article=172(不是我的,但仍然很棒) - Chris Lutz
@Chris。那个例子非常酷,非常适合个人项目,在这些项目中你可以拥抱这样的东西,并真正将其整合到你的思维模型中。但是,如果你将这样的东西添加到一个大型项目中,你可能会为社区创建一个维护噩梦。Linus Torvalds喜欢抱怨C++的这个方面,而他的例子比你的要简单得多。有趣的是,现代C++特性可以创建表面上非常好的代码,除了任何试图分解和理解它的代码都会淹没在数百万个<和>字符中的副作用。 - Bill Forster
TorvaldsеҸӘжҳҜеӣ дёәжҲ‘ж— жі•зҗҶи§Јзҡ„еҺҹеӣ жӣҙе–ңж¬ўx>>6 & 0x1FиҖҢдёҚжҳҜx<6>гҖӮеңЁиҝҷз§Қй—®йўҳдёҠпјҢжҖ§иғҪе’ҢеҸҜиҜ»жҖ§ж №жң¬жҳҜзӣёдә’зҹӣзӣҫзҡ„гҖӮ - MSalters

3
似乎结构体并不是你所需要的工具,你需要使用数组向量。数组用于存储相同类型的多个数据。向量是数组包装器,可以自动管理添加和删除项目。
如果您需要同一数据的列表以及其他数据(例如字符串),则可以将数组或向量作为结构体的一部分。
struct X {
   int[10] numbs;
   string name;
};

X c;

2
像这样怎么样:
char getByte(char *startPos, int index) {

    int i = (index*6) / 8;

    if (index % 4 == 0)
        return 0b11111100 & startPos[i] >> 2;
    else if (index % 4 == 3)
        return 0b00111111 & startPos[i];
    else if (index % 4 == 2)
        return (0b00001111 & startPos[i] << 2) | (0b11000000 & startPos[i+1] >> 6);
    else
        return (0b00000011 & startPos[i] << 4) | (0b11110000 & startPos[i+1] >> 4);
}

1
很确定应该是 index % 4 == 3 而不是 index % 3 - Ben Voigt
@Billy - 我做了一些谷歌搜索,所有我找到的来源都同意你的观点。相当不幸,但显然是真的。 - Chris Lutz
1
@Billy,@Chris,@Gaz:我很确定位域的基础类型在哪里进行填充起到了作用。例如,如果这是一个16位的 int,编译器可以将2个6位字段和4个填充位放入 int 中,但这在32位的 int 中是不允许的,编译器必须将5个6位字段加上2个填充位放在一起才能形成 int - Ben Voigt
1
@Ben @Chris @Gaz:请参考http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf的6.2.6.1章节。目前还不清楚是否允许这样做。根据6.7.2.1条款10,如果位域可以适应一个可寻址单元(通常是`char`),编译器必须将其放入该单个可寻址单元中,但如果位域太宽而无法放入单个单元中,则可以在可寻址单元之间插入空间。 - Billy ONeal
1
@Gaz:我认为可能还需要一些位移操作。 - Ben Voigt
显示剩余10条评论

2

你无法做到这一点。要做到这一点需要某种形式的反射,而C或C++都不支持。


2
由于你的位字段大小相同,因此您可以封装std::bitset(或vector,gulp ...),并提供自己的迭代器(每次增加将书签移动六位),以及operator[](等),以使您的代码更容易编写。虽然性能可能不如位字段。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接