6

我想在 C++ 中设置一个FourCC值,即一个无符号的 4 字节整数。

我想显而易见的方法是#define,例如

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

接着:

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

你能想到的最优雅的方式是什么?

4

12 回答 12

17

您可以使用以下方法使其成为编译时常量:

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

通过一些额外的努力,您可以在编译时检查传入的每个数字是否在 0 到 255 之间。

于 2009-05-01T13:41:07.737 回答
10
uint32_t FourCC = *((uint32_t*)"blah");

为什么不是这个?

编辑:int-> uint32_t。

不,它不会将 char** 转换为 uint32_t。它将 (char*) 转换为 (uint32_t*),然后取消引用 (uint32_t*)。不涉及字节序,因为它将 uint32_t 分配给 uint32_t。唯一的缺陷是对齐方式,并且我没有明确指出 32 位类型。

于 2009-05-01T14:46:38.903 回答
6

通过使用 C++11 constexpr,您可以编写如下内容:

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

然后将其用作:

fourcc( "blah" );

优点:

  • 更具可读性,
  • 如果字符串参数在编译时已知,则函数在编译时进行评估(无运行时开销)。
  • 不依赖于字节序(即参数的第一个字符将始终位于fourcc 的最高有效字节中)。

缺点:

  • 需要 c++11(或更高版本)编译器。
于 2014-12-15T11:36:36.613 回答
4

或者对内联函数做同样的事情

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)) )
} 

并避免宏的头痛,但否则你的方法对我来说看起来不错。

于 2009-05-01T13:37:56.620 回答
3

如果我没记错的话,您可以只使用多字符字符常量,对吗?

unsigned int fourCC = 'blah';

这在 ANSI/ISO 规范中是完全有效的,尽管有些编译器会抱怨一点。这就是过去在旧版 Macintosh API 中处理资源类型的方式。

于 2009-05-01T14:00:37.767 回答
1

我认为您的算法没有任何问题。但是对于这样的事情,我只会写一个函数而不是宏。宏有很多隐藏的功能/问题会随着时间的推移而困扰您。

uint FourCC(char a, char b, char c, char d) { 
  return ( (uint32) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a)) );
}
于 2009-05-01T13:37:55.933 回答
1

假设 Windows(因为 FOURCC 是一个 Windows 概念),Win API 已经提供了mmioStringToFOURCCmmioFOURCC

于 2009-05-01T14:07:02.197 回答
1

如果不需要编译时常量,也许最简洁的是

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

这仅根据需要接受四个字符的标签。

于 2009-05-01T14:26:21.013 回答
0

而不是#define,我可能会放置几乎相同的代码并依赖编译器来内联它。

于 2009-05-01T13:38:13.780 回答
0

怎么样:

#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
于 2013-08-22T13:34:01.307 回答
0

如今,使用 constexpr 和 c++17 有更好的解决方案(可能更早,不确定)。我不确定它是否完全跨平台,但它适用于 Visual Studio 和 XCode。

首先,您需要一个包装函数来将函数转换为编译时值:

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

然后你需要一个 constexpr 函数将短字符串转换为整数:

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;
}

然后包装在宏中,同时拥有 4 字节和 8 字节字符常量,以及小端和大端变体(如果你愿意的话)......

#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)>()

一些测试来验证..

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;
}

未完成边界溢出检查,但可能可以添加编译时断言。

于 2020-12-21T04:24:11.587 回答
-1
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)
            )       
    );
}
于 2009-05-01T15:51:30.713 回答