Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Application of Rule 15.0.1 to Abstract Interface Classes
#2
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>);
Posted by and on behalf of
the MISRA C++ Working Group
Reply


Messages In This Thread
RE: Application of Rule 15.0.1 to Abstract Interface Classes - by misra cpp - 07-06-2024, 03:04 PM

Forum Jump:


Users browsing this thread: 4 Guest(s)