0

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?
}
penelope
  • 8,251
  • 8
  • 45
  • 87
  • 1
    Sounds like a case for `std::function`? – Daniel Jour Apr 05 '18 at 16:29
  • Also note that there is no point having filterStrong be a member function if it doesn't need any data members or member functions. – Daniel Jour Apr 05 '18 at 16:34
  • @DanielJour please note what I wrote in my text: In the actual implementations, all of these methods require access to private members of the `Thing`. It would just complicate the already big MEW too much. – penelope Apr 05 '18 at 16:36
  • @DanielJour that's what I taught as well, but I don't know how to use `std::function` to refer to member functions, i.e. the functions requiring `this` as one of their arguments. – penelope Apr 05 '18 at 16:46

3 Answers3

3

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) });
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Yes, this is exactly the solution. I didn't know how to correctly formulate my question, which was _"how to specialize `std::function` to call a member function on any class instance"_, which was also previously answered here: https://stackoverflow.com/a/7582574/884412 . In the final solution, I left `filteringFunction` as is (it is needed for definition of `softenFilter`), but defined `typedef anyFilter std::function` as you suggest, to use as vector type: `std::vector`. Then it will happily accept both `anyFilter` and `filteringFunction` (more specialized). – penelope Apr 05 '18 at 17:14
0

You'll have to use a specialization of std::function as your vector element type. The key issue is that the object returned by std::bind() is not a bare function pointer. It is rather a Callable -- a function object -- it is some type (exactly what type is unimportant and in fact unspecified) that has an operator() with the appropriate return type which takes the appropriate parameters. This is exactly the role of std::function -- a type which can wrap any Callable of the correct signature in a way that lets you handle it with a known concrete type regardless of the actual type of the Callable.

typedef std::function<bool(int)> filteringFunction;
static std::vector<filteringFunction> filteringOptions;

// now can you store your member function pointers in
// filteringOptions after bind()ing the first parameter
// as you've already done

To satisfy the skeptics, here is the OP's code modified to use this technique.

#include <iostream>

#include <vector>
#include <functional>

class Thing{
    private:
        int basicFilter;

        typedef std::function<bool(int)> filteringFunction;

        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 (!f(parameter))
                return f(parameter + 2);
            return true;
        }

        void setFilteringFunctions(void){
            filteringFunction strong = std::bind(&Thing::filterStrong,
                this, std::placeholders::_1);
            filteringFunction weak = std::bind(&Thing::filterWeak,
                this, std::placeholders::_1);

            filteringFunction softStrong = std::bind(&Thing::softenFilter,
                this, strong, std::placeholders::_1);
            filteringFunction softWeak = std::bind(&Thing::softenFilter,
                this, weak, std::placeholders::_1);

            filteringOptions.emplace_back(softStrong);
            filteringOptions.emplace_back(softWeak);
        }

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

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

};

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

