是否有任何现有的解决方案来制作便携式有序位字段?

时间:2015-11-18 05:55:40

标签: c++ bitmap

我想要一种指定位图的方法,如下所示:

struct Bitmap
{
    unsigned foo: 2;
    unsigned bar: 5;
    unsigned baz: 3;
};

,和许多类似的结构,但我需要位字段具有可预测的顺序。但是C ++并不保证位字段的任何顺序或打包,因此我必须使用按位运算来制作一些特殊代码来实现它。这可以通过以下结构实现:

class Bitmap
{
    unsigned value;
public:
    unsigned foo() { return value&0x3; }
    unsigned bar() { return (value>>2)&0x1f; }
    unsigned baz() { return (value>>7)&0x7; }
    void set_foo(unsigned new_foo) { value=(value&~0x3)|new_foo; }
    void set_bar(unsigned new_bar) { value=(value&~(0x1f<<2))|(new_bar<<2); }
    void set_baz(unsigned new_baz) { value=(value&~(0x7<<7))|new_baz; }
    Bitmap(unsigned newFoo,unsigned newBar,unsigned newBaz)
        : value(newFoo|(newBar<<2)|(newBaz<<7))
    {}
};

为许多不同的位图编写这样的代码是一项重复的任务,这就是为什么我想自动化它。我可能会使用模板,但在这种情况下,我将无法为每个结构命名不同的位字段(或者必须编写更多代码来包装通用结构以提供名称)。

理想情况下,我需要使用一些宏来与此类似:

DEFINE_BITMAP(Bitmap,foo,2,bar,5,baz,3);
Bitmap myBits(1,9,5);
doStuff(myBits.bar());

其中DEFINE_BITMAP的调用之间的字段数可以不同,宽度也是如此。

所以在我开始发明这个轮子之前:它已经完成了吗?如果是的话,该寻找什么?

2 个答案:

答案 0 :(得分:1)

好的,所以我承认我在某种程度上低估了这个问题。您表示您希望宏使用可变数量的位域规范。这需要一点点工作......但是你走了。

/* Starting with this stuff:
 * https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms
 */

#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__

#define IIF(c) PRIMITIVE_CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t

#define COMPL(b) PRIMITIVE_CAT(COMPL_, b)
#define COMPL_0 1
#define COMPL_1 0

#define INC(x) PRIMITIVE_CAT(INC_, x)
#define INC_0 1
#define INC_1 2
#define INC_2 3
#define INC_3 4
#define INC_4 5
#define INC_5 6
#define INC_6 7
#define INC_7 8
#define INC_8 9
#define INC_9 10
#define INC_10 11
#define INC_11 12
#define INC_12 13
#define INC_13 14
#define INC_14 15
#define INC_15 16
#define INC_16 17
#define INC_17 18
#define INC_18 19
#define INC_19 20

#define DEC(x) PRIMITIVE_CAT(DEC_, x)
#define DEC_0 0
#define DEC_1 0
#define DEC_2 1
#define DEC_3 2
#define DEC_4 3
#define DEC_5 4
#define DEC_6 5
#define DEC_7 6
#define DEC_8 7
#define DEC_9 8
#define DEC_10 9
#define DEC_11 10
#define DEC_12 11
#define DEC_13 12
#define DEC_14 13
#define DEC_15 14
#define DEC_16 15
#define DEC_17 16
#define DEC_18 17
#define DEC_19 18
#define DEC_20 19

#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)
#define PROBE(x) x, 1,

#define IS_PAREN(x) CHECK(IS_PAREN_PROBE x)
#define IS_PAREN_PROBE(...) PROBE(~)

#define NOT(x) CHECK(PRIMITIVE_CAT(NOT_, x))
#define NOT_0 PROBE(~)

#define BOOL(x) COMPL(NOT(x))
#define IF(c) IIF(BOOL(c))

/* We'll add this stuff: */

#define NUM_ARGS1(_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1, n, ...) n
#define NUM_ARGS0(...) NUM_ARGS1(__VA_ARGS__,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)
#define NUM_ARGS(...) IF(DEC(NUM_ARGS0(__VA_ARGS__)))(NUM_ARGS0(__VA_ARGS__),IF(IS_PAREN(__VA_ARGS__ ()))(0,1))

/* Something to extract things from parentheses */

#define GET_1ST(a) GET_1ST_0 a
#define GET_1ST_0(a,b) a

#define GET_2ND(a) GET_2ND_0 a
#define GET_2ND_0(a,b) b

