Search code examples
c++virtual-inheritancediamond-problem

Solution to diamond inheritance, but is it appropriate?


Aside from the other various things I've been working on, I've also been making an image api. One problem I've encountered though is the need for there to be an image base class that contains the dimensions of the image. From it would branch ImageIn and ImageOut, and from those come ImageIO. Here was my original solution;

class ImageBase{
public:
  point<int> Dim() const=0;
};

class ImageI : virtual public ImageBase{
public:
  virtual unsigned int get(int x, int y) const=0;
};

class ImageO : virtual public ImageBase{
public:
  virtual void set(int x, int y, unsigned int col)=0;
};

class ImageIO : public ImageI, public ImageO{
};

class Bitmap : public ImageIO{
  ...
};

Of course, now their are two vtable pointers and dims to each instance of image, and I suppose given the size of an array of pixels, it's probably not worth worrying about, but there is something ascetically displeasing about this, so I purpose this solution;

class ImageIO{ // could also be called ImageBase
public:
  point<int> Dim() const=0;
  virtual unsigned int get(int x, int y) const=0;
  virtual void set(int x, int y, unsigned int col)=0;
};

class ImageI : public ImageIO{
public:
  point<int> Dim() const=0;
  virtual unsigned int get(int x, int y) const=0;
private:
  virtual void set(int x, int y, unsigned int col)=0;
};

class ImageO : public ImageIO{
public:
  point<int> Dim() const=0;
  virtual void set(int x, int y, unsigned int col)=0;
private:
  virtual unsigned int get(int x, int y) const=0;
};

class Bitmap : public ImageIO{
  ...
};

Now, because certain operators in ImageO and ImageI are private, they cannot be accessed from a pointer of that type, accomplishing pretty much what having an input and output edition of a class accomplishes. There is a slight issue though, the casting syntax is now somewhat more complex.

ImageI* myImgInput = static_cast<ImageI*>((ImageIO*)(&myReal));

I'm thinking some clever use of casting functions can make the process automatic, so the final question is, are there any inherent flaws in this? I've tested it with a minor implementation, and it works fully, but I'd hate to get the api done, then have to start over.

Edit

Note that what was ImageBase in the second example is now ImageIO for the sake of clarity, and that ImageI and ImageO in the second example were not supposed to virtually inherit from ImageIO


Solution

  • This cannot work if the bases have data members

    Using the following sizes

    Imagaebase=4
    ImageI=12 + 4 from Imagebase
    ImageO=24 + 4 from Imagebase
    

    In the first example the size of Bitmap would be 4 + 12 + 24 (Note Imagebase only counts once) = 40

    In the second case the size of Bitmap would be 4

    Casting would not work if the sizes are so askew

    If there is no data in any of the classes then:

    The Base should be ImageIO and have all functions in it

    Then have ImageI and ImageO inherit from it to make the not allowed function(s) to be private. Then there is no casting needed.

    This would look like the following:

    class ImageIO{
    public:
      point<int> Dim() const=0;
      virtual unsigned int get(int x, int y) const=0;
      virtual void set(int x, int y, unsigned int col)=0;
    };
    
    class ImageI : virtual public ImageIO{
    private:
      virtual void set(int x, int y, unsigned int col)
      {
            // Can't be called
      }
    };
    
    class ImageO : virtual public ImageIO{
    private:
      virtual unsigned int get(int x, int y) const
      {
            // Can't be called
      }
    };
    
    class Bitmap : public ImageIO{
      ...
    };