我想在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') );
你能想到的最优雅的方式是什么?
答案 0 :(得分:16)
您可以使用以下命令使其成为编译时常量:
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 :(得分:8)
uint32_t FourCC = *((uint32_t*)"blah");
为什么不呢?
编辑:int - &gt; uint32_t的。
并且它没有向uint32_t施放char **。它将(char *)转换为(uint32_t *),然后取消引用(uint32_t *)。没有涉及endian-ness,因为它将uint32_t分配给uint32_t。唯一的缺陷是对齐,我没有明确指出32位类型。
答案 2 :(得分:5)
或使用内联函数
执行相同操作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)) )
}
并避免宏的头痛,但否则你的方法对我来说很好。
答案 3 :(得分:3)
如果我没弄错的话,你可以直接使用多字符字符常量吗?
unsigned int fourCC = 'blah';
这完全适用于ANSI / ISO规范,尽管有些编译器会抱怨一下。这就是以前在旧的Macintosh API中处理资源类型的方式。
答案 4 :(得分:3)
通过使用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" );
的优点:
缺点:
答案 5 :(得分:1)
我认为你的算法没有错。但对于像这样的东西,我只会写一个函数而不是宏。宏有许多隐藏的功能/问题,随着时间的推移会咬你。
uint FourCC(char a, char b, char c, char d) {
return ( (uint32) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a)) );
}
答案 6 :(得分:1)
假设Windows(因为FOURCC是Windows概念),Win API已经提供mmioStringToFOURCC和mmioFOURCC。
答案 7 :(得分: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));
根据需要,它只接受四个字符的标记。
答案 8 :(得分:0)
而不是#define,我可能会使用相同的代码并依赖编译器来内联它。
答案 9 :(得分: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
答案 10 :(得分: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;
}
未完成边界溢出检查,不过可能可以添加编译时断言。
答案 11 :(得分:-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)
)
);
}