void complexFilteringProcedure(Thing &aThing, int parameter, int rule){
    // do a lot of things
    std::cout << "Filtering: " << aThing.filterUsingRule(parameter, rule) << 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 use soft strong rule?
    //complexFilteringProcedure(otherThing, 7, 3); // how to use soft weak rule?
}
TypeIA
  • 16,916
  • 1
  • 38
  • 52
  • If I use `std::function` as my vector element type, it will not let me add either a `filterStrong` nor `filterWeak` into `filteringOptions`, as they are both _member_ functions and require access to `this`, which is not reflected in the vector type. – penelope Apr 05 '18 at 16:45
  • Or did I misunderstand something about the intended usage? The example I posted really compiles and runs, would you be able to modify it using your suggestions so the commented lines would work as well? – penelope Apr 05 '18 at 16:47
  • @penelope You still have to use `std::bind()` to [bind the first parameter](https://stackoverflow.com/questions/7582546/using-generic-stdfunction-objects-with-member-functions-in-one-class) (the implicit `this` parameter, i.e. the object on which the member function is called) just as you're doing in your original code. And yes, it will really compile and run. When you get it working please consider reversing your downvote. – TypeIA Apr 05 '18 at 16:57
  • @penelope forget about bind, you can wrap up member function call into lambda... which can ass a std::function, or as a function pointer, if it is captureless. Bind was implemented for same reasons before c++11, but now you can do (obj.*ptr)(args); inside of closure – Swift - Friday Pie Apr 05 '18 at 16:59
  • 1
    @Swift You can do that. It will not be captureless because you have to capture `this`. Thus in the end the result is the same as what `bind()` will do internally. In my mind `bind()` yields cleaner code that more explicitly represents the intent. – TypeIA Apr 05 '18 at 17:00
  • yeah in this particular case it should be [&]. problem of bind is portability (it doesn't work as intended everywhere, especially on windows.. or with c++14 template parameter induction). it also was slanted for removal one time. Is result same or not is undetermined (implementation-defined) – Swift - Friday Pie Apr 05 '18 at 17:01
  • 1
    Please clarify my mind: does every function object you use need access to this object or it can be a free function or a method operating on another object? In the former case you are good, in the later case go for std::function as a reliable kickstart and ask about bind or lambda. – Red.Wave Apr 05 '18 at 17:05
  • @Red.Wave I believe the updated MWE contains everything now. The filter functions need access to a private member of the `Thing` class. There will be multiple instances of the class `Thing` and the private member in question will be initialized differently for each of the instances. – penelope Apr 05 '18 at 17:09
  • Unfortunately, it will not work with the proposed `filteringFunction` defined as `std::function` as that signature does not allow me to bind the `this` parameter anywhere. I got it working with the suggestion from @Jarod42's answer, `std::function` as well as with `std::function` (found in a different SO answer), as both of those allow to bind `this` (to either `Thing&` or `Thing*`). Please consider revising your answer, as it is incorrect as it stands (except the very first sentence, that I have to use a specialization of `std::function`). – penelope Apr 05 '18 at 17:30
  • @penelope That is not correct - you just have to use `bind()` correctly. I edited the answer to include complete code modified from your original. – TypeIA Apr 05 '18 at 18:39
  • @TypeIA I've tested out the code you posted. It does compile, but unfortunately, it is still not correct, as it will bind the methods of the _first initialized instance of class `Thing`_ as `filteringOptions` (in the example, `myThing`), and never call the methods of the other objects (ie. `otherThing`). If you add a simple `cout` that will print the value of `basicFilter` inside `filterStrong` and `filterWeak`, you will see that the line `complexFilteringProcedure(otherThing, 7, 1); ` calls the `filterWeak` method from `myThing` (`basicFilter==5`) and not `otherThing` (`basicFilter==10`). – penelope Apr 06 '18 at 14:12
  • Just try adding `std::cout << "basicFilter used: " << this->basicFilter << std::endl;` in `filterStrong` and `filterWeak`, you will see that the output is the same regardless on which object you call the method on, as you bound `this` (referring to `myThing`) to the first argument of `softenFilter` in `myThing` constructor where `filteringOptions` are initialized. Since you bound that specific instance to the `this` parameter, you can not influence or change on which instances the methods are being called (always the first initialized instance of the class `Thing`). – penelope Apr 06 '18 at 14:19
0
typedef std::function<bool(Thing*, int)> filteringFuction;

Now you can use static functions as well as std::bind and lambda or any callable that accepts an int and returns bool.

    static bool test(Thing*, int);
    static bool decoratee(Thing*, bool , int);
    this->filteringOptions.emplace_back([](Thing* sth, int x){return false;});
    this->filteringOptions.emplace_back(&Thing::weakFilter);
    this->filteringOptions.emplace_back(std::bind(decoratee, _1, false, _2));
    this->filteringOptions.emplace_back(&test);

    int param;
    for(auto& callee:this->filteringOptions)
        callee(this,param);
Red.Wave
  • 2,790
  • 11
  • 17
  • Thank you for the attempted answer. Two things confuse me about your approach however. First (less important), my `filteringOption` is a static member variable, so it shouldn't be called on an instance of the class (ie. `this`). Secondly, it seems like you are binding a specific instance of the `Thing` class to every callable added to `filteringOptions`. Meaning, `filteringOptions[0]` will be always called on the object used in `filteringOptions` initialiaztion, `filteringOptions[1]` is always called on `sth`. I want to be able to use any `filteringOptions[i]` on any object of type `Thing`. – penelope Apr 11 '18 at 13:58
  • Sorry, I haven't managed to understand your intention yet. I will edit the post once I do. You seem to be a C# programmer, right? I guess you are trying to implement a somewhat static event with signature bool(Thing,int). If that is the case, minor modification to the reply will solve the problem. Let me learn what you need. – Red.Wave Apr 11 '18 at 14:19
  • No, I'm actually natively C++, but as it happens sometimes in science and research, researcher-produced code often lacks a bit behind the standards. I'm finally trying to catch up with C++11. What I need has been already answered and accepted, implemented and integrated in my real project. I'd like to store all possible filtering rules implemented in a class in a single vector, and call them through a unified member function which allows the choice of rule through a parameter. In addition to `pure` filtering rules, I'd also like to be able to mix the pure ones with the decorated ones. – penelope Apr 12 '18 at 12:59
  • The problem I am currently seeing with your answer, as well as the one from @TypeIA, is that you are binding a specific instance of class `Thing` to all those member function calls that you are storing in `filteringOptions`. So with your code, whenever I call a filter through a filtering option, it will be called on the same instance it was initialized with, rather than the current object you are working on and trying to call a filter on. – penelope Apr 12 '18 at 13:02
  • So you a collection of functions to operate on any desired instance. I did not take the time to declare a function object class. You can figure that one out yourself. The post is edited now. – Red.Wave Apr 12 '18 at 13:25