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:

typedef struct _BITFIELDSTRUCTURE {
    DWORD dwValue1;
    ULONG BitValue1: 1;
    ULONG BitValue2: 1;
    ULONG BitValue3: 1;
    ULONG BitValue4: 1;
} BITFIELDSTRUCTURE, * BITFIELDSTRUCTURE;

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:

type
  _BITFIELDSTRUCTURE = record
  dwValue1: DWORD;
  strict private
    BitField: DWORD;
    function GetBitValue1: Boolean;
    function GetBitValue2: Boolean;
    function GetBitValue3: Boolean;
    function GetBitValue4: Boolean;
    procedure SetBitValue1(const Value: Boolean);
    procedure SetBitValue2(const Value: Boolean);
    procedure SetBitValue3(const Value: Boolean);
    procedure SetBitValue4(const Value: Boolean);
  public
    property BitValue1: Boolean read GetBitValue1 write SetBitValue1;
    property BitValue2: Boolean read GetBitValue2 write SetBitValue2;
    property BitValue3: Boolean read GetBitValue3 write SetBitValue3;
    property BitValue4: Boolean read GetBitValue4 write SetBitValue4;
  end;
  TBitFieldStructure = _BITFIELDSTRUCTURE;
  PBitFieldStructure = ^_BITFIELDSTRUCTURE;

Code completion shows that the record has one DWORD Value and 4 Boolean Values which is just what we want!
CodeCompletion

Offcourse we need to implement the Getters and Setters:

function _BITFIELDSTRUCTURE.GetBitValue1;
begin
  Result := BitField and 1 = 1;
end;

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:


  public
    constructor Create(const dummy: word);
    …

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:

 _BITFIELDSTRUCT = record
  dwValue1: DWORD;
  BitField: Set Of (
    BitValue1, BitValue2, BitValue3, BitValue4
  );
  end;
  TBitFieldStruct = _BITFIELDSTRUCT;
  PBitFieldStruct = ^_BITFIELDSTRUCT;

We can use normal set operations to get and set bitvalues:

procedure TForm1.Button2Click(Sender: TObject);
var
  bValue: Boolean;
  BitFieldStruct: TBitFieldStruct;
begin
  bValue := BitValue2 in BitFieldStruct.BitField;
  BitFieldStruct.BitField := [BitValue1, BitValue3];
  BitFieldStruct.BitField := BitFieldStruct.BitField[BitValue3];
end;

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:

const
  al32Bit=31;
  al64bit=63;
  al96bit=95;
  al128bit=127;
  al160bit=159;
  al192bit=191;
  al224bit=221;
  al256bit=255;

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:

 _BITFIELDSTRUCT = record
  dwValue1: DWORD;
  BitField: Set Of (
    BitValue1, BitValue2, BitValue3, BitValue4, al32Bit
  );
  end;
  TBitFieldStruct = _BITFIELDSTRUCT;
  PBitFieldStruct = ^_BITFIELDSTRUCT;

So I thought I had it figured out… until I came to this line in the C header file:

ULONG   SomeValue  : 1;
ULONG   OtherValue : 1;
ULONG   ColorDepth : 3;

So we have a bitfield consisting off multiple bits! This gave me some headaches but I finally came up with the following approach

 BitField: Set Of (
    SomeValue, OtherValue, ColorDepth1, ColorDepth2, ColorDepth3, al32Bit
  );

We need a helper function to retreive the numeric value of ColorDepth:

function ValueFromBitSet(var ABitSet; const StartBit: Byte;
  const Count: Byte): Int64;
var
  MaxBitSet: TMaxBitSet;
  i, BitValue: Integer;
begin
  // The result can contain max. 64 bit value, Raise if Count > 64
  if Count > 64 then Raise EIntOverflow.Create(‘Count cannot exceed 64′);

  // 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:

Struct.BitFields := [OtherValue, ColorDepth1, ColorDepth3];
WriteLn(Format(‘Value=%d’, [ValueFromBitSet(Struct.BitFields, Integer(ColorDepth1), 3)]));
end.

Some limitations remain, although I don’t think you are likely to encouter these:

  • A Delphi Set can contain at most 256 values.
  • The ValueFromBitSet function returns an Int64, so values that do not fit in an Int64 cannot be returned.
  • Values in a Set need a unique name.