Given this macro
#define MAKE_TYPE(_a, _b, _c, _d) ((_a) | ((_b) << 8) | ((_c) << 16) | ((_d) << 24))
I'd like to replace it so that it takes an array[4] instead. So that I can write MAKE_TYPE("ABCD") instead of the ugly MAKE_TYPE('A', 'B', 'C'开发者_开发百科, 'D')
I'm using this in some at compile time to generate some constants.
But, it doesn't like me passing in a string like this
#define MAKE_TYPE(s) ((s[3]) | ((s[2]) << 8) | ((s[1]) << 16) | ((s[0]) << 24))
error: an array reference cannot appear in a constant-expression
Well that didn't work. So I thought I'd take a crack at it using template metaprogramming
template< char[4] s > class MAKE_TYPE
{
public:
enum{ RESULT = s[3] | (s[2] << 8) | (s[1] << 16) | (s[0] << 24) };
};
Unfortunately, this also didn't work. I can't seem to put a char[4] in the template. And I get these errors:
error: expected ‘>’ before ‘s’ error: ‘s’ was not declared in this scope error: an array reference cannot appear in a constant-expression
How can I do it?
The error message means what it says: arrays can't appear in constant-expressions.
Template expressions must be constant-expressions, so arrays can't be in template arguments.
You could have a pointer to an array, but that's not what you want.
Ah, seeing as the limit is just four characters, you could use a multi-character constant, such as 'ABCD'
in single quotes. But, then the order of the characters is implementation-defined.
The other thing you appear to be attempting is to generate the name of the class itself from the character constants. This is kind of possible if you pass bare letters and not character literals into the macro, but, well, not really. Even if it kind of worked, it would be pretty hellish.
Have you tried some more parentheses and/or casts?
#define MAKE_TYPE(s) (int((s)[3]) | (int((s)[2]) << 8) | (int((s)[1]) << 16) | (int((s)[0]) << 24))
This works for me, I use something very similar for FourCC codes and my macro is used by lots of different compilers.
You could use a multi-character constant, e.g. int four = 'four'
. Behavior is compiler-specific though.
If you know that the input is char *
:
template <class RT> RT MakeType( const char * _arg )
{
return _arg[3] | (_arg[2] << 8) | (_arg[1] << 16) | (_arg[0] << 24);
}
else:
template <class T, class RT> RT MakeType( const T &B0, const T &B1, const T &B2, const T &B3 )
{
return B3 | (B2 << 8) | (B1 << 16) | (B0 << 24);
}
I would do:
inline unsigned int MakeType( const char * _arg )
{
return _arg[3] | (_arg[2] << 8) | (_arg[1] << 16) | (_arg[0] << 24);
}
and cast if it is necessary.
How about something like this?
template <typename T>
typename boost::enable_if_c<(T('1234')==0x31323334), T>::type
make(T v)
{
return v;
}
const int value = make('ABCD');
This is a safety net for the compiler-dependent behaviour of the multi-character constant. You can add further implementations for different compiler behavior, switching bytes around as necessary. This assumes byte-order of '1234'
evaluation is consistent per compiler.
Template experts may be able to refine.
精彩评论