0

I want to create a multimap that maps several bitmaps to their specific char. For latin chars there are more bitmaps (because of font size). Also I need to store chinese chars. There are different fonts (called meg5, meg7, _china01). meg-family fonts are used for latin letters and china01 is used for chinese letters. I need to be able to switch between languages. Thats why I thought of storing them all in one multima. For latin letters I need to determine correct font (between meg5 and meg7). Here are my classes:

class Bitmap {
public:
    virtual ~Bitmap() = default;
    inline std::vector<int> getBMPData() const { return m_data; }
    inline void setBMPData(std::vector<int> data) { m_data = data; }
private:
    std::vector<int> m_data;
};

class MegFamily : public Bitmap {
private:
    uint8_t m_OverallSize = 0;
    uint8_t m_xDisplacement = 0;
    uint8_t m_yDisplacement = 0;
    uint8_t m_width = 0;
    uint8_t m_height = 0;
public:
    MegFamily() {};
    inline uint8_t getOverallSize() const { return m_OverallSize; }
    inline uint8_t getXDisplacement() const { return m_xDisplacement; }
    inline uint8_t getYDisplacement() const { return m_yDisplacement; }
    inline uint8_t getWidth() const { return m_width; }
    inline uint8_t getHeight() const { return m_height; }

    //only for test purposes
    inline void setOverallSize(uint8_t overallSize) { m_OverallSize = overallSize; }
    inline void setXDisplacement(uint8_t xDisplacement) { m_xDisplacement = xDisplacement; }
    inline void setYDisplacement(uint8_t yDisplacement) { m_yDisplacement = yDisplacement; }
    inline void setWidth(uint8_t width) { m_width = width; }
    inline void setHeight(uint8_t height) { m_height = height; }
};

class Meg5 : public MegFamily {};
class Meg7 : public MegFamily {};

class ChineseFont : public Bitmap{};

I wanted to use e.g. find('A') for all A Bitmaps and then determine their font. This is what I've done so far.

    typedef std::vector<int> BMPData;
    std::multimap<char, Bitmap> BMPLibrary;

    ChineseFont SomeChineseName;
    Meg5 meg5_A;
    Meg7 meg7_A;



    BMPData BMPSomeChineseName{0x00,0x38,0x27,0x24,0x24,0x24,0x24,0x24,0x24,0xe4,0x04,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x09,0x09,0x08,0x07,0x00,0x00};

    BMPData BMPmeg5A{ 0x01, 0x15, 0x05, 0x02 };

    BMPData BMPmeg7A{ 0x7E, 0x09, 0x09, 0x09, 0x7E };

    SomeChineseName.setBMPData(BMPSomeChineseName);

    meg5_A.setBMPData(BMPmeg5A);
    meg5_A.setOverallSize(5);
    meg5_A.setXDisplacement(0);
    meg5_A.setYDisplacement(0);
    meg5_A.setWidth(4);
    meg5_A.setHeight(5);

    meg7_A.setBMPData(BMPmeg5A);
    meg7_A.setOverallSize(6);
    meg7_A.setXDisplacement(0);
    meg7_A.setYDisplacement(0);
    meg7_A.setWidth(5);
    meg7_A.setHeight(7);

    BMPLibrary.insert(std::pair<char, Bitmap>('A', meg5_A));
    BMPLibrary.insert(std::pair<char, Bitmap>('A', meg7_A));
    BMPLibrary.insert(std::pair<char, Bitmap>('\u2ed8', SomeChineseName));

    std::cout << "searching for As" << std::endl;
    auto pairFound = BMPLibrary.find('A');

    if (pairFound != BMPLibrary.end())
    {
        size_t numPairsInMap = BMPLibrary.count('A');

        for (size_t counter = 0; counter < numPairsInMap; ++counter)
        {
            std::cout << "Type of found element: " << typeid(pairFound->second).name() << std::endl;
        }
    }

My output is the following:

searching for As
Type of found element: class Bitmap
Type of found element: class Bitmap

