如何在遵循MISRA C++标准的情况下实现CRTP

11

我的团队正在开发一个嵌入式系统,我们需要遵循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 (conversion from ptr to ptr (e926)的警告,则会有投诉。
所以,我的问题是:是否有任何好的方法来实现CRTP,而不必抑制MISRA警告,从而以安全的方式?
关于指针转换的一个相关问题: MISRA C++ 2008规则5-2-7违规:一个带指针类型的对象不应该直接或间接地被转换为无关的指针类型 在CRTP中我也遇到了同样的错误。
编辑:只使用C++03和没有外部库,例如boost。
3个回答

6

您可以使用反向方法:

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;

谢谢。那个方法可行。为了完整起见,如果有人遇到同样的问题:如果T没有虚方法或MISRA会抱怨,请使用私有继承。 - LeDYoM

3

问题在于该工具检查的是模板定义而不是模板实例化。

必须有一些方法来帮助工具理解情况。最好的方法是使用C++2a概念,但很可能该工具不支持,编译器也不支持。

另一个解决方案是提供一个静态断言,希望该工具能够理解:

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();
    }
};

或者同时使用。

试试这个工具,我希望它能理解并停止报错。如果不能,那么我认为这是一个错误。

有人指出必须使用C++03。在这种情况下,你可以使用boost,其中这些辅助模板 enable_ifis_base_of 最初就被定义了。


1
看起来不是很符合C++03的标准。 - user10316011
2
@jakub_d:sfinae解决方案在C++03中也适用,只需将std::is_base_of的实现复制到代码中的某个位置即可。std::is_base_of与C++11/14核心语言无关,它只是一个可以复制的库附加功能。此外,通过使用模板特化,也可以在C++03中构建static_assert。 - Klaus
问题在于类作用域中的 static_assert 会在类被实例化时进行检查,而在 class B : public UpdateMethod<B> 处 => B 在此处是不完整的,因此检查将失败。 - StoryTeller - Unslander Monica
@jakub_d 这个限制在问题中没有描述,而是在评论中提到了,我错过了。问题应该被更正,我的回答也是。无论如何,SFINAE 在 C++03 中可以工作,但需要额外的工作。 - Marek R
是的,我发帖说必须是C++03。而且我不会添加任何外部库,如boost。 - LeDYoM
显示剩余2条评论

1
检查器不喜欢的是向下转型。我们能否完全不进行强制类型转换而完成它?派生类可以在构造过程中提供正确类型的正确值。有点像预先进行向下转型。这将会多花费一个额外的存储指针。就像这样:
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 {}
};

FYI:这个方法可能可行,但是你会遇到另一个MISRA错误:基类没有非析构虚函数,这意味着你应该继承私有类,然后就无法访问update函数了。不过还是很不错的尝试。 - LeDYoM
你还必须实现复制/移动构造函数。 - Jarod42

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接