![]() |
Application of Rule 15.0.1 to Abstract Interface Classes - Printable Version +- MISRA Discussion Forums (https://forum.misra.org.uk) +-- Forum: MISRA C++ (https://forum.misra.org.uk/forumdisplay.php?fid=18) +--- Forum: MISRA C++:2023 guidelines (https://forum.misra.org.uk/forumdisplay.php?fid=188) +---- Forum: 4.15 Special member functions (https://forum.misra.org.uk/forumdisplay.php?fid=201) +---- Thread: Application of Rule 15.0.1 to Abstract Interface Classes (/showthread.php?tid=1684) |
Application of Rule 15.0.1 to Abstract Interface Classes - nehalpatel - 14-05-2024 It seems rule 15.0.1 has conflicting requirements for pure virtual / abstract classes with no data members. e.g. if we create the following classes: Code: class CanMessage { Per the rule, we either need
However these prevent us from being able to create a std::unique_ptr to the base class (and allow the derived classes to manage their data through their destructors), or prevents us from being able to copy or move one CanClassicMessage to another CanClassicMessage, if the base copy/move constructors are deleted. There is an exception for Aggreagate types, but it seems there needs to be an other one for an Interface/Abstract class which holds no data. RE: Application of Rule 15.0.1 to Abstract Interface Classes - misra cpp - 07-06-2024 We don't agree that your use case requires a modification of rule 15-0-1. Your code shows one of the issues the rule is seeking to prevent: A function receiving references to CanMessage objects as parameters could call one of the public copy or move operations. This would not copy or move the leaf class (e.g. CanClassicMessage) objects, but only their base class part. In your example no data would be copied or moved at all since CanMessage does not have any data members. In order to avoid this slicing and make your code compliant, you need to make CanMessage unmovable. You have already correctly observed that you must maintain the public virtual destructor for your use case. Note that the rule does not require you to make the leaf classes unmovable. Since your design makes use of the default copy and move operations in the leaf classes, you are implictly calling the respective operations in the base class. Therefore, you need to make the copy and move operations in CanMessage protected. In the leaf classes, these operations are public and defaulted (as in your code), which is the same as not stating them at all (Rule of Zero). Without guarantee of fitness for any purpose, a compliant (w.r.t. to 15.0.1) version of your example is class CanMessage { public: virtual ~CanMessage() = default; protected: CanMessage() = default; // Want to move and copy through derived classes CanMessage(const CanMessage&) noexcept = default; CanMessage(CanMessage&&) noexcept = default; CanMessage& operator=(const CanMessage&) & noexcept = default; CanMessage& operator=(CanMessage&&) & noexcept = default; public: virtual uint32_t Id() = 0; virtual uint8_t DataLength() = 0; virtual std:: span<std::byte> Data() = 0; }; class CanClassicMessage : public CanMessage { public: CanClassicMessage(uint32_t id, uint8_t data_length) : id_{id}, data_length_{data_length} {} uint32_t Id() override { return id_; } uint8_t DataLength() override { return data_length_; } std:: span<std::byte> Data() { return data_; } private: uint32_t id_; uint8_t data_length_; std::array<std::byte, 8> data_; }; class CanFDMessage : public CanMessage { public: CanFDMessage(uint32_t id, uint8_t data_length) : id_{id}, data_length_{data_length} {} uint32_t Id() override { return id_; } uint8_t DataLength() override { return data_length_; } std:: span<std::byte> Data() { return data_; } private: uint32_t id_; uint8_t data_length_; std::array<std::byte, 64> data_; }; // Test static_assert(!std::is_copy_constructible_v<CanMessage>); static_assert(!std::is_copy_assignable_v<CanMessage>); static_assert(!std::is_move_constructible_v<CanMessage>); static_assert(!std::is_move_assignable_v<CanMessage>); static_assert(std::is_copy_constructible_v<CanClassicMessage>); static_assert(std::is_copy_assignable_v<CanClassicMessage>); static_assert(std::is_move_constructible_v<CanClassicMessage>); static_assert(std::is_move_assignable_v<CanClassicMessage>); static_assert(std::is_copy_constructible_v<CanFDMessage>); static_assert(std::is_copy_assignable_v<CanFDMessage>); static_assert(std::is_move_constructible_v<CanFDMessage>); static_assert(std::is_move_assignable_v<CanFDMessage>); RE: Application of Rule 15.0.1 to Abstract Interface Classes - nehalpatel - 13-06-2024 Thank you for the thorough explanation and writeup! |