Search code examples
c++c++11pointer-to-member

Hold a collection of member function pointers and decorated member function pointers


As a part of a much larger project, one of my objects (Thing in MWE) has a set of filters (filterStrong, filterWeak) defined on it. The goal is to use all the implemented filters in complexFilteringProcedure, where the user could chose the filtering rule through a parameter, and the function itself would depend on the success of the filtering rule chosen. The function complexFilteringProcedure would act on an object of type Thing, and call one of its private methods (filtering rules) depending on the parameter.

I implemented this by holding a vector of all possible filters in filteringOptions and implementing a single public filtering interface, filterUsingRule. Ideally, this would allow me to later on add new filtering rules to the project as I need them, and only modify the setFilteringFunction where the filter list is initialized.

Now, I started writing a new set of filtering rules, and realized all of them could be obtained by decorating the current filtering rules all in the same manner (softenFilter; please do correct me if "decorating" is the wrong expression here). I remembered reading into std::bind recently and taught, great. I would also like to add all the decorated filtering rules in my list of filteringOptions, that is, every original filter decorated with softenFilter.

Reading up a little bit more on std::bind, I think the possible reasons for my problems are twofold:

  • the return type of std::bind is a templated mess, and definitely not Thing::filteringFunction
  • I might be binding the this referring to the calling object when defining softStrong and softWeak

But, I am stuck further than that, not sure how to look for a solution to my specific problem. My main question are: Can this functionality be achieved? (functionality of filterUsingRule) and further, Can this functionality be achieved elegantly? (I know I could always define a set of functions bool softStrong(int param) { return softenFilter(filterStrong, param); } that manually bind the filters to the decorator, but I was hoping that std::bind or some new C++ magic would help with that).

The MWE recreating what I have successfully done and what I would like to achieve is as follows:

#include <iostream>

#include <vector>
#include <functional>

class Thing{
    private:
        int basicFilter;

        typedef  bool (Thing::*filteringFunction)(int);

        static std::vector<filteringFunction> filteringOptions;

        bool filterStrong(int parameter) {return parameter > basicFilter*2;}
        bool filterWeak(int parameter) {return parameter > basicFilter;}

        bool softenFilter(filteringFunction f, int parameter){
            if (!((this->*(f))(parameter)))
                return (this->*(f))(parameter+2);
            return true;
        }

        void setFilteringFunctions(void){
            Thing::filteringOptions.emplace_back(&Thing::filterStrong);
            Thing::filteringOptions.emplace_back(&Thing::filterWeak);

            auto softStrong = std::bind(&Thing::softenFilter,
                                        &Thing::filterStrong,
                                        std::placeholders::_1); // ok
            auto softWeak = std::bind(&Thing::softenFilter,
                                      &Thing::filterWeak,
                                      std::placeholders::_1); // ok

            filteringOptions.emplace_back(&softStrong); // how? 
            filteringOptions.emplace_back(softWeak); // how?                                                               
        }

    public:
        Thing(int basicFilter) : basicFilter(basicFilter){
            if (Thing::filteringOptions.empty())
                setFilteringFunctions();
        }

        bool filterUsingRule(int parameter, int rule = 0){
            return ((int)Thing::filteringOptions.size() > rule) &&
                   (this->*(Thing::filteringOptions[rule]))(parameter);
        }

};

std::vector <Thing::filteringFunction> Thing::filteringOptions(0);

void complexFilteringProcedure(Thing &aThing, int parameter, int rule){
    // do a lot of things
    if (aThing.filterUsingRule(parameter, rule))
        std::cout << "Filtering with " << rule << "successful" << std::endl;
    else
        std::cout << "Filtering with " << rule << "failed" << std::endl;
    // and some more things
}


int main(void){
    Thing myThing(5), otherThing(10);

    complexFilteringProcedure(myThing, 7, 0); // uses strong rule
    complexFilteringProcedure(otherThing, 7, 1); // uses weak rule

    complexFilteringProcedure(myThing, 7, 2); // how to do this correctly?
    complexFilteringProcedure(otherThing, 7, 3); // or this?
}

Solution

  • You might use std::function

    using filteringFunction = std::function<bool (Thing&, int)>;
    

    and then

    void setFilteringFunctions()
    {
        Thing::filteringOptions.emplace_back(&Thing::filterStrong);
        Thing::filteringOptions.emplace_back(&Thing::filterWeak);
    
        auto softStrong = std::bind(&Thing::softenFilter,
                                    std::placeholders::_1,
                                    &Thing::filterStrong,
                                    std::placeholders::_2
                                    );
        auto softWeak = std::bind(&Thing::softenFilter,
                                  std::placeholders::_1,
                                  &Thing::filterWeak,
                                  std::placeholders::_2);
    
        Thing::filteringOptions.emplace_back(&softStrong);
        Thing::filteringOptions.emplace_back(&softWeak);
        // or
        Thing::filteringOptions.emplace_back([](Thing& instance, int param){ 
            return instance.filterStrong(param + 2) });
    }