Jump to content

  • Log In with Google      Sign In   
  • Create Account

about style - why use hex instead of int in this function


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
17 replies to this topic

#1 sweetRum   Members   -  Reputation: 119

Like
0Likes
Like

Posted 24 September 2013 - 02:23 PM

this function is declared as such:

#define FLAGS_NONE (0x00000000)
#define FLAGS_01   (0x10000000)
#define FLAGS_02   (0x20000000)

...

contextSetString(int flags, API_FIELDS field, char *value);

When it is called, it looks like this:

contextSetString(FLAGS_NONE, API_ENVELOPE_FIELD, "fopfsmvfdp...");


Sponsor:

#2 Brother Bob   Moderators   -  Reputation: 8251

Like
2Likes
Like

Posted 24 September 2013 - 02:30 PM

Hexadecimal values are also integers. They just happen to have a written text-form that is more beneficial than decimal values when it comes to representing values where the individual bits of the value has a meaning. It is almost directly apparent which bits are set in a value from its hexadecimal notation than its decimal notation.



#3 Servant of the Lord   Crossbones+   -  Reputation: 19665

Like
4Likes
Like

Posted 24 September 2013 - 02:40 PM

It's not hexadecimal instead of ints, hexadecimal is a way to represent ints just like decimal is. It's hexadecimal instead of decimal or octal or binary - but all four of those produce integers.
 
If C++ supported native binary literals, that would be even more preferable (note: binary literals will be in the new C++14 standard).
 
With binary literals, it'd look like:

enum Flags {None  =      0b,
            Flag1 =      1b,
            Flag2 =     10b,
            Flag3 =    100b,
            Flag4 =   1000b,
            Flag5 =  10000b,
            Flag6 = 100000b,
            DefaultFlags = (Flag3 | Flag2 | Flag6),
            etc... };

It more clearly displays that each flag takes up only a single bit, and shows what bit they take.

They aren't just going 1, 2, 3, 4, like normal enums or constants, because normal enums can only be used one at a time; either you're using EnumValueA, or you're using EnumValueB.

With bit flags, you want to be able to use multiple flags at once - any combination that makes sense.
So you do: (Flag2 | Flag5 | Flag3) using bitwise OR operator, not the && logical AND operator.
 
Regardless of the order OR'd together (Flag2 | Flag5 | Flag3) becomes 10110b.
 
Because of this great feature, every flag needs to take one single bit that it exclusively uses.
 
Here's an example paraphrased from my own code library:

StringList results = Seperate("red,   green   , , blue, \"text with, comma in it\"", ',', RemoveEmptySegments | RemoveWhitespacePadding | PreserveTextInQuotes);

results = {"red", "green", "blue", "text with, comma in it"}

Edited by Servant of the Lord, 26 September 2013 - 12:31 PM.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#4 sweetRum   Members   -  Reputation: 119

Like
0Likes
Like

Posted 24 September 2013 - 04:17 PM

RemoveEmptySegments | RemoveWhitespacePadding | PreserveTextInQuotes

 

So would those flags be equal to:  PreserveTextInQuotes | RemoveEmptySegments | RemoveWhitespacePadding ?



#5 SiCrane   Moderators   -  Reputation: 9604

Like
1Likes
Like

Posted 24 September 2013 - 04:21 PM

For integral types, bitwise-or is both commutative and associative, so yes.



#6 Cosmic314   Members   -  Reputation: 1215

Like
0Likes
Like

Posted 24 September 2013 - 04:57 PM

Hexadecimal also allows for easy decoding of bits:

0x0 = 0000   0x8 = 1000
0x1 = 0001   0x9 = 1001
0x2 = 0010   0xA = 1010
0x3 = 0011   0xB = 1011
0x4 = 0100   0xC = 1100
0x5 = 0101   0xD = 1101
0x6 = 0110   0xE = 1110
0x7 = 0111   0xF = 1111


Each hex digit represents four bits.  If you have a representation of groups of 4 bits, such as a 32 or 64 bit number, the mental translation is easier to compute.  For example, if you need to convert to bits it is just a matter of lookup on this table:

0xFEDCBA98 is:

1111 1110 1101 1100 1011 1010 1001 1000
F    E    D    C    B    A    9    8

The unsigned decimal representation of this number is:

 

4275878552

 

As an exercise, convert this decimal number to binary by hand.  It's not a difficult mathematical problem but certainly more involved than simply looking up digits in a table.  As folks have already mentioned, seeing hex should sort of train your brain to think of the data in terms of flag bits rather than as a mathematical number.


Edited by Cosmic314, 24 September 2013 - 05:06 PM.


#7 Khatharr   Crossbones+   -  Reputation: 3007

Like
1Likes
Like

Posted 24 September 2013 - 05:02 PM

 

If C++ supported native binary literals, that would be even more preferable (note: C++ will in the C++14 standard).
 

 

YAHOO!!!!! laugh.png


void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

#8 sweetRum   Members   -  Reputation: 119

Like
0Likes
Like

Posted 24 September 2013 - 05:22 PM