/* And our bitfield builders */

#define BITFIELDS_MAKE_GETTER_SETTER( structname, name, bits, shift ) \
  unsigned name() const { return (value >> (shift)) & ((1U << (bits)) - 1); } \
  structname& name( unsigned field ) { value &= ~(((1U << (bits)) - 1) << (shift)); value |= field << (shift); return *this; }

#define BITFIELDS( name, ... ) CAT(BITFIELDS_,NUM_ARGS(__VA_ARGS__)) ( name, 0, __VA_ARGS__ )
#define BITFIELDS_0(  name, N )
#define BITFIELDS_1(  name, N, a                   ) BITFIELDS_MAKE_GETTER_SETTER(name, GET_1ST(a), GET_2ND(a), N)
#define BITFIELDS_2(  name, N, a,b                 ) BITFIELDS_1(name,N,a) BITFIELDS_1(name,N+GET_2ND(a),b)
#define BITFIELDS_3(  name, N, a,b,c               ) BITFIELDS_1(name,N,a) BITFIELDS_2(name,N+GET_2ND(a),b,c)
#define BITFIELDS_4(  name, N, a,b,c,d             ) BITFIELDS_1(name,N,a) BITFIELDS_3(name,N+GET_2ND(a),b,c,d)
#define BITFIELDS_5(  name, N, a,b,c,d,e           ) BITFIELDS_1(name,N,a) BITFIELDS_4(name,N+GET_2ND(a),b,c,d,e)
#define BITFIELDS_6(  name, N, a,b,c,d,e,f         ) BITFIELDS_1(name,N,a) BITFIELDS_5(name,N+GET_2ND(a),b,c,d,e,f)
#define BITFIELDS_7(  name, N, a,b,c,d,e,f,g       ) BITFIELDS_1(name,N,a) BITFIELDS_6(name,N+GET_2ND(a),b,c,d,e,f,g)
#define BITFIELDS_8(  name, N, a,b,c,d,e,f,g,h     ) BITFIELDS_1(name,N,a) BITFIELDS_7(name,N+GET_2ND(a),b,c,d,e,f,g,h)
#define BITFIELDS_9(  name, N, a,b,c,d,e,f,g,h,i   ) BITFIELDS_1(name,N,a) BITFIELDS_8(name,N+GET_2ND(a),b,c,d,e,f,g,h,i)
#define BITFIELDS_10( name, N, a,b,c,d,e,f,g,h,i,j ) BITFIELDS_1(name,N,a) BITFIELDS_9(name,N+GET_2ND(a),b,c,d,e,f,g,h,i,j)

/* Here's our bitfield class constructor */

#define MAKE_BITFIELD_STRUCT( name, ... ) \
  struct name                        \
  {                                  \
    unsigned long long value;        \
    BITFIELDS( name, __VA_ARGS__ )   \
  }

一旦你拥有了它,你可以很容易地使用它:

#include <iostream>

// Define struct B { ... }
// Fields are specified left-to-right as LSB-to-MSB.
// Each field is given by its name and the number of bits it occupies.
MAKE_BITFIELD_STRUCT( Bitmap, (foo,2), (bar,5), (baz,3) );

int main()
{
  // Construct a Bitmap
  Bitmap b = Bitmap().foo(1).bar(15).baz(7);

  // Prove its worth
  std::cout << std::hex << b.value << "\n"; // produces "3bd"
}

Boost.Preprocessor中也有可用的设施来做这件事,但我觉得使用它们可以在龙之间行走......

请注意,我提供的代码具有以下限制:

  • 您最多可以声明10个位域。如果您想要使用更多内容,则必须更新BITFIELDS_N定义(通过添加更多定义,最多20个)。
  • 每个位域的最大大小为int,所有组合位域的最大大小为long long int。如果您需要更多内容,请考虑更新定义以使用std::bitset

还要注意使用setter的命名参数idiom。

希望这有帮助。

答案 1 :(得分:0)

1)是否可以制作便携式位域?

没有。国际海事组织没有充分的理由说明为什么标准会让它变得奇思妙想,但事实就是如此。

2)所以在我开始发明这个轮子之前:它已经完成了吗?如果是,那该找什么?

我能给出的唯一建议是忘记位域并使用直接位掩码。您可以使用各种技巧来创建位掩码整数;使用宏技巧是一个很好的。只是要小心保持宏。

对于C ++的东西,您也可以在Using Enum Classes as Bitfields上查看这篇文章。 (我自己没有用过,但看起来很酷。)