How use bit/bit-operator to control object state?
Asked Answered
D

3

6

I want to create light object data-package to pass between client and server applications.

It is a so simple task, that I can control with only 1 byte, so each bit in a byte will have a different meaning,

Using only the bit

0 = False 
1 = True

Itens I need now:

1 - Loaded from database 
2 - Persisted
3 - Changed
4 - Marked to Delete
5 -
6 - 
7 - Null Value 
8 - Read Only


1) How do I use bit operators in Delphi to check each bit value? 
2) How do I set the bit Values?

Solution

After all help, Ill use the next Set

  TStateType = (
    stLoaded    = 0,   // loaded from persistance
    stNative    = 2,   // value loaded and converted to native type
    stPersisted = 3,   // saved
    stChanged   = 4,   // object or member changed
    stToDelete  = 5,   // marked to delete
    stReadOnly  = 6,   // read only object, will not allow changes
    stNull      = 7    // value is null
  );
  TState = Set of TStateType;

And for stream -> persistance, this will be the record to be used:

  TDataPackage = record
    Data: TBytes;
    TypeInfo: TMetaInfo;
    State: Byte;
    Instance: TBuffer;
  end;

Thank you guys, for all the answers and comments.

Domel answered 5/2, 2009 at 16:25 Comment(3)
Sets are a better technique than direct bitwise operators. They are no less "light", since set operations compile down to the same machine code as binary operations. The advantage is that they are type-safe and checked by the compiler, whereas bitwise operations are all on integers.Catkin
So if you have a set with a maximum of 8 possible members, the type will be 1 byte in size.Catkin
I asked a related question here: "Is it faster to use an array or bit access for multiple boolean values? "Woolfolk
M
8

I'd really use a set for this. However, I see you really want a byte. Use sets everywhere then typecast to a byte in the end.

This solution will require much less typing, has support for standard delphi operators and really carries no performance penalty as Barry Kelly has pointed out.

procedure Test;
type
  TSetValues = (
    TSetValue1   = 0,
    TSetValue2   = 1,
    TSetValue4   = 2,
    TSetValue8   = 3,
    TSetValue16  = 4,
    TSetValue32  = 5,
    TSetValue64  = 6,
    TSetValue128 = 7
  );

  TMySet = set of TSetValues;
var
  myValue: byte;
  mySet: TMySet;
begin
  mySet := [TSetValue2, TSetValue16, TSetValue128];
  myValue := byte(mySet);
  ShowMessage(IntToStr(myValue)); // <-- shows 146
end;
Michaud answered 5/2, 2009 at 17:8 Comment(1)
This is the winner, I think a little refactory will help me to improve my existing code.Domel
E
6

I would use a set for this:

type
    TMyDatum = (mdLoaded, mdPersisted, mdChanged, mdMarkedToDelete, ...);
    TMyData = set of TMyDatum;

var
  Foo: TMyData;
begin 
  Foo := [mdLoaded, mdChanged];
  if (mdPersisted in Foo) then ...

These are implemented as integers, so you can pass them easily. And I find the code much, much more readable than bitwise operators.

Electrocardiograph answered 5/2, 2009 at 16:46 Comment(4)
@Craig, this is what I have today. As I pointed in my question, it must be more light, and Im not sure about the size of a enumerated type. Also to stream 1 Byte, is more easy than stream Enumerated types. The code to test the bit values will be embedded inside a TStatus object, like Status.IsLoadedDomel
Cesar - a set has a size of 1, 2, 3, 4 etc. bytes, however many necessary to fit in all the bits required, one bit per possible member of the set. Sets are directly equivalent to bitwise binary operations.Catkin
Cesar: It ain't broke, then. Don't "fix" it!Electrocardiograph
@Barry, Thanks now I got how this really works, and learned I can cast to Byte.Domel
P
1

This page describes Delphi operators, including bitwise operators.

It sounds like you need to use the and operator. For example:

const
  LOADED_FROM_DATABASE = 1;
  PERSISTED = 2;
  CHANGED = 4;
  // etc...

//...

if (bitFlags and LOADED_FROM_DATABASE) <> 0 then
begin
  // handle LOADED FROM DATABASE
end;

if (bitFlags and PERSISTED) <> 0 then
begin
  // handle PERSISTED
end;

// etc...

In order to set the flags, you can use OR:

bitFlags := LOADED_FROM_DATABASE or PERSISTED or CHANGED;
Probate answered 5/2, 2009 at 16:36 Comment(2)
Sets are a better primitive for this in Delphi, as they are part of the syntax and semantics of the language, yet compile down to the same machine-code operations.Catkin
@Barry, I didn't even think of that, great idea!Probate

© 2022 - 2024 — McMap. All rights reserved.