I c, so you could pass a function many flags in any order using hexadecimal form and descriptive #define calls. And what would the syntax look like to parse the haxadecimal to determine which flags where set once in the body of the function?



#9 Cosmic314   Members   -  Reputation: 1215

Like
0Likes
Like

Posted 24 September 2013 - 05:57 PM

The parsing depends on what you are given.  Typically you have a mask and possibly a shift value.  For example if you want bits 20:17 of a 32 bit number I'd use a macro:

#define GET_FIELD(d,m,s) ((d & m) >> s)

unsigned field = GET_FIELD(input,0x001E0000,17);  // The mask is probably recorded as a #define somewhere.

If you're only given the mask you can compute the shift:

unsigned get_shift(unsigned mask)
{
   // this routine spins forever if mask = 0x0
   unsigned count = 0;
   while( mask & 0x1 == 0 )
   {
      mask >>= 1;
      ++count;
   }
   return(count);
}

Edited by Cosmic314, 24 September 2013 - 05:57 PM.


#10 sweetRum   Members   -  Reputation: 119

Like
0Likes
Like

Posted 24 September 2013 - 06:19 PM

i c. pretty cool, thank you.



#11 Servant of the Lord   Crossbones+   -  Reputation: 19665

Like
3Likes
Like

Posted 24 September 2013 - 06:20 PM

I c, so you could pass a function many flags in any order using hexadecimal form and descriptive #define calls. And what would the syntax look like to parse the haxadecimal to determine which flags where set once in the body of the function?


If you're using C++, it's considered bad practice to use #define to declare constant values. That was something that was common 20 years ago, but people have learned that it's not a good idea (through many a trial and error - and I don't mean figuratively laugh.png).

Instead, people use 'const int'/'const unsigned' or enums to create permanently unchanging values. #defines have hidden dangers to them can cause bugs if they are overused (they aren't broken - they just sometimes behave in ways people don't expect resulting in obvious-in-hindsight but hard-to-locate bugs). Older books and tutorials, and older code libraries (like Win32) use alot of #defines.

C++ code doesn't need to know the difference between hexadecimal, decimal, octal, and binary. They are all just integers of some kind or another (maybe signed, maybe unsigned, depending on the context). C++ code itself doesn't care what human format we write our numbers insmile.png

 

The compiler, when compiling our code, converts our C++ language code into machine assembly.

In the exact same way, the compiler converts our human numbering systems into whatever format (binary) the computer cares about.

 

Try running this code:

#include <iostream>
using namespace std;

void myFunc(int someValue)
{
     std::cout << "Value: " << someValue << std::endl;
}

int main()
{
	myFunc(255);
	myFunc(0xFF);
	myFunc(0377);
	return 0;
}

There are two things to note here:

  1. The function didn't have to treat hexadecimal values any differently than 'normal' decimal values
  2. The output from the functions were exactly the same (because dec 255 == hex FF == oct 377; the values were equivalent)

The code didn't care at all. From our perspective, we wrote each number differently. From the code's perspective, it was the exact same thing.

These numbering systems can be used interchangeably for different purposes, whenever it is more convenient to write in one system over another.


Edited by Servant of the Lord, 24 September 2013 - 06:27 PM.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#12 Cosmic314   Members   -  Reputation: 1215

Like
0Likes
Like

Posted 24 September 2013 - 06:41 PM


If you're using C++, it's considered bad practice to use #define to declare constant values. That was something that was common 20 years ago, but people have learned that it's not a good idea (through many a trial and error - and I don't mean figuratively ).

Instead, people use 'const int'/'const unsigned' or enums to create permanently unchanging values. #defines have hidden dangers to them can cause bugs if they are overused (they aren't broken - they just sometimes behave in ways people don't expect resulting in obvious-in-hindsight but hard-to-locate bugs). Older books and tutorials, and older code libraries (like Win32) use alot of #defines.

 

Yeah, this is a good point.  Instead of making macros as I had for GET_FIELD, it's probably better to declare an inline function.  In ARM assembly I could rewrite that entire expression as one CPU instruction, so there's a good chance the compiler will do the same.

 

A question for you:  Let's say you have > 1,000,000 constants of which you use only a fraction, let's say 1000.  Will the compiler maintain a symbol table with all 1,000,000 entries or is it smart enough to strip away unused constants?



#13 Servant of the Lord   Crossbones+   -  Reputation: 19665

Like
0Likes
Like

Posted 24 September 2013 - 08:30 PM

A question for you:  Let's say you have > 1,000,000 constants of which you use only a fraction, let's say 1000.  Will the compiler maintain a symbol table with all 1,000,000 entries or is it smart enough to strip away unused constants?

I don't know, optimizations at the assembly level isn't an area I've looked into! mellow.png

I've never really had anywhere near a million constants before - my projects are small hobbyist / indie developer PC games and tools, not AAA console games, so the occasional function-level and architectural-level optimizations are my projects have needed in the past.


It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#14 Cosmic314   Members   -  Reputation: 1215

Like
0Likes
Like

Posted 24 September 2013 - 08:57 PM

