Search code examples
c++c++11polymorphismfunctor

How to instruct a polymorphic functor in a container to do its job and return results?


I have a std::vector<> of pointers to a base functor. This container, a calculator class, should be able to hold onto an arbitrary number of these function objects that calculate arbitrary statistics.

The part I'm struggling with is how to write a method for the container class, that iterates over these pointers, picks the one you're interested in out of hte group, tells it to do its job by calling operator(), then returns the result.

Here's some code that illustrates the idea:

#include <iostream>
#include <memory> //shared_ptr
#include <vector>

class BaseFunctor{
    virtual double operator()(std::vector<double> inputs) = 0;  
};

class Sum : public BaseFunctor{
    double operator() (std::vector<double> inputs){ 
        double sum (0.0);
        for(auto elem : inputs)
            sum += elem;
        return sum;
    }
};

class Prod : public BaseFunctor{
    double operator() (std::vector<double> inputs){ 
        double sum (1.0);
        for(auto elem : inputs)
            sum *= elem;
        return sum;
    }
};

class Calculator {
private:
    std::vector<std::shared_ptr<BaseFunctor>> m_stats;
    std::vector<double> m_simple_data;
public:
    Calculator() 
        : m_simple_data({1.0, 2.0, 3.0}) {};
    void addStat(std::shared_ptr<BaseFunctor> stat) { 
        m_stats.push_back(stat); 
    }
    // calculate stat?
};

int main() {

    Calculator calc;
    calc.addStat(std::make_shared<Sum>());
    calc.addStat(std::make_shared<Prod>());
    
    return 0;
}

Now, how could I tell calc to give me the product, and only the product? Especially when I can't control what statistics the user ends up being interested in?

I imagine I could store the pointers in a map, instead, but that seems redundant: making the key have the same name as the class.

Here is an idea, but it doesn't compile.


Solution

  • This works, but I'm open to comments/criticisms/suggestions.

    #include <iostream>
    #include <memory> //unique_ptr
    #include <map>
    #include <vector>
    #include <string>
    #include <functional>
    
    class BaseFunctor{
    public:
        virtual double operator()(std::vector<double> inputs) = 0;  
        virtual std::string name() = 0;  
    };
    
    class Sum : public BaseFunctor{
        double operator() (std::vector<double> inputs){ 
            double sum (0.0);
            for(auto elem : inputs)
                sum += elem;
            return sum;
        }
        std::string name() { return "sum"; }
    };
    
    class Prod : public BaseFunctor{
        double operator() (std::vector<double> inputs){ 
            double prod (1.0);
            for(auto elem : inputs)
                prod *= elem;
            return prod;
        }
        std::string name() { return "prod"; }
    };
    
    class Calculator {
    private:
        std::map<std::string, std::unique_ptr<BaseFunctor> > m_stats;
        std::vector<double> m_simple_data;
    public:
        Calculator() 
            : m_simple_data({1.0, 2.0, 5.0}) {};
            
        void addStat(std::unique_ptr<BaseFunctor> stat) { 
            m_stats.insert(
                std::pair<std::string, std::unique_ptr<BaseFunctor>>(
                    stat->name(),
                    std::move(stat)
                )   
            );
            //m_stats.push_back(std::move(stat)); 
        }
        
        double calc(std::string op_name){
            return (*m_stats[op_name])(m_simple_data);
        }
    };
    
    int main() {
    
        Calculator thing;
        thing.addStat(std::unique_ptr<BaseFunctor>(new Sum()) );
        thing.addStat(std::unique_ptr<BaseFunctor>(new Prod()) );
    
        std::cout << "sum: " << thing.calc("sum") << "\n";
        std::cout << "prod: " << thing.calc("prod");
        
        return 0;
    }