11

My team is developing a embedded system where we need to follow MISRA C++.

We are refactoring the code to use less virtual methods so we are trying to implement the CRTP to use static polymorphism instead of the dynamic one.

But we have the problem that static polymorfism requires a pointer conversion, so our static analysis checker complains.

Here is the interface

template <typename T>
class UpdateMethod
{
protected:
    ~UpdateMethod() {}
 public:
    void operator()() const
    {
        // [MISRA Rule 5-2-7] violation:
        static_cast<const T*>(this)->update();
    }
};

Here is one of the implementations:

class A
    : public UpdateMethod<A>
{
 public:
    void update() const {}
};

When in pass the MISRA checker it complains about the static_cast (conversion from ptr to ptr (e926).

So, my question is: is there any good way to implement the CRTP without having to suppress the MISRA warning, so in a secured way?

A related question only about the pointer conversion: MISRA C++ 2008 Rule 5-2-7 violation: An object with pointer type shall not be converted to an unrelated pointer type, either directly or indirectly I have the same error in the CRTP.

Edit: As mentioned only C++03 and no external library like boost.

LeDYoM
  • 949
  • 1
  • 6
  • 21

3 Answers3

6

You might use the reverse approach:

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;
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Thanks. That worked. So the sake of completeness if someone has the same problem: use private inheritance if T has no virtual methods or MISRA will complain also. – LeDYoM Oct 12 '18 at 12:33
3

Ok problem is that tool does checking on template definition and not on template instantiation.

There must be some way to help tool understand the situation. The best way would be C++2a concepts, but most probably tool doesn't support that and compiler probably doesn't do that too.

Other solution would be provide a static_assert hoping that tool will understand that:

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

The other way is to use SFINAE and make operator available when casting make seance:

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

Or use both.

Try this I hope tool will understand that and stop reporting an error. If not then IMHO it is a bug in tool.

Somebody point out that that C++03 must be used. In this case you can use boost, where this helper templates enable_if and is_base_of where initially defined.

Marek R
  • 32,568
  • 6
  • 55
  • 140
  • 1
    that doesn't look very C++03 –  Oct 11 '18 at 09:50
  • 2
    @jakub_d: The sfinae solution works in c++03 also if you simply copy the implementation of std::is_base_of into your code somewhere. std::is_base_of is not related to c++11/14 core language, it is simply a library add on which can be copied. Also static_assert can be build on c++03 simply by using template specialization. – Klaus Oct 11 '18 at 09:51
  • Problem here is that the `static_assert`ion in class scope is going to be checked when the class is instantiated => at `class B : public UpdateMethod` => B is incomplete here, so the check will fail. – StoryTeller - Unslander Monica Oct 11 '18 at 10:01
  • @jakub_d this limitation is not described in question. It is in a comment which I missed. Question should be corrected and my answer too. Anyway SFINAE works in C++03, but requires extra work. – Marek R Oct 11 '18 at 10:01
  • @StoryTeller: Checking in method scope works well. So answer should be modified;) – Klaus Oct 11 '18 at 10:05
  • I don't think these checks help much. The conversion would be ill-formed anyway if these checks would fail, and no self-respecting compiler would fail to catch that. The UB occurs when `class B : public UpdateMethod` exists and you also do `class A : public UpdateMethod`. Now, your checks pass, because `B` is indeed a base of `UpdateMethod`, but the conversion is UB, because base of `A` is being converted to `B`. – eerorika Oct 11 '18 at 10:31
  • Yes, I posted that it must be C++03. And I will add no external libraries like boost. – LeDYoM Oct 11 '18 at 10:40
1

What the checker doesn't like is the downcasting. Can we do it without casting at all? The derived class can supply the correct value with the correct type e.g. during construction. Sort of pre-downcast. It will cost you one extra stored pointer. Like this:

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: That could work, but then you have another MISRA error: Base class has no non-destructor virtual functions, witch means you should inherit private and then you cannot access update. But good try. – LeDYoM Oct 12 '18 at 12:31
  • And you have to implement copy/move constructor too. – Jarod42 Oct 12 '18 at 12:43