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

C++ 如何在C+中包装类的每个成员函数的调用+;11?,c++,c++11,C++,C++11,Herb Sutter在一次关于C++11和并发性的演讲中提出了这个问题(请参阅) 这里的关键思想是有一个非锁定类X,其中每个函数调用都应该用一个在函数之后解锁的锁来装饰 然而,Herb Sutter随后偏离了方向,提出了一种基于函子的方法。我想知道是否有可能使用C++11以一种通用的方式(而不是手动包装每个函数调用)用类的锁定和解锁来包装每个函数调用 还有装饰图案 class XXX { public: XXX(X &x) : m_x(x) {} // expli

Herb Sutter在一次关于C++11和并发性的演讲中提出了这个问题(请参阅)

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

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

还有装饰图案

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;
};
但是有没有这样的可能:

template<>
class wrap_everything;

wrap_everything<X> x;
x.somefunc(x1,x2,...); // this is then locked.
模板
班级包扎一切;
把所有东西都包起来;
x、 somefunc(x1,x2,…);//然后,这将被锁定。
为了完整性,这是herb 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";
});
模板类锁定器{
私人:
可变T m_T;
可变std::互斥m_m;
公众:
locker(T=T{}):m_T(T{}
模板
自动运算符(){
std::锁紧装置{m{m};
返回f(t);
}
};
//用法
储物柜;
s([](字符串和s){
s+=“foobar”;
s+=“巴福”;
});

你不可能完全按照自己的意愿去做,但接近的事情是可以做到的

#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);
}
#包括
福班{
公众:
无效一(整数x){

STD::Pc>我不相信在当前的C++中有一种可移植的通用方法。如果模板能够将过载集作为模板参数(我非常愿意在C++ 14中看到),并且调用站点可以从<代码> x.y(z)< /> >改为<代码> x> > y(z)。,我认为它可以用代理和重载<代码> >运算符> >代码>。否则,最好的通用方法是使用面向方面的C++编程框架(如APETCT+++)。
但是,能够包装每个成员函数调用实际上只是其中的一半。根据接口原则,类的接口是指提及类并随类一起提供的函数。这包括与e类。能够以包装的方式将实例传递给这样的函数是一个比仅仅包装成员函数调用更微妙的问题,这正是Sutter的方法显示真正强大和灵活性的地方。

问题是关于执行环绕模式。我做了一个通用的(但几乎没有测试过)执行指针在

这允许:

struct X { void f() { } };
auto x = mutex_around<X>();
x->f();  // locks a mutex for duration of call to X::f
struct X{void f(){};
自动x=互斥(mutex_-ound();
x->f();//在调用x::f期间锁定互斥锁

关于围绕execute-around模式家族如何工作的更深入的解释可以找到

对于任何感兴趣的人,我还编写了一个围绕execute-around idom的通用实现:

通过一个关于如何从中创建线程安全对象的示例:


为了记录在案,因为乔纳森提供的方案看起来已经很不错了,我的方案可能需要清理一下。

这是完全可能的,很久以前就有人提出了,而他最初的方案仍然有效。参见

基本上,这个想法是在两个级别重写操作符
->
,并锁定/解锁第一个操作符
->
返回的临时对象的构造函数和析构函数中的互斥体


第二个操作符
->
将返回调用该方法的对象的指针。

至少在某些编译器中是这样(例如,gcc)编译器可以做到这一点,而无需修改代码。通常用于分析,但您可以让它在每次函数调用之前和之后插入对指定函数的调用。对于代码来说,找出您想要锁的位置和您真正不想要锁的位置仍然是非常重要的。@JerryCoffin我想这对每个维护人员来说都是致命的在代码之外的其他地方寻找锁。在语言规则中,这很可能是完全不可能的。但是,我很欣赏这个问题的理论性质,我希望您知道这样一个事实,基于函子的方法在大多数情况下更有用、更合适。这不仅是可能的方法,而且是可行的“这也是更好的方法。@Alex:可能吧。当然,这不是一个实际的建议(因此作为评论发布,而不是回答)。是我,还是你在locker构造函数的初始化列表及其
操作符()中切换了
t
m\t
?还有,在你的
操作符()中切换了
lock\u guard
应该使用
m\m
而不是
m
+1来初始化
lock\u guard
来提及AOP,AOP正是为了回答这类问题。这讨论了将成员函数调用括起来的代理+
操作符->
技术。我还不确定这是否是我要寻找的,但它确实很有用我已经很兴奋了!+1哈,对!操作员的链接行为->
!非常好的解决方案。pdf的链接已失效:(
struct X { void f() { } };
auto x = mutex_around<X>();
x->f();  // locks a mutex for duration of call to X::f