Sign in to follow this  
codingo

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

Recommended Posts

codingo    119

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...");

Share this post


Link to post
Share on other sites
Brother Bob    10344

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.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
codingo    119
RemoveEmptySegments | RemoveWhitespacePadding | PreserveTextInQuotes

 

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

Share this post


Link to post
Share on other sites
Cosmic314    2002

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

Share this post


Link to post
Share on other sites
codingo    119

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?

Share this post


Link to post
Share on other sites
Cosmic314    2002

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
Cosmic314    2002


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?

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites
Cosmic314    2002

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

Share this post


Link to post
Share on other sites
Khatharr    8812
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.

Share this post


Link to post
Share on other sites
RobTheBloke    2553

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.... 

 

[source]

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)
};
[/source]
 
personally I prefer shifts for flag values:
 
[source]
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)
};
[/source]

Share this post


Link to post
Share on other sites
RobTheBloke    2553

 

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

Share this post


Link to post
Share on other sites
Unnamed User    575
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

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this