Я работаю над библиотекой C ++, которая использует статический полиморфизм через шаблоны. Он спроектирован таким образом, потому что целью является встроенная система с небольшим стеком, и часто это выгодно для inline
функции-члены, чтобы сэкономить на использовании стека.
Использование шаблонов для статического полиморфизма означает, что имена типов источников событий (чаще всего драйверов устройств) часто бывают невероятно длинными:
class DeviceThatUsesSPI<class SPI_BUS_TYPE> {
public:
class DeviceEvent : public Event<DeviceThatUsesSPI> {};
// and the rest of the device driver implementation, including
// the code that emits that event.
}
SomeSpecificGpioBus gpio_bus();
SoftwareSpiBus<typeof(gpio_bus)> spi_bus(&gpio_bus);
DeviceThatUsesSPI<typeof(spi_bus)> device(&spi_bus);
Как видите, мы используем GCC typeof
оператор расширения, чтобы не выписывать полное отвратительное имя типа DeviceThatUsesSPI<SoftwareSpiBus<SomeSpecificGpioBus>>
несколько раз. Это работало как шарм везде, где мы пробовали его, до сегодняшнего дня я пытался использовать его для доступа к вложенному классу, представляющему событие. В моем текущем примере это специализация шаблона, реализующая привязку обработчика событий во время компиляции:
template<>
inline void event_handler<typeof(device)::ExampleEvent>(typeof(device) *emitter) {
// event handler implementation...
}
Тем не менее, я также попробовал это в гораздо более минимальном примере объявления переменной:
typeof(device)::ExampleEvent event;
В обоих случаях G ++ не может проанализировать выражение с синтаксической ошибкой. Я предполагаю, что это потому, что в стандартной грамматике C ++ нет ситуации, когда ::
следует за чем угодно, кроме идентификатора, и анализатор не может отследить и обработать первую часть как тип, когда он встречает двоеточия.
Тем не мение, руководство GCC о typeof
дает следующее обещание об этом операторе:
typeof
Конструкция может использоваться везде, где может использоваться имя typedef. Например, вы можете использовать его в объявлении, в приведении или внутриsizeof
или жеtypeof
,
Если я заменю два использования typeof
в моем примере с typedef G ++ счастлив:
typedef typeof(device) device_type;
template<>
inline void event_handler<device_type::ExampleEvent>(typeof(device) *emitter) {
// event handler implementation...
}
device_type::ExampleEvent event;
Так что это усиливает мое подозрение, что компилятор в порядке с тем, что я написал семантически, но грамматика не позволяет мне выразить это. Хотя использование перенаправления typedef действительно помогает мне работать с кодом, я бы предпочел найти способ сделать объявления обработчика событий самодостаточными для удобства пользователей этой библиотеки. Есть ли способ написать typeof
оператор, чтобы убрать неоднозначность синтаксического анализа, чтобы я мог сделать объявление события однострочным?
Я думаю, что небольшая мета-функция может добиться цели.
template<typename T>
struct self
{
typedef T type;
};
тогда используйте это как:
template<>
inline void
event_handler<self<typeof(device)>::type::ExampleEvent>(typeof(device) *emitter)
{
// event handler implementation...
}
Или вы можете определить мета-функцию (которая Меньше универсальный) как:
template<typename T>
struct event
{
typedef typename T::ExampleEvent type;
};
тогда используйте это как:
template<>
inline void
event_handler<event<typeof(device)>::type>(typeof(device) *emitter)
{
// event handler implementation...
}
Кстати, в C ++ 11 вы можете использовать decltype
вместо typeof
(который является расширением компилятора):
template<>
inline void
event_handler<decltype(device)::ExampleEvent>(typeof(device) *emitter)
{
// event handler implementation...
}
Надеюсь, это поможет. 🙂
В соответствии с отчет об ошибках в GCC это была известная проблема, исправленная в GCC 4.7. Модернизация GCC является долгосрочным решением.
В отчете об ошибке описывается еще один обходной путь использования шаблона класса в качестве косвенной меры для обхода синтаксического анализатора в старых версиях:
class SameType<T> {
public:
typedef T R;
}
T<typeof(device)>::ExampleEvent event;
Это лучше, чем typedef, потому что он обобщает все типы событий, но все же неестественно для пользователя.
Эта же проблема, применительно к новому стандарту decltype
оператор, был на самом деле предметом изменение спецификации C ++ 11 это прояснило правила разбора типов, чтобы сделать эту работу, как ожидалось.