如何在C++ 11中包装类的每个成员函数的调用?

Ale*_*lex 21 c++ c++11

Herb Sutter在谈论C++ 11和并发时问了这个问题(见这个视频)

这里的关键思想是拥有一个非锁定类X,其中每个函数调用都应该使用在函数后解锁的锁来进行修饰.

然而,Herb Sutter随后漂移并提出了一种基于仿函数的方法.我想知道是否有可能使用C++ 11以锁定方式包装每个函数调用并以通用方式解锁类(不手动包装每个函数调用).

class X {
  public:
    X() = default;
    void somefunc(arg1 x1, arg2 x2, ...);
    void somefunc2(arg1 x1, arg2 x2, ...);
    /* and more */
};

// herb admits one way to make all functions *available*
// in another class is by derivation

class XX : public X {
  public:
    XX() = default;
    // all functions available in NON overloaded form...
};
Run Code Online (Sandbox Code Playgroud)

还有装饰模式

class XXX {
  public:
    XXX(X &x) : m_x(x) {}

    // explicitly call each wrapped function ... done for each class separately.
    void somefunc(arg1 x1, arg2 x2, ...);
    void somefunc2(arg1 x1, arg2 x2, ...);
  private:
    class X& m_x;
};
Run Code Online (Sandbox Code Playgroud)

但有这样的事情是可能的:

template<>
class wrap_everything;

wrap_everything<X> x;
x.somefunc(x1,x2,...); // this is then locked.
Run Code Online (Sandbox Code Playgroud)

为了完整起见,这是草药sutter的基于仿函数的方法:

template <class T> class locker {
  private:
    mutable T m_t;
    mutable std::mutex m_m;
  public:
    locker( T t = T{} ) : m_t(t) {}
    template <typename F>
    auto operator()(F f) const -> decltype(f(m_t)) {
      std::lock_guard<mutex> _{m_m};
      return f(t);
    }
};


// usage 
locker<std::string> s;
s([](string &s) {
   s += "foobar";
   s += "barfoo";
});
Run Code Online (Sandbox Code Playgroud)

Jon*_*ely 16

问题是关于EXECUTE-AROUND模式.我在https://gitlab.com/redistd/redistd/blob/master/include/redi/exec_around.h上制作了一个通用(但很少经过测试)的EXECUTE-AROUND POINTER实现.

这允许:

struct X { void f() { } };
auto x = mutex_around<X>();
x->f();  // locks a mutex for duration of call to X::f
Run Code Online (Sandbox Code Playgroud)

关于如何围绕模式执行的家族的更深入的解释可以在这里找到(pdf)


Ada*_*son 11

我不相信在当前的C++中有一种可移植的通用方法.如果模板能够将重载集作为模板参数(我非常希望在C++ 14中看到很多原因),并且调用站点可以从更改x.y(z)x->y(z),我认为它可能已经完成使用代理和重载operator->.否则,执行此类操作的最佳通用方法是使用面向C++的面向方面编程框架(例如AspectC++).

但是,能够包装每个成员函数调用只是故事的一半.根据接口原则,类的接口是提及类的函数,并提供类.这包括与类相同的命名空间中的公共成员函数,友元函数和自由函数.能够以包装的方式将实例传递给这些函数是一个比仅仅包装成员函数调用更微妙的问题,这是Sutter的方法显示真正的力量和灵活性的地方.

  • [经典Stroustrup论文](http://www.stroustrup.com/wrapper.pdf)讨论了包围成员函数调用的代理+`operator->`技术. (8认同)
  • +1提及AOP,其目的正是为了回答这类问题. (3认同)

n. *_* m. 6

不可能完全按照自己的意愿去做,但是接近的是可行的.

#include <iostream>

class Foo {
  public:
    void one (int x) {
        std::cout << "Called Foo::one(" << x << ")\n";
    }
    void two (int x, double y) {
        std::cout << "Called Foo::two(" << x << ", " << y << ")\n";
    }
};

class ScopeDecorator {
  public:
    ScopeDecorator() {
        std::cout << "Enter scope\n";
    }
    ~ScopeDecorator() {
        std::cout << "Exit scope\n";
    }
};

template <class Wrappee, class Wrapper>
class Wrap {
  public:
    Wrap (Wrappee& w) : wrappee(w) {}
    template <typename rettype, typename... argtype>
        rettype call (rettype (Wrappee::*func)(argtype...), argtype... args)
        {
            Wrapper wrapper;
            return (wrappee.*func)(args...);
        }
  private:
    Wrappee& wrappee;
};

int main ()
{
    Foo foo;
    Wrap<Foo, ScopeDecorator> wfoo(foo);
    wfoo.call(&Foo::one, 42);
    wfoo.call(&Foo::two, 32, 3.1415);
}
Run Code Online (Sandbox Code Playgroud)