Как реализовать CRTP после MISRA С++
Моя команда разрабатывает встроенную систему, где нам нужно следовать MISRA C++.
Мы реорганизуем код, чтобы использовать меньше виртуальных методов, поэтому мы пытаемся реализовать CRTP для использования статического полиморфизма вместо динамического.
Но у нас есть проблема, что статический полиморфизм требует преобразования указателя, поэтому наш статический анализатор проверки жалуется.
Вот интерфейс
template <typename T>
class UpdateMethod
{
protected:
~UpdateMethod() {}
public:
void operator()() const
{
// [MISRA Rule 5-2-7] violation:
static_cast<const T*>(this)->update();
}
};
Вот одна из реализаций:
class A
: public UpdateMethod<A>
{
public:
void update() const {}
};
Когда проходит контролер MISRA, он жалуется на static_cast (преобразование из ptr в ptr (e926).
Итак, мой вопрос: есть ли хороший способ реализовать CRTP без необходимости пресекать предупреждение MISRA, поэтому безопасным способом?
Связанный с этим вопрос только о преобразовании указателя: MISRA C++ 2008 Правило 5-2-7 Нарушение: объект с типом указателя не должен быть преобразован в несвязанный тип указателя, прямо или косвенно у меня такая же ошибка в CRTP.
Изменение: как упоминалось только C++ 03, и никакой внешней библиотеки, такой как boost.
Ответы
Ответ 1
Вы можете использовать обратный подход:
template <typename T>
class UpdateMethod : public T
{
public:
void operator()() const
{
this->update();
}
};
class A_impl
{
public:
void update() const {}
};
typedef UpdateMethod<A_impl> A;
Ответ 2
Проблема в том, что инструмент проверяет определение шаблона, а не на создание экземпляра шаблона.
Должен быть какой-то способ помочь инструменту понять ситуацию. Лучший способ - это концепции C++2a
, но, скорее всего, инструмент не поддерживает это, и компилятор, вероятно, тоже этого не делает.
Другим решением будет предоставление static_assert, надеясь, что инструмент поймет, что:
template <typename T>
class UpdateMethod
{
static_assert(std::is_base_of<UpdateMethod<T>, T>::value, "This is CRTP");
protected:
~UpdateMethod() {}
public:
void operator()() const
{
static_cast<const T*>(this)->update();
}
};
Другой способ - использовать SFINAE и сделать оператор доступным при кастинге:
template <typename T>
class UpdateMethod
{
protected:
~UpdateMethod() {}
public:
typename std::enable_if<std::is_base_of<UpdateMethod<T>, T>::value>::type
operator()() const
{
static_cast<const T*>(this)->update();
}
};
Или используйте оба.
Попробуйте это, я надеюсь, что инструмент поймет это и перестанет сообщать об ошибке. Если нет, то ИМХО это ошибка в инструменте.
Кто-то указывает, что С++ 03 должен использоваться. В этом случае вы можете использовать boost
, где это вспомогательные шаблоны enable_if и is_base_of, где изначально определено.
Ответ 3
То, что проверяет не нравится, - это понижение. Можем ли мы это сделать без кастинга вообще? Производный класс может предоставить правильное значение с правильным типом, например, во время строительства. Тип предварительного понижения. Это будет стоить вам лишнего хранимого указателя. Как это:
template <typename T>
class UpdateMethod
{
protected:
T* implThis;
~UpdateMethod() {}
UpdateMethod(T* implThis):implThis(implThis) {}
public:
void operator()() const
{
// this was the problematic cast
implThis->update();
}
};
class A
: public UpdateMethod<A>
{
public:
A(): UpdateMethod(this) {}
void update() const {}
};