Recent

Author Topic: sets as bitmasks typecast to longword  (Read 5218 times)

damieiro

  • Full Member
  • ***
  • Posts: 200
sets as bitmasks typecast to longword
« on: October 11, 2021, 08:42:50 am »
Hi!

https://wiki.freepascal.org/Set

Code: Pascal  [Select][+][-]
  1.  
  2. Sets can be used to create bitmasks as shown in the example.
  3.  
  4. (*
  5.   The set [FLAG_A, FLAG_C] will be stored like this:
  6.  
  7.   TFlags :   0000'0101
  8.                    │││
  9.   FLAG_C ──────────┘││
  10.   FLAG_B ───────────┘│
  11.   FLAG_A ────────────┘
  12. *)
  13.  
  14. type
  15.   TFlag = (FLAG_A, FLAG_B, FLAG_C);
  16.   TFlags = set of TFlag;
  17.  
  18. var
  19.   Flags: TFlags;
  20.  
  21.  
  22. [..]
  23.   Flags:= [FLAG_A, FLAG_C];
  24.   if FLAG_A in Flags then ..  // check FLAG_A is set in flags variable
  25.  
  26.  

The main question is for sets with 32 elements or less (or other number) is there a way to typecast directly the value as a longword or longint or similar (like a lot of flags variables) within languaje construct. It seems that for set properties (i'm not testing it) can be used getsetprop

Example (illegal)
FlagsWord:= longword (Flags); or FlagsWord:=settoword (flags);

otherwise, i would know if it's safe some kind of addressing like

Code: Pascal  [Select][+][-]
  1.  Flagsword:longword absolute flags;   // taking same adress as flags. some kind of brute typecasting

Using sets as bitmasks is very handy, but it's needed it's conversion if making calls for external routines that only accept things like dword





MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: sets as bitmasks typecast to longword
« Reply #1 on: October 11, 2021, 08:55:21 am »
Code: [Select]
program test;

type
  TFlag = (FLAG_A, FLAG_B, FLAG_C);
  TFlags = set of TFlag;
 
var
  Flags: TFlags;
  num: longword;

begin
  flags := [FLAG_A, FLAG_C];
  num := longword(flags);
  WriteLn(num)
end.

$ fpc test.pas
Free Pascal Compiler version 3.2.0 [2020/11/20] for x86_64
Copyright (c) 1993-2020 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling test.pas
Linking test
16 lines compiled, 0.3 sec

$ ./test
5

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Thaddy

  • Hero Member
  • *****
  • Posts: 14373
  • Sensorship about opinions does not belong here.
Re: sets as bitmasks typecast to longword
« Reply #2 on: October 11, 2021, 09:01:00 am »
Mark: as long as there are no specific defaults at declaration: Yes.

More in general: as of 3.2 there are helpers for bit manipulation. And with bits, always assume unsigned types unless you want the sign bit.
In trunk, those have even be usefully expanded over mine by our forum member Avra.
(Tnx Avra!) Most of both our code is inlined code, so very fast, as efficient as direct manipulation.
« Last Edit: October 11, 2021, 09:44:28 am by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: sets as bitmasks typecast to longword
« Reply #3 on: October 11, 2021, 10:30:05 am »
I was doing a basic check pending more details.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

damieiro

  • Full Member
  • ***
  • Posts: 200
Re: sets as bitmasks typecast to longword
« Reply #4 on: October 11, 2021, 11:37:58 am »
ok, typecast with longword/longint works as expected.
It doesn't typecast failed for other types (word, byte), it seems only typecast to 32 bit types.

@Thaddy: i have never heard of helpers for bit manipulation.

The example code in with i'm using flags it's this calling SDL_TTF dll;
Code: Pascal  [Select][+][-]
  1.  
  2. tFontStyleFlags= (ftBold,ftItalic,ftUnderline,ftStrikethrough);
  3. tFontStyle = set of tFontStyleFlags;
  4. {
  5. SDL Constants:
  6. TTF_STYLE_NORMAL;          //0x00
  7.   TTF_STYLE_BOLD;            //0x01
  8.   TTF_STYLE_ITALIC;          //0x02
  9.   TTF_STYLE_UNDERLINE;       //0x04
  10.   TTF_STYLE_STRIKETHROUGH;   //0x08}          
  11.  
  12.  
  13. {*and then you call SDL Font Style like this}
  14.  
  15. Var
  16. FontStyle:tFontStyle
  17.  
  18. begin
  19. {Do things}
  20.  
  21. //do SDL call
  22.   TTF_SetFontStyle  ( pointerToFont, longword(fontstyle));  
  23.  
  24.  

I do not need the fastest implementation, i prefer using sets and i think compilator would give the best code with the bitwise operations if in a set. And code is far more readable with sets

note: code is more data-wise. Flags are a property in a class, so their read and set are made by their getter and setter and if there is a change make the SDL call if necessary.

I do not know if i can "weight" each set value, to have all the sum of their components and not worryng for typing these in the Correct order.

Fot example.
This is legal.

Code: Pascal  [Select][+][-]
  1. tFontStyleFlags= (ftNormal:=TTF_STYLE_NORMAL,
  2.           ftBold:=TTF_STYLE_BOLD,
  3.           ftItalic:=TTF_STYLE_ITALIC,
  4.           ftUnderline:=TTF_STYLE_UNDERLINE,
  5.           ftStrikeThrough:=TTF_STYLE_STRIKETHROUGH);  
  6.  
but the typecast would fail because ftNormal takes the lowest byte, so i would need to loop and making a sum of the elements... This option will be safe in the way it doesn't matter the order i would declare in my set, but i would like some compiler magic to know the sum of a set.
and the above tipecast would fail if i would declare flags in wrong order. And the "Normal" flag would be a empty set...

I go for readable code :D


« Last Edit: October 11, 2021, 11:57:27 am by damieiro »

Thaddy

  • Hero Member
  • *****
  • Posts: 14373
  • Sensorship about opinions does not belong here.
Re: sets as bitmasks typecast to longword
« Reply #5 on: October 11, 2021, 11:51:38 am »
@Thaddy: i have never heard of helpers for bit manipulation.
Well, by now you know. Anyway there was a big discussion(s) on this forum and the bugtracker, mainly by Bart (very helpful debugging), Avra and me. I do not know the status of its documentation, though. Simple features like this may get under the radar of our "benevolent documentor for life", Michaël Van Canneyt.

They are in sysutils.
« Last Edit: October 11, 2021, 12:10:11 pm by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

damieiro

  • Full Member
  • ***
  • Posts: 200
Re: sets as bitmasks typecast to longword
« Reply #6 on: October 11, 2021, 12:05:52 pm »
I have seen it
https://bitbucket.org/avra/bithelpers/src/master/bithelpers.pas

But it's not what i need. I go for using set as a aliases for common function calling.


MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: sets as bitmasks typecast to longword
« Reply #7 on: October 11, 2021, 12:10:12 pm »
ok, typecast with longword/longint works as expected.
It doesn't typecast failed for other types (word, byte), it seems only typecast to 32 bit types.

Have you investigated https://www.freepascal.org/docs-html/current/prog/progsu61.html#x68-670001.2.61 ?

There is a fundamental question here: it could be argued that since a set doesn't have an implicit numeric representation then its value can't be checked before a cast to a smaller type.

As an historical note, Modula-2 had a BITSET type which was explicitly defined as having the size of the host machine word, while ordinary set were not. Unfortunately, and characteristically, Wirth didn't specify which end of the machine word was identified by bit zero. And as another historical note, I've seen multiple machine architectures where the hardware designers called the MSB 0 while the system software programmers called the LSB 0.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Thaddy

  • Hero Member
  • *****
  • Posts: 14373
  • Sensorship about opinions does not belong here.
Re: sets as bitmasks typecast to longword
« Reply #8 on: October 11, 2021, 12:13:35 pm »
I have seen it
https://bitbucket.org/avra/bithelpers/src/master/bithelpers.pas

But it's not what i need. I go for using set as a aliases for common function calling.
No you did not!. The code from Avra is merged in trunk and different from that link.
What do you really need? Example plz...
I think you are wrong footed, but that is just a guess.
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

damieiro

  • Full Member
  • ***
  • Posts: 200
Re: sets as bitmasks typecast to longword
« Reply #9 on: October 11, 2021, 12:57:51 pm »
thx.
But i think i solved it :)

I did this (spanish, but i think it's ok)
Code: Pascal  [Select][+][-]
  1. tflagEstiloFuente= (ftNegrita,ftCursiva,ftSubrayado,ftTachado);
  2.  
  3. { TTF_STYLE_NORMAL;          //0x00
  4.   TTF_STYLE_BOLD;            //0x01  Negrita
  5.   TTF_STYLE_ITALIC;          //0x02  Cursiva
  6.   TTF_STYLE_UNDERLINE;       //0x04 Subrayado
  7.   TTF_STYLE_STRIKETHROUGH;   //0x08 Tachado}
  8.  
  9. setFlagsEstiloFuente = set of tFlagEstiloFuente;
  10.  
  11. {And then do the call to SDL like this}
  12.  
  13. procedure SeleccionarEstiloFuenteTTF (var pfuente:pfuenteTTF; estilo:setFlagsEstiloFuente);
  14. // tflagEstiloFuente= (Negrita,Cursiva,Subrayado,Tachado) en graficos.tipos ;)
  15.  
  16. var
  17.   lwFlags:longword;
  18. begin
  19.  
  20.   lwFlags:=longword(estilo);
  21.   TTF_SetFontStyle  ( pFuente, lwflags);
  22.  
  23. end;  
  24.  
  25.  


it works also

Code: Pascal  [Select][+][-]
  1. procedure SeleccionarEstiloFuenteTTF (var pfuente:pfuenteTTF; estilo:setFlagsEstiloFuente);
  2. var
  3.   lwFlags:longword absolute estilo;
  4. begin
  5.   TTF_SetFontStyle  ( pFuente, lwflags);
  6. end;  


And i like quick (but no dirty) options. This is fast and readable.
« Last Edit: October 11, 2021, 01:17:41 pm by damieiro »

damieiro

  • Full Member
  • ***
  • Posts: 200
Re: sets as bitmasks typecast to longword
« Reply #10 on: October 11, 2021, 01:12:57 pm »
The main issue is that
Code: Pascal  [Select][+][-]
  1. tflagEstiloFuente= (ftNegrita,ftCursiva,ftSubrayado,ftTachado);

if i would type in wrong order (by error)
Code: Pascal  [Select][+][-]
  1. tflagEstiloFuente= (ftTachado, ftNegrita,ftCursiva,ftSubrayado);

it would be a fail.
I could enforce it by this

Code: Pascal  [Select][+][-]
  1. tflagEstiloFuente= (ftNegrita:=0,ftCursiva:=1,ftSubrayado:=2,ftTachado:=3);
so if i type

Code: Pascal  [Select][+][-]
  1. tflagEstiloFuente= (ftTachado:=3,ftNegrita:=0,ftCursiva:=1,ftSubrayado:=2);

it would work.

But i cannot enforce order by value of constants, which would be really nice...

Code: Pascal  [Select][+][-]
  1. tflagEstiloFuente=
  2. (ftNegrita:=TTF_STYLE_BOLD,{0x01}
  3. ftCursiva:= TTF_STYLE_ITALIC, {0x02}
  4. ftSubrayado:=TTF_STYLE_UNDERLINE, {0x04}
  5. ftTachado:= TTF_STYLE_STRIKETHROUGH {0x08});)

Because compiler takes the enum type as a power of two (for example a [ftNegrita,ftSubrayado] is 18 with this definition, and not 5 which is intended). So, if enforced order, it doesn't do a sort, it takes the literal place of the bit...










MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: sets as bitmasks typecast to longword
« Reply #11 on: October 11, 2021, 01:20:48 pm »
The main issue is that
Code: Pascal  [Select][+][-]
  1. tflagEstiloFuente= (ftNegrita,ftCursiva,ftSubrayado,ftTachado);

if i would type in wrong order (by error)
Code: Pascal  [Select][+][-]
  1. tflagEstiloFuente= (ftTachado, ftNegrita,ftCursiva,ftSubrayado);

it would be a fail.

But why would you possibly do that? That's an enumerated type definition, so there should only be one of them globally.

The one place where I've found that sort of thing to be a problem is if for some reason one wants to extend an enumerated type e.g. as a status code associated with a subclass.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

damieiro

  • Full Member
  • ***
  • Posts: 200
Re: sets as bitmasks typecast to longword
« Reply #12 on: October 11, 2021, 01:24:40 pm »
ok, typecast with longword/longint works as expected.
It doesn't typecast failed for other types (word, byte), it seems only typecast to 32 bit types.

Have you investigated https://www.freepascal.org/docs-html/current/prog/progsu61.html#x68-670001.2.61 ?

There is a fundamental question here: it could be argued that since a set doesn't have an implicit numeric representation then its value can't be checked before a cast to a smaller type.

As an historical note, Modula-2 had a BITSET type which was explicitly defined as having the size of the host machine word, while ordinary set were not. Unfortunately, and characteristically, Wirth didn't specify which end of the machine word was identified by bit zero. And as another historical note, I've seen multiple machine architectures where the hardware designers called the MSB 0 while the system software programmers called the LSB 0.

MarkMLl

Yep. And for me it's ok to typecast to longword allways (it catches all flags needed for external libraries)
It have glitches. For example in delphi mode, the set size varies like a bitpacked one.
  -> https://www.freepascal.org/docs-html/prog/progsu161.html

So i would try to make a "more or less " safe type cast. If in free pascal mode it's ok for me a guaranteed 32 bit set if 32 or less flags.
And i would think thay endianness it's ok... for longword/cardinal types.







damieiro

  • Full Member
  • ***
  • Posts: 200
Re: sets as bitmasks typecast to longword
« Reply #13 on: October 11, 2021, 01:36:06 pm »
Quote
But why would you possibly do that? That's an enumerated type definition, so there should only be one of them globally.

The one place where I've found that sort of thing to be a problem is if for some reason one wants to extend an enumerated type e.g. as a status code associated with a subclass.

MarkMLl

If has several (not critical problems)

Examples:

tflags= (flagA,flagB,flagC,flagD,flagE);

I could copy it bad and then i have not FlagD and FlagE is bad weighted. And i couldn't ever note it if flag D is never used or deprecated... And deprecate is useful
tflags= (flagA,flagB,flagC,flagE);

Other is that one can argue than flags could be in reverse order
tflags= (flagE, flagD, flagC, flagB, flagA) like binary positions.

but if defined like this (this is valid, but must start in 0 and increased by 1, the value, really indicates their bit position)

tflags = (flagA:=0, flagB:=1, flagC:=2, flagD:=3, flagE:=4)

you can deprecate flag D
tflags =(flagA:=0, flagB:=1, flagC:=2, flagE:=4)

you can reverse order
tflags =(flagE:=4,flagD:=3,flagC:=2, flagB:=1,flagA:=0);
« Last Edit: October 11, 2021, 01:38:45 pm by damieiro »

MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: sets as bitmasks typecast to longword
« Reply #14 on: October 11, 2021, 02:06:59 pm »
If has several (not critical problems)

Examples:

tflags= (flagA,flagB,flagC,flagD,flagE);

I could copy it bad and then i have not FlagD and FlagE is bad weighted.

But /why/ are you copying it? That definition ought to be global and /never/ redefined... I have to conclude that you are, basically, abusing the language since the intention of enumerations (unlike subranges) was that they were purely symbolic and didn't have an implicit ordering: they were basically there as the foundation type for sets.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

 

TinyPortal © 2005-2018