My question is: Is it possible to determine wheter a result is instance of meg5 or meg7? Thanks for your help.

  • 1
    read about [object slicing](https://stackoverflow.com/questions/274626/what-is-object-slicing). Your map does not contain `Meg5` or `Meg7` instances. – 463035818_is_not_an_ai May 10 '21 at 12:55
  • I don't really get your point, sorry – doublesobig May 10 '21 at 14:01
  • what did you not understand in the linked q&a? If you insert a `Meg5` into a `std::multimap` then the map will not conain a `Meg5` object. Its not the answer to your quesiton btw, but you need to understand that first, before you can determine the dynamic types of the objects in the map. – 463035818_is_not_an_ai May 10 '21 at 14:04
  • I do unterstand that it is always possible to assign an object of a derived class to an object of its base class. But I am not really sure how to collect them in my multimap – doublesobig May 10 '21 at 14:52
  • "I do unterstand that it is always possible to assign an object of a derived class to an object of its base class. " No it isnt possible! Thats my point. – 463035818_is_not_an_ai May 10 '21 at 14:52
  • The specific information from derived class will be sliced off and only the "known and common" information are collected. Thats why all the additional information (overall size, displacements, ...) are lost – doublesobig May 10 '21 at 15:03
  • So technically it is possible but not want I wanted at all. – doublesobig May 10 '21 at 15:18
  • @doublesobig You would need to store `Bitmap*` *pointers*, not `Bitmap` *instances*. Then you can store *pointers* to objects of type `Meg5`/`Meg7`/etc, and use `dynamic_cast` if you need to know when a given `Bitmap*` is pointing at a `Meg5`/etc or not – Remy Lebeau May 11 '21 at 01:04
  • @RemyLebeau i am failing to implement a dynamic_cast. Is there a tutorial you would recommend me for dynamically casting multimap elements? – doublesobig May 18 '21 at 17:52
  • @doublesobig Then you are obviously using it wrong. Please [edit] your question to show your updated code. But asking for tutorials is [off-topic](https://stackoverflow.com/help/on-topic) for StackOverflow. There are plenty of questions on StackOverflow, and tutorials elsewhere online, that show how to use `dynamic_cast` correctly. Or get yourself some [good C++ books](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). – Remy Lebeau May 18 '21 at 17:55

2 Answers2

1

I think you have a possible option if you don't mind adding some helper functions:

class Bitmap {
    ...
public:
    virtual std::string name() const = 0;
};

...

class Meg7 : MegFamily {
    ...
public:
    std::string name() const override { return "Meg7"; }
};

Then (like some of the other comments have suggested) you'll want to change the

    std::multimap<char, Bitmap> BMPLibrary;
    ...
    BMPLibrary.insert(std::pair<char, Bitmap>('A', meg7_A));

to

    std::multimap<char, std::unqiue_ptr<Bitmap>> BMPLibrary;
    ...
    BMPLibrary.insert(std::pair<char, std::unqiue_ptr<Bitmap>>('A', std::make_unique<Meg7>(std::move(meg7_A))));
    // or you can just use emplace, which is a bit less verbose
    BMPLibrary.emplace('A', std::make_unique<Meg7>(std::move(meg7_A)));

since when you use abstract and base classes, you'll generally always need it to be a pointer to the base class, rather than just the base class.

Finally, to get the final name you can simply use:

            std::cout << "Type of found element: " << pairFound->second->name() << std::endl;
mattlangford
  • 1,260
  • 1
  • 8
  • 12
0

Thanks for your help. I changed the class definitions a bit as I think it may be more elegant

class Bitmap {
public:
    Bitmap(std::vector<int> BMPData)
        : m_data(BMPData)
    {}
    virtual ~Bitmap() = default;
    inline std::vector<int> getBMPData() const { return m_data; }
    //inline void setBMPData(std::vector<int> data) { m_data = data; }
    void printBMPData() const;
    virtual std::string name() const = 0;
protected:
    std::vector<int> m_data;
};

class MegFamily : public Bitmap {
protected:
    uint8_t m_OverallSize = 0;
    uint8_t m_xDisplacement = 0;
    uint8_t m_yDisplacement = 0;
    uint8_t m_width = 0;
    uint8_t m_height = 0;
public:
    MegFamily(uint8_t OverallSize, uint8_t xDisplacement, uint8_t yDisplacement, uint8_t width, uint8_t height, std::vector<int> BMPData)
        : m_OverallSize(OverallSize), m_xDisplacement(xDisplacement), m_yDisplacement(yDisplacement), m_width(width), m_height(height),
        Bitmap(BMPData)
    {}
    virtual void hello() const { std::cout << "MegFamily" << std::endl; }
    inline uint8_t getOverallSize() const { return m_OverallSize; }
    inline uint8_t getXDisplacement() const { return m_xDisplacement; }
    inline uint8_t getYDisplacement() const { return m_yDisplacement; }
    inline uint8_t getWidth() const { return m_width; }
    inline uint8_t getHeight() const { return m_height; }
};

class Meg5 : public MegFamily
{
public:
    virtual void hello() const { std::cout << "Meg5" << std::endl; }
    std::string name() const override { return "Meg5"; }
    Meg5(uint8_t OverallSize, uint8_t xDisplacement, uint8_t yDisplacement, uint8_t width, uint8_t height, std::vector<int> BMPData)
        : MegFamily{ OverallSize, xDisplacement, yDisplacement, width, height, BMPData} {};
    ~Meg5() {};
};
class Meg7 : public MegFamily
{
public:
    virtual void hello() const { std::cout << "Meg7" << std::endl; }
    std::string name() const override { return "Meg7"; }
    Meg7(uint8_t OverallSize, uint8_t xDisplacement, uint8_t yDisplacement, uint8_t width, uint8_t height, std::vector<int> BMPData)
        : MegFamily{ OverallSize, xDisplacement, yDisplacement, width, height, BMPData} {};
    ~Meg7() {};
};

class ChineseFont : public Bitmap
{
public:
    ChineseFont(std::vector<int> BMPData)
        : Bitmap(BMPData) {};
    std::string name() const override { return "ChineseFont"; }
};

Additionally I implemented your recommended ideas

BMPData BMPSomeChineseName{0x00,0x38,0x27,0x24,0x24,0x24,0x24,0x24,0x24,0xe4,0x04,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x09,0x09,0x08,0x07,0x00,0x00 };
BMPData BMPmeg5A{ 0x01, 0x15, 0x05, 0x02 };
BMPData BMPmeg7A{ 0x7E, 0x09, 0x09, 0x09, 0x7E };


Meg5 meg5_A = Meg5(5, 0, 0, 4, 5, BMPmeg5A);
Meg7 meg7_A = Meg7(6,0,0,5,7, BMPmeg7A);
ChineseFont SomeChineseName = ChineseFont(BMPSomeChineseName);

std::multimap<char, std::unique_ptr<Bitmap>> BMPLibrary;
    BMPLibrary.emplace('A', std::make_unique<Meg5>(std::move(meg5_A)));
    BMPLibrary.emplace('A', std::make_unique<Meg7>(std::move(meg7_A)));
    BMPLibrary.emplace('\u2ed8', std::make_unique<ChineseFont>(std::move(SomeChineseName)));

Now it's possible to determine the type at runtime. Thank you!