Recently I needed to convert a C header file to Delphi which contained bitfields. Let’s take a look at a sample structure that contains bitfields:
It means that there is a DWORD (Cardinal) dwValue1 followed by a bitfield with the size of a ULONG (32 bits). In this bitfield 4 values are defined (BitValue1..4) which are used as boolean’s because the value can offcourse be 0 or 1. Since Delphi doesn’t know a bitfield type the question is how to translate it. Usually it would mean that we simply treat the whole bitfield value as a ULONG and extract the required properties by applying a bitmask (shl/shr). Starting from BDS2006 we can define a record with propertes and use getters and setters. Using this technique we can present boolean values to the user:
Code completion shows that the record has one DWORD Value and 4 Boolean Values which is just what we want!

Offcourse we need to implement the Getters and Setters:
function _BITFIELDSTRUCTURE.GetBitValue2;
begin
Result := BitField and 2 = 2;
end;
function _BITFIELDSTRUCTURE.GetBitValue3;
begin
Result := BitField and 4 = 4;
end;
function _BITFIELDSTRUCTURE.GetBitValue4;
begin
Result := BitField and 8 = 8;
end;
procedure _BITFIELDSTRUCTURE.SetBitValue1(const Value: Boolean);
begin
if Value then BitField := BitField or 1 else BitField := BitField and (not 1);
end;
procedure _BITFIELDSTRUCTURE.SetBitValue2(const Value: Boolean);
begin
if Value then BitField := BitField or 2 else BitField := BitField and (not 2);
end;
procedure _BITFIELDSTRUCTURE.SetBitValue3(const Value: Boolean);
begin
if Value then BitField := BitField or 4 else BitField := BitField and (not 4);
end;
procedure _BITFIELDSTRUCTURE.SetBitValue4(const Value: Boolean);
begin
if Value then BitField := BitField or 8 else BitField := BitField and (not 8);
end;
We can even add a constructor to it, this can be used to e.g. initialize the record (in the example below we fill with zeroes). Note that only a constructor with at least one argument can be used:
implementation
constructor _BITFIELDSTRUCTURE.Create; // Did you know that Delphi permits leaving out (const dummy: word) here?
begin
ZeroMemory(@Self, SizeOf(Self));
end;
…
procedure TForm1.Button1Click(Sender: TObject);
var
BitFieldStructure: TBitFieldStructure;
begin
BitFieldStructure := TBitFieldStructure.Create(0);
So why not use a class instead of record? The answer is that a class is just a pointer we can never pass this to a function, procedure or api call that expects a record. But if we want to support older Delphi versions, like Delphi 6 or Delphi 7 and even Delphi 2005, which are still used a lot we need to find another solution. I came up with (ab)using sets to emulate bitfields, we can do this because a set is actually a set of bits (limited to 256 bits). The example structure could look like this if we use sets:
We can use normal set operations to get and set bitvalues:
Settings like minimal enum size and record alignment are important because we need to asssure that te record size matches the C structure’s size (especially when using structures with a lot of bitfields. I choose to do this with a litte trick, first I declare some constants:
We use these constants to force the correct size, in the example the bitfield was a ULONG which is 32 bits. We add the al32Bit constant to the bitfield:
So I thought I had it figured out… until I came to this line in the C header file:
So we have a bitfield consisting off multiple bits! This gave me some headaches but I finally came up with the following approach
We need a helper function to retreive the numeric value of ColorDepth:
// A Delphi Set contains at most 256 bits. So we raise Exception is we exceed
if StartBit + Count > 255 then Raise
EIntOverflow.Create(‘Startbit + Count cannot exceed maximum set size (255)’);
Result := 0;
BitValue := 1;
// A Delphi Set Of can hold a maximum of 256 bits, since we do not know
// which size was passed to us we cast to 256 bits.
MaxBitSet := TMaxBitSet(ABitSet);
// Loop through the requested bits from end to start (Little Endian)
for i := StartBit+Count-1 downto StartBit do
begin
// is the bit set?
if i in MaxBitSet then
begin
// Multiply with BitValue and add to result
Result := Result + BitValue;
end;
// Multiply BitValue by 2
BitValue := BitValue shl 1;
end;
end;
The helper function is used like this:
Some limitations remain, although I don’t think you are likely to encouter these:
10 Responses
Fernando Madruga
01|May|2008 1Nice trick but… isn’t xor a bit dangerous? What if you set the same bit twice without clearing it first? I may have missed something as I skimmed most of the code but it sure looks like an *and* with a proper bitmask would be safer…
Fernando Madruga
01|May|2008 2I said it wrong on my previous comment: the problem happens if you *clear* the same bit twice without setting it in between…
rweijnen
02|May|2008 3@Fernando: You are right!
Correction (also corrected in the article):
procedure _BITFIELDSTRUCTURE.SetBitValue1(const Value: Boolean);
begin
if Value then BitField := BitField or 1 else BitField := BitField and (not 1);
end;
procedure _BITFIELDSTRUCTURE.SetBitValue2(const Value: Boolean);
begin
if Value then BitField := BitField or 2 else BitField := BitField and (not 2);
end;
procedure _BITFIELDSTRUCTURE.SetBitValue3(const Value: Boolean);
begin
if Value then BitField := BitField or 4 else BitField := BitField and (not 4);
end;
procedure _BITFIELDSTRUCTURE.SetBitValue4(const Value: Boolean);
begin
if Value then BitField := BitField or 8 else BitField := BitField and (not 8);
end;
Oliver
02|May|2008 4Christian Wimmer
02|May|2008 5traitor
Remko
03|May|2008 6Are you getting lazy Oliver?
Oliver
03|May|2008 7What do you mean “getting”? It’s one of the prerequisites I demand of everyone who installs my DDKWizard
… only natural to be lazy myself. In fact I think programmers need to be lazy to some extent.
Remko
08|May|2008 8Seems like I left this part out:
type
TMaxBitSet = Set of Byte;
PMaxBitSet = ^TMaxBitSet;
Patrick van Logchem
04|Nov|2009 9Another elegant way to work with bit-fields, is to hide them behind indexed properties. I discovered this trick a while ago (I believe I’m the first to come up with this technique) and wrote about it on the following page :
http://stackoverflow.com/questions/282019/how-to-simulate-bit-fields-in-delphi-records
Good luck with it. Cheers!
Arioch
16|Nov|2011 10And now XE2 has at last “inline” keyword told to work.
Using sets is obvious and natural for this.
But it was not until getters in records that multi-bit fields could be treated with comfort.
Leave a reply
You must be logged in to post a comment.
Search
Paypal donation (EUR)
Download Win 7 Search Provider
Categories
Archives
Tags
Recent Posts
Recent Comments
Blogroll
JEDI Sites
Pages
A design creation of Design Disease
Copyright © 2007 - JEDI Windows API - is proudly powered by WordPress
InSense 1.0 Theme by Design Disease brought to you by HostGator Web Hosting.