3
\$\begingroup\$

I have an image loader, now only for bitmaps. I'm a little confused because I want to split my code to different classes, one for writing a BMP data to a file (WRITER), one for loading BMP data from file, or from pre-generated dataset (1) and storing it in its private members.( (1) like a noise function saved to an array).

Currently I have the 2 class for this. The header:

namespace PND
{

    inline void out(const ::std::string); //standard output log
    inline void err(const ::std::string); //error log

    bool file_exists(::std::string m_filename) //this could be used in both classes
    {
        ::std::ifstream infile(m_filename+".bmp");
        return infile.good();
    }

    class BMP
    {
    public:
        BMP();
        BMP(const BMP&);//copy csrt
        ~BMP();
        BMP& operator= (BMP other); //ass. operator

        bool loadData(::std::string filename);//from file, filename is the file's filename
        bool loadData(::std::string filename,const int width, const int height, const unsigned char* data, const unsigned int length);//from array, filename is user defined
        //they are simple pixel data accessors with out of bounds handling
        unsigned char getData_B(const unsigned int x, const unsigned int y);
        unsigned char getData_G(const unsigned int x, const unsigned int y);
        unsigned char getData_R(const unsigned int x, const unsigned int y);

        unsigned int* getSize() { return m_size; }; //some getter
        ::std::string getName() { return m_filename; };

    private:

        void swap(BMP& other);
        void freeData();
            //the bitmap data:
        BITMAPINFOHEADER m_infoheader;
        BITMAPFILEHEADER m_fileheader;
        ::std::string m_filename;
        unsigned char* m_data;
        unsigned int m_size[2];
        long __m_size; //it is the length of the m_data array (num of pixels * 3)
    };

    class WRITER
    {
    public:
        bool writeData(::std::string filename, const int width, const int height, const unsigned char* data, const unsigned int length);
        bool writeData(::std::string filename, const BMP& bmp);//now this is the problem, more info below the code

    private:
        ::std::ofstream outstream;

        bool openFile(::std::string filename);
        bool writeHeader(const int width,const int height);
        bool writeFile(const unsigned char* data, const unsigned int length);
        bool closeStream();
    };
}

I'm in trouble because I would like to access (read only) the bitmap data (headers, pixel data) from the image - that was saved to a BMP object - in the WRITER class. But because they are private members I can not access them in the writeheader() and writefile() functions. It would be nice to access them without making them public members, and I also try to avoid using inheritance in this case since a WRITER is not really a BMP. Lots of gettersare also not the best way for accessing arrays I think.

So the BMP could only load and save the image data. And with a WRITER I can write this data to a .bmp file. I want to make a pretty clean interface with public functions, and do not mix classes if not neccessary.

\$\endgroup\$
3
\$\begingroup\$

The only standard ways to access private data from another class are:

  1. Getters
  2. Friend class

If you want to make a friend class, just do something like this:

class Writer ;

class BMP
{
    //...
private:
friend class Writer ;
}

Now the Writer class can access all of BMP's data fields.

I would personally suggest using getters though. You could have getters for the header information and use operator [] for accessing any arrays.

unsigned char& BMP::operator [] (const size_t i) {
    return m_data [i] ;
}

const unsigned char& BMP::operator [] (const size_t i) const {
    return m_data [i] ;
}
\$\endgroup\$
  • \$\begingroup\$ +1 I've never used overloaded []. That's seems much better for accessing arrays thanks! Also, friend functions are the way to go, I think so(never really used them) \$\endgroup\$ – David Szalai Dec 15 '13 at 10:51
3
\$\begingroup\$

Is there a reason the WRITER class can't be static (i.e. just have all static methods) and then put a write() function in the BMP class that would just call the static writer methods? You would have to create an overload to take all the private members you would want to pass in (i.e. a write method to take in file name and a const BITMAPINFOHEADER& or whatever you might need). Otherwise a friend function (as red_eight mentioned) would be the way to go in your case.

Alternatively if you are trying to separate your classes into a sort of hierarchical reader/writer structure, a possibility would be to have a base BMP class that has all the necessary info (like headers, size, filename etc) then having 2 separate classes derived from it, for example using your code:

namespace PND
{
    inline void out(const ::std::string); //standard output log
    inline void err(const ::std::string); //error log

    bool file_exists(::std::string m_filename) //this could be used in both classes
    {
        ::std::ifstream infile(m_filename+".bmp");
        return infile.good();
    }

    class BMP
    {
    public:
        BMP();
        BMP(const BMP&);//copy csrt
        virtual ~BMP();
        BMP& operator= (BMP other); //ass. operator

        //they are simple pixel data accessors with out of bounds handling
        unsigned char getData_B(const unsigned int x, const unsigned int y);
        unsigned char getData_G(const unsigned int x, const unsigned int y);
        unsigned char getData_R(const unsigned int x, const unsigned int y);

        unsigned int* getSize() { return m_size; }; //some getter
        ::std::string getName() { return m_filename; };

    protected:
            //the bitmap data:
        BITMAPINFOHEADER m_infoheader;
        BITMAPFILEHEADER m_fileheader;
        ::std::string m_filename;
        unsigned char* m_data;
        unsigned int m_size[2];
        long __m_size; //it is the length of the m_data array (num of pixels * 3)
    private: // NOTE: not sure if these would be private or in BMP_READER
        void swap(BMP& other);
        void freeData();
    };

    class BMP_READER : virtual public BMP
    {
    public:
        bool loadData(::std::string filename);//from file, filename is the file's filename
        bool loadData(::std::string filename, const int width, const int height, const unsigned char* data, const unsigned int length);//from array, filename is user defined
    }

    class BMP_WRITER : virtual public BMP
    {
    public:
        static bool writeData(::std::string filename, const int width, const int height, const unsigned char* data, const unsigned int length);
        bool writeData(); // already have 'm_filename' and access to base BMP

    private:
        ::std::ofstream outstream;

        bool openFile(::std::string filename);
        bool writeHeader(const int width,const int height);
        bool writeFile(const unsigned char* data, const unsigned int length);
        bool closeStream();
    };
}
\$\endgroup\$
  • \$\begingroup\$ Thanks for the answer, I'm gonna use friend functions/classes for this. I've not really used them, but I think its the best solution. +1 \$\endgroup\$ – David Szalai Dec 15 '13 at 10:49
  • 1
    \$\begingroup\$ Sounds good, sometimes they really are the best answer to a solution; check out this link for a good example on friend functions if you might need it :) \$\endgroup\$ – txtechhelp Dec 15 '13 at 10:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.