I'm a little curious now.  I may have to try an experiment and see what happens.  In my day job I do embedded characterization.  The SoCs we develop have tens of thousands of memory mapped registers which are all defined in a massive include file as a series of defines.  If I convert them to constants and compile as a separate unit it might help with compile times.  But I can't keep all those definitions in the final linked file.  Ironically I don't have enough memory on the SoC itself to hold that number of constants.


Edited by Cosmic314, 24 September 2013 - 08:58 PM.


#15 Khatharr   Crossbones+   -  Reputation: 3007

Like
0Likes
Like

Posted 24 September 2013 - 10:24 PM

Compilers tend to strip things thst aren't used unless they have some reason to expect that it would be bad to do so. You could test it by writing a tiny program that declares a few constants snd then prints one out. Turn on optimization but leave debugging enabled, the just break somewhere and look the used constant up in memory to see if the unused ones are there as well.
void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

#16 RobTheBloke   Crossbones+   -  Reputation: 2342

Like
2Likes
Like

Posted 25 September 2013 - 03:45 AM

If C++ supported native binary literals, that would be even more preferable (note: binary literals will in the C++14 standard).
 

 

Or there is the old enum trick.... 

 

enum BinaryNumber
{
  b00000000,
  b00000001,
  b00000010,
  b00000011,
  b00000100,
  // snip
  b11111100,  
  b11111101,  
  b11111110,  
  b11111111
};
 
enum Flags 
{
  None  = b00000000,
  Flag1 = b00000001,
  Flag2 = b00000010,
  Flag3 = b00000100,
  Flag4 = b00001000,
  Flag5 = b00010000,
  Flag6 = b00100000,
  DefaultFlags = (Flag3 | Flag2 | Flag6)
};
 
personally I prefer shifts for flag values:
 

enum Flags 
{
  None  = 0,
  Flag1 = 1 << 0,
  Flag2 = 1 << 1,
  Flag3 = 1 << 2,
  Flag4 = 1 << 3,
  Flag5 = 1 << 4,
  Flag6 = 1 << 5,
  DefaultFlags = (Flag3 | Flag2 | Flag6)
};


#17 RobTheBloke   Crossbones+   -  Reputation: 2342

Like
1Likes
Like

Posted 25 September 2013 - 03:55 AM

 

A question for you:  Let's say you have > 1,000,000 constants of which you use only a fraction, let's say 1000.  Will the compiler maintain a symbol table with all 1,000,000 entries or is it smart enough to strip away unused constants?

I don't know, optimizations at the assembly level isn't an area I've looked into! mellow.png

I've never really had anywhere near a million constants before - my projects are small hobbyist / indie developer PC games and tools, not AAA console games, so the occasional function-level and architectural-level optimizations are my projects have needed in the past.

 

 

A million constants will only be included in the final exe if you use *each* constant in an assignment or comparison. It's no different to comparing or assigning a million constant int values (an enum is an int). Just because a 32bit integer can store 4 billion+ different values, does not mean those values will automatically end up in the exe. Since both scenarios are highly unlikely, it's not really worth worrying about. Well, I suppose there is one situation. If you happen to have a switch statement will 1 million possibilities, then in theory that might happen. In practice, if the switch-cases are listed in order of smallest -> largest, then the compiler may be able to do something clever with optimisation in that case (i.e. assume an integer expression, and remove the constants entirely). Either way though, this isn't something to worry about. Constants are a good thing, and if you need 1 million of them in a switch statement, then so be it (just don't ask me to maintain the code!)

 

(The above advice assumes C++ is being used. If it's a .NET language, and you're compiling a class library, then things may be different. It's still not worth worrying about though. Chances are 1 million integer constants are still more efficient than 1 million string comparisons!)

 

 

Compilers tend to strip things thst aren't used unless they have some reason to expect that it would be bad to do so. You could test it by writing a tiny program that declares a few constants snd then prints one out. Turn on optimization but leave debugging enabled, the just break somewhere and look the used constant up in memory to see if the unused ones are there as well.

 

There's no need to do this. The constants won't be there. 


Edited by RobTheBloke, 25 September 2013 - 03:56 AM.


#18 Leandro GR   Members   -  Reputation: 520

Like
1Likes
Like

Posted 25 September 2013 - 09:07 AM

I just think it's way easier to write and read this
enum MyFlags
{
    Flag1 = 0x1,
    Flag2 = 0x2,
    Flag3 = 0x4,
    Flag4 = 0x8,
    Flag5 = 0x10,
    Flag6 = 0x20,
    Flag7 = 0x40,
    Flag8 = 0x80
}
Than this
enum MyFlags
{
    Flag1 = 1,
    Flag2 = 2,
    Flag3 = 4,
    Flag4 = 8,
    Flag5 = 16,
    Flag6 = 32,
    Flag7 = 64,
    Flag8 = 128
}
Both ways are easy to do, if I use decimal I just have to double the last value, but with hexadecimal I just have to remember the sequence 1, 2, 4 and 8 as it will just repeat itself over and over, just adding zeros to the end




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS