0

I want to use a C++ map of function to execute some subroutines. Since, such a map of function requires to have the same type of input and output for all the mapped functions, while my functions can have different input type, I though I could made all these different types derive from a SuperParentClass.

However, this idea gives me some troubles. It does not compile. Indeed, the compiler capture the derived type and not the parent class. But if you I directly the parent class in my functions, I won't be able to use their specialties inside my functions (so I come for help).

Here is my MWE:

#include <functional>
#include <iostream>
#include <map>
#include <string>
#include <math.h> 


class SuperParentClass
{
private:
    /* data */
public:
    std::string nameClass;
};

class ChildClassRectangle : public SuperParentClass
{
private:
    /* data */
public:
    int longueur=2;
    int largeur=2;
};

class ChildClassCircle : public SuperParentClass
{
private:
    /* data */
public:
    int radius=2;
};

int areaRect(ChildClassRectangle rect)
{
    std::cout << "areaRect "<< rect.largeur*rect.longueur << std::endl;
    return 0;
}

int areaCircle(ChildClassCircle circ)
{
    std::cout << "areaCircle "<< 3.14*pow(circ.radius,2) << std::endl;
    return 0;
}


int main()
{  
    std::cout << "TEST " << std::endl;
    std::map<std::string, SuperParentClass> mapObject;
    ChildClassCircle circ;
    circ.radius=2;
    mapObject["Circle"]=circ;
    ChildClassRectangle rect;
    rect.longueur=1;
    rect.largeur=3;
    mapObject["Rectangle"]=rect;
    
    using CallFunc = std::function<int(SuperParentClass)>;
    std::map<std::string, CallFunc> mapFunction;
    // mapFunction["Circle"]=areaCircle;
    // mapFunction["Rectangle"]=areaRect;

    // mapFunction["Circle"](mapObject["Circle"]);
    // mapObject["Rectangle"].largeur=4;
    // mapFunction["Rectangle"](mapObject["Rectangle"]);
};

The last 5 lines are commented are what I want to do that is not working.

R. N
  • 707
  • 11
  • 31
  • 1
    Does this answer your question? [What is object slicing?](https://stackoverflow.com/questions/274626/what-is-object-slicing) – Thomas Sablik Apr 30 '21 at 13:24
  • `std::variant` and `std::function)>`? – Jarod42 Apr 30 '21 at 13:24
  • Your container could contain smart pointers instead of objects and `area` could be a virtual member function. – Thomas Sablik Apr 30 '21 at 13:26
  • @ThomasSablik About the link, I agree that it is related, but I don't see how is it solving my issue. If you have an idea about, please share it :) About the container with a virtual function, I think you want to override it for each child class. It was an option I wanted to avoid but I guess it can be an alternative solution. – R. N Apr 30 '21 at 13:31
  • @Jarod42 The problem with `variant` or `any` is that this is a type by itself, so you need to recast after entering in every function. This is exactly why I though about the Parent vs Child classes, to avoid to cast all the time. – R. N Apr 30 '21 at 13:39
  • The link describes that you can't store a child object in a parent object but you can store a pointer to a child object in a pointer to a parent object. That's your actual problem. `mapObject` doesn't contain the necessary data to calculate the area because of object slicing. Even with casting you're not able to solve it. – Thomas Sablik Apr 30 '21 at 13:42
  • There is no polymorphism without pointers or references in C++. – Thomas Sablik Apr 30 '21 at 13:47

1 Answers1

1

Rather than having a map go to functions it can map to a SuperParentClass pointer. Then use a virtual function in each class to encapsulate the desired behavior.


int areaRect(ChildClassRectangle rect);
int areaCircle(ChildClassCircle circ);

class SuperParentClass
{
public:
    virtual int area() = 0;
    ... // more code...
};

class ChildClassRectangle : public SuperParentClass
{
public:
    virtual int area() override
    {
        return areaRect(*this);
    }
    ... // more code...
};

class ChildClassCircle : public SuperParentClass
{
public:
    virtual int area() override
    {
        return areaCircle(*this);
    }
    ... // more code...
};

To eliminate the pass by value of classes, simply move the functions into the area() functions.

Now function calls are directly available without a separate map.

Here is a rough version that probably doesn't work exactly; but conveys the idea.

void main()
{
    std::map<std::string, SuperParentClass*> mapObject;
    ...
    mapObject["Circle"]=&circ;
    ...
    mapObject["Rectangle"]=&rect;

    ...
    const int circleReturn = mapObject["Circle"]->area();
    // Cannot access derived properties here
    // mapObject["Rectangle"]->largeur=4; // No longer valid.
    const int rectReturn = mapObject["Rectangle"]->area();
}

Pointers aren't required here; as objects can be emplaced on the container; or some other type of data structuring. This is simply meant to show using a virtual function is a simpler approach and better design.

thelizardking34
  • 338
  • 1
  • 12
  • Usually the container should own the objects. I prefer `std::map>`. Have you tried to compile the code? `mapObject["Rectangle"]->largeur=4;` won't work. The base class doesn't know `largeur`. The base class needs a virtual destructor. – Thomas Sablik Apr 30 '21 at 13:49
  • That's a good point on larguer; will edit to fix. Will clarify this isn't complete implementation – thelizardking34 Apr 30 '21 at 14:00
  • 1
    The base class needs a virtual destructor. – Thomas Sablik Apr 30 '21 at 14:19