5
votes

I'm trying to find a way to call a number of class member functions, each having differing parameters, with certain known functionality happening before and after the call.

This wrapper function is what I've tried, but for example the final call to it doesn't compile with error:

'bool Wrapper(Work * ,std::function< bool(Args...)>,Args &&...)' : could not deduce template argument for 'std::function< bool(double,std::string,Args...)>' from 'std::_Bind< true,bool,std::_Pmf_wrap< bool (__thiscall Work::* )(double,std::string),bool,Work,double,std::string>,Work *const >'

class Work
    {
    public:
        void DoWork(int a, double b, string c);

    private:
        void Pre() {};
        void Post() {};
        bool Step1() { return true; }
        bool Step2(int) { return true; }
        bool Step3(double, string) { return true; }
    };

template<typename... Args>
bool Wrapper(Work *work, std::function<bool(Args...)> func, Args&&... args)
    {
    work->Pre();
    bool ret = func(std::forward<Args>(args)...);
    work->Post();
    return ret;
    }

void Work::DoWork(int a, double b, string c)
{
    if (!Wrapper<>(this, std::bind(&Work::Step1, this))) // error
        return;
    if (!Wrapper<int>(this, std::bind(&Work::Step2, this), a)) // error
        return;
    if (!Wrapper<double, string>(this, std::bind(&Work::Step3, this), b, c)) // error
        return;
}

int main()
{
    Work work;
    work.DoWork(1, 2.0, "three");
    return 0;
}

(Putting the pre- and post- functionality inside the steps would seem at first glance far preferable but that's undesirable as the above is a grossly simplified example of the actual code, and the steps have multiple places of returns, and no tests.)

I thought the explicit template arguments would make the template resolution possible. What am I doing wrong?

2
An alternative would be to pass all the parameters to std::bind() and to make the wrapper something like bool Wrapper(Work *work, std::function<bool()> func), but maybe I'm basing this on the simplification of the example.stefaanv

2 Answers

4
votes

With C++11, std::bind can be replaced by lambda and you can remove template for wrapper :

class Work
{
    public:
        void DoWork(int a, double b, string c);

    private:
        void Pre() {};
        void Post() {};
        bool Step1() { return true; }
        bool Step2(int) { return true; }
        bool Step3(double, string) { return true; }

        friend bool Wrapper(Work *work, std::function<bool()> func);
};

bool Wrapper(Work *work, std::function<bool()> func)
{
    work->Pre();
    bool ret = func();
    work->Post();
    return ret;
}

void Work::DoWork(int a, double b, string c)
{
    if (!Wrapper(this, [this]() -> bool { return this->Step1(); }))
        return;
    if (!Wrapper(this, [this, a]() -> bool { return this->Step2(a); }))
        return;
    if (!Wrapper(this, [this, b, c]() -> bool { return this->Step3(b, c); }))
        return;
}

int main()
{
    Work work;
    work.DoWork(1, 2.0, "three");
    return 0;
}

Example : http://coliru.stacked-crooked.com/a/2cd3b3e2a4abfcdc

4
votes

returned type of std::bind or lambda are not std::function and it would be ambiguous which std::function to construct from them.

One solution is to allow any functor and don't use std::function

template<typename F, typename... Args>
bool Wrapper(Work &work, F&& func, Args&&... args)
{
    work.Pre();
    const bool ret = std::forward<F>(func)(std::forward<Args>(args)...);
    work.Post();
    return ret;
}

Demo