#c #macros #template-meta-programming
#c #макросы #шаблон-метапрограммирование
Вопрос:
Учитывая этот макрос
#define MAKE_TYPE(_a, _b, _c, _d) ((_a) | ((_b) << 8) | ((_c) << 16) | ((_d) << 24))
Я хотел бы заменить его так, чтобы вместо него использовался массив[4].
Чтобы я мог написать MAKE_TYPE(«ABCD») вместо уродливого MAKE_TYPE(‘A’, ‘B’, ‘C’, ‘D’)
Я использую это в некоторых во время компиляции для генерации некоторых констант.
Но ему не нравится, когда я передаю строку, подобную этой
#define MAKE_TYPE(s) ((s[3]) | ((s[2]) << 8) | ((s[1]) << 16) | ((s[0]) << 24))
ошибка: ссылка на массив не может появиться в константном выражении
Что ж, это не сработало. Итак, я подумал, что попробую это сделать, используя шаблонное метапрограммирование
template< char[4] s > class MAKE_TYPE
{
public:
enum{ RESULT = s[3] | (s[2] << 8) | (s[1] << 16) | (s[0] << 24) };
};
К сожалению, это также не сработало. Кажется, я не могу поместить символ [4] в шаблон.
И я получаю эти ошибки:
ошибка: ожидаемый ‘>’ перед ‘s’ ошибка: ‘s’ не был объявлен в этой области ошибка: ссылка на массив не может появиться в константном выражении
Как я могу это сделать?
Комментарии:
1. Как говорили другие, на самом деле вы не можете этого сделать, но ваши сообщения об ошибках из версии шаблона будут вводить в заблуждение, потому что вы написали
char[4] s
то, что имели в видуchar s[4]
. С этим исправлением он по-прежнему не будет работать, но вы будете получать ошибки, которые сообщают вам, в чем заключается реальная проблема, а не не относящиеся к делу синтаксические ошибки :-).2. Кроме того,
char s[4]
если вы хотите указать «ABCD» в качестве имени, оно должно гласитьchar s[5]
🙂3. да, конечно, из-за того, что значение null делает его s[5]. Решили решить это другим способом. спасибо за помощь, ребята.
Ответ №1:
Сообщение об ошибке означает то, что оно говорит: массивы не могут отображаться в константных выражениях.
Выражения шаблона должны быть константными выражениями, поэтому массивы не могут быть в аргументах шаблона.
У вас мог бы быть указатель на массив, но это не то, что вы хотите.
Ах, учитывая, что ограничение составляет всего четыре символа, вы могли бы использовать многосимвольную константу, например, 'ABCD'
в одинарных кавычках. Но тогда порядок символов определяется реализацией.
Другая вещь, которую вы, похоже, пытаетесь сгенерировать, — это сгенерировать имя самого класса из символьных констант. Это отчасти возможно, если вы передаете в макрос простые буквы, а не символьные литералы, но, ну, не совсем. Даже если бы это вроде как сработало, это было бы довольно адски.
Комментарии:
1. Спасибо, я нашел альтернативное решение.
Ответ №2:
Вы пробовали использовать еще какие-нибудь круглые скобки и / или приведения?
#define MAKE_TYPE(s) (int((s)[3]) | (int((s)[2]) << 8) | (int((s)[1]) << 16) | (int((s)[0]) << 24))
У меня это работает, я использую что-то очень похожее для кодов FourCC, и мой макрос используется множеством разных компиляторов.
Комментарии:
1. Извините, я думал, что это сделало то, что я хотел. Оказывается, другие правы. Не удается присвоить константе.
Ответ №3:
Вы могли бы использовать многосимвольную константу, например int four = 'four'
. Однако поведение зависит от компилятора.
Ответ №4:
Если вы знаете, что входные данные являются char *
:
template <class RT> RT MakeType( const char * _arg )
{
return _arg[3] | (_arg[2] << 8) | (_arg[1] << 16) | (_arg[0] << 24);
}
ещё:
template <class T, class RT> RT MakeType( const T amp;B0, const T amp;B1, const T amp;B2, const T amp;B3 )
{
return B3 | (B2 << 8) | (B1 << 16) | (B0 << 24);
}
Я бы сделал:
inline unsigned int MakeType( const char * _arg )
{
return _arg[3] | (_arg[2] << 8) | (_arg[1] << 16) | (_arg[0] << 24);
}
и приведение, если это необходимо.
Комментарии:
1. Это не приведет к выполнению вычислений во время компиляции. Таким образом, преимущество макроса теряется.
2. @xtofl: если вы используете ссылки, переменная не будет скопирована.
3. @Naszta: Я знаю. Но в этом случае перенос ссылки в стек обходится в четыре раза дороже, чем перенос значения… (для 32-разрядной версии. В 64-разрядной версии это может быть 8 раз.) Если вы настаиваете на передаче ссылки, передайте ее const: функции не разрешено изменять фактические аргументы.
4.
inline
не будет иметь никакого значения. Во время компиляции значениеRESULT
вычисляется во время самой компиляции, так что оно ведет себя как постоянное значение, и с ним не связаны накладные расходы во время выполнения. Здесь его выполнениеinline
может избежать вызова функции, но результат все равно вычисляется во время выполнения.5. @Naveen: нет. Результат будет вычислен во время выполнения. Дополнительная информация о препроцессорах: cs.cf.ac.uk/Dave/C/node14.html
Ответ №5:
Как насчет чего-то подобного?
template <typename T>
typename boost::enable_if_c<(T('1234')==0x31323334), T>::type
make(T v)
{
return v;
}
const int value = make('ABCD');
Это защитная сетка для зависящего от компилятора поведения многосимвольной константы. Вы можете добавить дополнительные реализации для другого поведения компилятора, переключая байты по мере необходимости. Это предполагает, что порядок байтов '1234'
вычисления согласован для каждого компилятора.
Эксперты по шаблонам могут быть в состоянии доработать.