tags:

views:

556

answers:

9

I want to set a FourCC value in C++, i.e. an unsigned 4 byte integer.

I suppose the obvious way is a #define, e.g.

#define FOURCC(a,b,c,d) ( (uint32) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a)) )

and then:

uint32 id( FOURCC('b','l','a','h') );

What is the most elegant way you can think to do this?

+1  A: 

I see nothing wrong with your algorithm. But for something like this I would just write a function instead of a macro. Macros have a lot of hidden features / problems that can bite you over time.

uint FourCC(char a, char b, char c, char d) { 
  return ( (uint32) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a)) );
}
JaredPar
Functions can't be used as compile-time constants. That means switch statements are not possible with a function. Either a macro or a template class (using a static const int or enum for the result, but not a function) should be fine.
Tom
+5  A: 

or do the same with an inline function

inline uint32_t FOURCC(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
{
     return ( (uint32) (((d)<<24) | (uint32_t(c)<<16) | (uint32_t(b)<<8) | uint32_t(a)) )
}

and avoid the headaches of a macro, but otherwise your approach looks fine to me.

Doug T.
I'd lose the extra paranthesis in the function version. :)
Brian Neal
Biggest downside is that you can't use an inline function for a case statement in a switch block. Either a template structure or a macro would work for that, though.
Tom
A: 

Rather than a #define, I'd probably put pretty much the same code and rely on the compiler to inline it.

Richard
+11  A: 

You can make it a compile-time constant using:

template <int a, int b, int c, int d>
struct FourCC
{
    static const unsigned int value = (((((d << 8) | c) << 8) | b) << 8) | a;
};

unsigned int id(FourCC<'a', 'b', 'c', 'd'>::value);

With a little extra effort, you can make it check at compile time that each number passed in is between 0 and 255.

James Hopkin
very nice if the value is constant at compile time +1
Doug T.
Ok, that's more intesting. :)
Nick
Any non-toy compiler will constant-fold the macro at compile time.
Dave
@Dave: that's true. This is really just a way of avoiding the macro without losing its advantages. The other replies suggesting inline functions no longer give you a compile-time constant, which may be handy. This gives some potential extra safety, but who's going to put the wrong thing in a FOURCC? To be honest, I'm sure if I'd bother with this myself, but I thought I'd put it out there.
James Hopkin
template <uint8_t a, uint8_t b, uint8_t c, uint8_t d> might do the 0..255 check for you.
Berkus
A: 

If I am not mistaken, you can just use multi-character character constants for that right?

unsigned int fourCC = 'blah';

This is perfectly valid by the ANSI/ISO specification though some compilers will complain a little. This is how resource types used to be handled in the older Macintosh APIs.

D.Shawley
I think these are implementation defined and aren't necessarily portable.
Brian Neal
In other words, you may not be sure where the 'b' is going to end up, high order byte or low order.
Brian Neal
Additionally, I think some compilers may interpret the 'blah' as a byte and not a 32 bit integer. I think the standard says it should be interpretted as an int.
Nick
I'll have to look this one up... FWIW, remember that 'a' is an integer constant not a character. I'll add another comment once I track this one down in the spec.
D.Shawley
Nice catch... I just checked the spec and it is implementation defined. In that case, I would opt for the inline function case.
D.Shawley
+1  A: 

Assuming Windows (as FOURCC is a Windows concept), the Win API already provides mmioStringToFOURCC and mmioFOURCC.

Stu Mackellar
Thanks. mmioFOURCC is a macro that expands to the same #define I have above.
Nick
+1  A: 

If a compile-time constant isn't required, perhaps the neatest is

unsigned int FourCCStr(const char (&tag)[5])
{
    return (((((tag[3] << 8 ) | tag[2]) << 8) | tag[1]) << 8) | tag[0];
}

#define FOURCC(tag) FourCCStr(#tag)

unsigned int id(FOURCC(blah));

This only accepts tags of four characters, as required.

James Hopkin
Or: typedef char tag[5]; uint32 FourCC(const tag
Nick
@Brian You might be able to delete your comment now :-)
James Hopkin
+4  A: 
uint32_t FourCC = *((uint32_t*)"blah");

Why not this?

EDIT: int -> uint32_t.

And no it does not cast a char** to uint32_t. It casts a (char*) to (uint32_t*) then dereferences the (uint32_t*). There is no endian-ness involved, since its assigning an uint32_tto an uint32_t. The only defects are the alignment and the I hadn't explicitly indicated a 32bit type.

Sanjaya R
Because the value varies between big- and little-endian machines. On some architectures, that may be misaligned, resulting in a hardware exception at runtime.
Tom
I had considered this way and I believe it would work on all platforms we support. None of the proposals deal with endianess. TBH this isn't a concern as it will be platform specific data.
Nick
@James Hopkin: This isn't converting the pointer to an int, it converts the pointer to an int POINTER, then dereferences it. The value will always end up the same as if you used a union between 4 chars and an int and assigned the chars to {'b','l','a','h'}. On the topic of endianess, FOURCC codes should be in the same order in memory (http://msdn.microsoft.com/en-us/library/dd375802(VS.85).aspx alludes to this when it mentions little-endianess on Windows platforms)
Grant Peters
A: 
uint32 fcc(char * a)
{   
    if( strlen(a) != 4)
     return 0;  //Unknown or unspecified format

    return 
    (
      (uint32) 
      ( 
       ((*(a+3))<<24) |
       ((*(a+2))<<16) |
       ((*(a+1))<<8) | 
       (*a)
      )  
    );
}
plan9assembler
strlen?! Sorry, I want this to be a compile time calculation.
Nick
TheDailyWTF version.
Berkus