Set a FourCC value in C++

9.3k Views Asked by At

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?

12

There are 12 best solutions below

2
On

By using C++11 constexpr you can write something like:

constexpr uint32_t fourcc( char const p[5] )
{
    return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
}

And then use it as:

fourcc( "blah" );

pros:

  • More readable,
  • if the string argument is known at compile time, then the function is evaluated at compile time (no run-time overhead).
  • doesn't depend on endianity (i.e. the first character of the argument will always be in the most significant byte of the fourcc).

cons:

  • Requires c++11 (or later) compiler.
0
On

Nowadays there is a better solution using constexpr and c++17 (maybe earlier, not sure). I'm not sure if its fully cross platform but it works on Visual Studio and XCode.

First, you need a wrapper function to convert functions to compile time values:

template <class TYPE, TYPE VALUE> constexpr TYPE CompileTimeValue() { return VALUE; }

Then you need an constexpr function to convert a short string to a integer:

template <class UINT, UInt32 IS_LITTLE_ENDIAN> constexpr UINT MakeCCFromNullTerminatedString(const char * string)
{
    UINT cc = 0;

    UINT shift = 1;

    if (IS_LITTLE_ENDIAN)
    {
        shift = (sizeof(UINT) == 8) ? (0xFFFFFFFFFFFFFFull + 1) : 0xFFFFFF + 1;
    }

    while (UINT c = *string++)
    {
        c *= shift;

        cc |= c;

        if (IS_LITTLE_ENDIAN)
        {
            shift /= 256;
        }
        else
        {
            shift *= 256;
        }
    }

    return cc;
}

Then wrap in macros, to have both 4 byte and 8 byte character constants, with little and big endian variants (if you want)...

#define ID32(x) CompileTimeValue<UInt32,MakeCCFromNullTerminatedString<UInt32,0>(x)>() 
#define ID64(x) CompileTimeValue<UInt64,MakeCCFromNullTerminatedString<UInt64,0>(x)>()
#define CC32(x) CompileTimeValue<UInt32,MakeCCFromNullTerminatedString<UInt32,1>(x)>()
#define CC64(x) CompileTimeValue<UInt64,MakeCCFromNullTerminatedString<UInt64,1>(x)>()

Some tests to verify..

ASSERT(CC32("test") == 'test');

UInt32 v = CC32("fun");

UInt32 test;

switch (v)
{
case CC32("fun"):
    test = 1;
    break;

case CC32("with"):
    test = 2;
    break;

case CC32("4ccs"):
    test = 3;
    break;
}

Bounds overrun checking is not done, probably could be added with compile time assertions though.

0
On

How about:

#if BYTE_ORDER == BIG_ENDIAN
#define FOURCC(c0,c1,c2,c3) ((uint32) ((((uint32)((uint8)(c0)))<<24) +(((uint32)((uint8)(c1)))<<16)+ (((uint32)((uint8)(c2)))<<8) + ((((uint32)((uint8)(c3)))))) 
#else
#if BYTE_ORDER == LITTLE_ENDIAN
#define FOURCC(c3,c2,c1,c0) ((uint32) ((((uint32)((uint8)(c0)))<<24) +(((uint32)((uint8)(c1)))<<16)+ (((uint32)((uint8)(c2)))<<8) + ((((uint32)((uint8)(c3)))))) 
#else
#error BYTE_ORDER not defined
#endif
#endif
1
On
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)
            )       
    );
}
1
On

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)) );
}
2
On

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.

0
On

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

9
On

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.

7
On

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.

1
On

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

1
On

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.

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