在C++中设置FourCC数值

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

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


如果我想做这样的事情怎么办: uint32 id( FOURCC( "blah" ) ); - Nick
FourCC id("blah"); - Nick
这是否有使用 OpenCV? - scorpiodawg
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之间。


1
任何非玩具编译器都将在编译时对宏进行常量折叠。 - Dave
2
@Dave:没错。这只是一种避免使用宏却不失其优势的方法。其他回复建议使用内联函数,但不再提供编译时常量,这可能很方便。这提供了一些额外的安全性,但谁会将错误的内容放入 FOURCC 中呢?说实话,我不确定自己是否会费心去做这件事,但我想把它分享出来。 - James Hopkin
在具有不同字节序的机器上,这样做会不会导致反向? - fuzzyTew
对我来说,为不同的字节序重新定义似乎不太优雅 - 我很好奇为什么没有人尝试使用带有字符数组的联合。 - fuzzyTew
1
@fuzzyTew Sanjaya确实提出了这个建议。我在最初误解后,赞同了那个答案,但你不能通过那种方式获得一个整数编译时常量。 - James Hopkin
显示剩余4条评论

10
uint32_t FourCC = *((uint32_t*)"blah");

为什么不能这样做?

编辑:int -> uint32_t。

而且它并没有将char **转换为uint32_t。它将(char*)转换为(uint32_t*),然后解引用(uint32_t*)。没有涉及字节序,因为它将一个uint32_t 赋值给另一个uint32_t,唯一的缺陷是对齐和我没有明确指定32位类型。


3
由于大端和小端机器之间的值不同,因此在某些体系结构中可能会出现未对齐的情况,导致运行时硬件异常。 - Tom
我曾考虑过这种方式,我相信它可以在我们支持的所有平台上运行。没有任何建议处理大小端问题。说实话,这并不是一个问题,因为它将是特定于平台的数据。 - Nick
@James Hopkin:这不是将指针转换为int,而是将指针转换为int POINTER,然后对其进行取消引用。该值将始终与使用4个字符和一个int之间的联合并将字符分配给{'b','l','a','h'}相同。关于字节顺序的问题,FOURCC代码应在内存中以相同的顺序排列(http://msdn.microsoft.com/en-us/library/dd375802(VS.85).aspx提到了Windows平台上的小端) - Grant Peters

7

通过使用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" );

优点:

  • 更易读。
  • 如果字符串参数在编译时已知,则函数将在编译时计算(没有运行时开销)。
  • 不受字节序影响(即参数的第一个字符始终位于四字符码的最高有效字节中)。

缺点:

  • 需要使用c++11(或更高版本)编译器。

这段代码没有返回正确的FOURCC,你需要使用(p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]而不是(p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3] - Octo Poulos
@OctoPoulos - 如所述,字符串的第一个字符将以最高有效字节结束(即p [0] << 24)。如果您对fourcc整数进行十六进制打印,则会按与相应字符在字符串中的顺序相同的顺序获得ASCII代码。 当您需要将整数存储在文件或网络上时,字节序变得很重要。在这种情况下,您可能希望反转组成整数的字节。您可以使用您的解决方案并保持字节的顺序,但我更喜欢区分数据和其用途。 - MaxP

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

避免使用宏,这样可以避免头疼的问题,但除此之外,你的方法在我看来是不错的。

我会在函数版本中去掉多余的括号。 :) - Brian Neal
1
最大的缺点是在switch块中不能使用内联函数作为case语句。不过,可以使用模板结构或宏来解决这个问题。 - Tom

4
如果我没理解错的话,您可以直接使用多字符常量对吧?
unsigned int fourCC = 'blah';

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


2
我认为这些是实现定义的,不一定可移植。 - Brian Neal
3
换句话说,你可能不确定字节'b'会最终出现在高阶位还是低阶位。 - Brian Neal
此外,我认为一些编译器可能会将“blah”解释为字节而不是32位整数。我认为标准规定应该将其解释为int。 - Nick
它是实现定义的,但据我所知,多字符字面值的唯一用途是FourCC。我所知道的所有实现都实现了FourCC行为。 - bames53
'a' 在 C++ 中是 char 类型,在 C 中则是 int 类型。 - bames53
显示剩余2条评论

1

我认为你的算法没有问题。但是对于这样的情况,我会写一个函数而不是宏。宏有很多隐藏的特性/问题,随着时间的推移可能会让你吃亏。

uint FourCC(char a, char b, char c, char d) { 
  return ( (uint32) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a)) );
}

1
函数不能用作编译时常量。这意味着无法使用函数进行switch语句。可以使用宏或模板类(使用静态const int或枚举类型的结果,但不是函数)来解决问题。 - Tom

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

这只接受四个字符的标签,正如所需。


或:typedef char tag[5]; uint32 FourCC(const tag& t); - Nick

1

谢谢。mmioFOURCC是一个宏,它会扩展到我上面定义的相同#define。 - Nick

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

0
与其使用 #define,我可能会放置几乎相同的代码,并依赖编译器进行内联。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接