2013-05-31 2 views
19

ハーブサッターは約C++ 11と同時実行性(this videoを参照)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... 
}; 

Decoratorパターン

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. 

完全性のために、これはハーブサッターのファンクタベースのアプローチです:

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"; 
}); 
+0

少なくともコンパイラ(gccなど)では、コンパイラはコードを変更しなくてもこれを実行できます。通常はプロファイリングに使用されますが、各関数呼び出しの前後に指定された関数への呼び出しを挿入することができます。あなたがロックを必要とする場所と実際には行っていない場所を整理するためのコードでは、それほど些細なことはありません。 –

+0

@JerryCoffin私はこれは、すべてのメンテナーがコード以外の場所でロックを探すのは致命的だと思います。 – Alex

+0

@Alex大きな質問です。あなたが言うように、SutterがC++とBeyond 2012でこのことを覚えてから、それをやり直したのを覚えています。多分、彼はC++ 14のフィーチャセットのヒントを捨てていたかもしれません。 –

答えて

15

質問は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 

の家族ができ周りパターンの作業を実行する方法の詳細深さで、について説明興味がある人々のためにhere (pdf)

+0

私はそれが私が探しているものかどうかはまだ分かりませんが、私はすでに興奮しています! – Alex

+0

+1ハッ、右! 'operator->'の連鎖動作!非常に良い解決策。 –

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); 
} 
11

現在のC++でこれを行う汎用的な方法はないと思います。テンプレートがテンプレートパラメータとしてオーバーロードセットを取ることができれば(多くの理由からC++ 14でよく見たい)、コールサイトをx.y(z)からx->y(z)に変更することができましたプロキシとオーバーロードされたoperator->で行う必要があります。そうでなければ、このようなことを行う最も一般的な方法は、Aspect Oriented ProgrammingフレームワークをC++に使うことです(AspectC++など)。

各メンバ関数の呼び出しをラップすることができるのは、実際には半分の話です。 Interface Principleによると、クラスのインタフェースは、クラスに言及し、クラスとともに提供される関数である。これには、パブリックメンバー関数、フレンド関数、クラスと同じ名前空間内のフリー関数が含まれます。このような関数にラップされた形でインスタンスを渡すことができるのは、単純にメンバ関数呼び出しをラッピングすることよりもはるかに微妙な問題です.Sutterのアプローチが真の力と柔軟性を示すところです。

+3

+1この種の質問に正確に答えることを目指しているAOPに言及してください。 –

+7

[Classic Stroustrup paper](http://www.stroustrup.com/wrapper.pdf)では、プロキシとオペレータのブラケットメンバー関数呼び出しに関する技術について説明しています。 –

0

を見つけ、私はまた、idom周りの実行の一般的な実装を書いた:

それから、スレッドセーフオブジェクトを作成する方法についての例と

https://github.com/ArnaudBienner/ExecuteAround/blob/master/ExecuteAround.h

https://github.com/ArnaudBienner/ExecuteAround/blob/master/main.cpp#L78

念のために、ジョナサンが提供するものは、すでに偉大に見える、と私はおそらくいくつかを必要とするので、掃除。

-1

これは完全に可能であり、Stroustrup以外には長年提案されており、彼の元の提案はまだ利用可能です。基本的なアイデアは、まずオペレータ->によって返された一時的なオブジェクトのコンストラクタとデストラクタ2つのレベルとロック/アンロックミューテックスのオペレータ->をオーバーライドすることであるwww.stroustrup.com/wrapper.pdf

を参照。

第2演算子->は、メソッドが呼び出されるオブジェクトのポインタを返します。

関連する問題