Best way to handle template creation?

Trying to find the best design for this!

Note that we have the Image template class that inherits from the template matrix library (in this case Eigen, but it could be anything)

template <typename T> class Image : public Eigen::Matrix <T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> 

Now think about what we want to write a function to handle reading images from a file. Of course, images can be of different types, that is, unisnged char, uint16_t, float, and even have different channels, like in grayscale, RGB, or even RGBA.

That way, we could, of course, use template classes to handle this easily. How in

 Image<RGB<unisgned char>> or Image<RGBA<float>> 

Simple when a person knows the type of image, say, monochrome 8bit

 Image<unisgned char> image = ReadImage(const char* const filename); 

or maybe even

 Image<unisgned char> image; bool b = ReadImage(const char* const filename, Image<unisgned char>& image) 

However, when reading an image file, we never know the type before reading the image. For example, Tiff and png support 8 bits and 16 bits with tiff support, even supporting float. In such cases, it is not possible to use any of the functions mentioned above. However, we can have a temmplate Factory class for sorting.

To do this, we first need to introduce the BaseImage class

 class BaseImage { public: inline BaseImage() {}; virtual inline ~BaseImage() {}; virtual inline int Width() const = 0; virtual inline int Height() const = 0; virtual inline int Depth() const = 0; etc... }; template <typename T> class Image : public BaseImage, public Eigen::Matrix <T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> 

Then we can have our Factory class, where we pass our unsigned char *, float *, etc. and let him handle the creation

 class ImageFactory { typename <T> static BaseImage* createImage(const T* const src, const int& width, const int& height, const int& depth) { if (depth == 1) { return new Image<T>(); } else if (depth == 3) { return new Image<RGB<T>>(); } etc... } } 

Of course, this makes me use dynamic allocation and inheritance.

I suppose I can get around the dynamic allocation by hiding it inside a class that takes care of this. In the constructor, Factory clas will be called

 class Image2 { public: Image2(const char* const path) { // where ReadImage and external function that will call ImageFactory pBaseImage = ReadImage(path); } ~Image2(); private: BaseImage* pBaseImage; }; 

In any case, my BaseImage class will have to expose all the functionality I need to use in my matrix library, which does not match the purpose of inheritance.

The question is, is there a better design than you can use here, because it gets pretty bulky

+6
source share
1 answer

I would post it as a comment, if I could show the code in a comment ... it seems obvious enough to me, and you probably thought about it. Why is this not what you are looking for?

 class Image { public: Image (int bytesPerPixelIn = 1) { data_ = NULL; bytesPerPixel=bytesPerPixelIn;}; void readImage (int width, int height); private: int bytesPerPixel_; void* data_; }; void Image::read (int width, int height, ...) { data_ = new unsigned char [width*height*pixelSize(imageType_)]; //... } //And you can inherit from this if need be to add special functions //like interleaving in BMP files 

Edit: Ok, I see. Would it be better?

 template <typename PixelType> class Image { public: Image () { data_ = NULL; }; virtual void allocateImage (int width, int height) { data_ = new PixelType [width*height]; } virtual void readImage (int width, int height) = 0; private: PixelType* data_; }; template <typename PixelType> class ImageTIFF: public Image <PixelType> ... 

or that:

 template <typename PixelType, int WIDTH, int HEIGHT> class Image { public: Image () { } virtual void readImage () = 0; private: PixelType* data_ [WIDTH][HEIGHT]; }; template <typename PixelType, int WIDTH, int HEIGHT> class ImageTIFF: public Image <PixelType, WIDTH, HEIGHT> ... 

The latter excludes dynamic memory ... but without dynamic memory you cannot have a resize function. I would use dynamic memory.

You will need inheritance for different file formats, since they do not read images the same way.

-

OK How about this? BaseImage here is similar to the Java “interface” and ensures that derived classes will have the functions they need.

 class BaseImage { public: virtual void allocateImage (int width, int height) = 0; virtual void readImage (int width, int height) = 0; }; template <typename PixelType> class Image: public BaseImage ... /* and so on */ BaseImage* myTIFFImage = readTIFFImage (/* whatever goes here */); 
+1
source

Source: https://habr.com/ru/post/975702/


All Articles