我想要一种指定位图的方法,它看起来像这样:
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/