c++ - 是否有任何现有的解决方案来制作可移植的有序位字段?

标签 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 的调用之间字段的数量可以不同,宽度也可以。

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

最佳答案

好吧,我承认我有点低估了这个问题。您指出您希望您的宏使用可变数量的位域规范。这需要一些工作……但现在开始吧。

/* 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 一起应用的命名参数习语。

希望这对您有所帮助。

关于c++ - 是否有任何现有的解决方案来制作可移植的有序位字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33772765/

相关文章:

c++ - 如何使用 QDatastream 在 QT 中正确序列化和反序列化 QList 类?

c++ - gcc 链接到未使用的库对构建系统性能的影响

c++ - 如何使用数据库 (SQlite) 制作 C++ 可移植应用程序

c++ - 以纳秒为单位测量精确时间 C++

C: 尝试将 BITMAP 转换为 .bmp 文件时出现问题

java - 如何将包含位图的对象传递给另一个 Activity

c++ - 在结构未排序数组中将索引向左移动

c# - 如何将图像从 PhotoChooserTask 复制到位图

android - 如何从 URL 加载图像到 viewpager

安卓开发 : Bitmaps on surfaceview leak Memory or too big?