2009-08-13 12 views
4

boost :: functionとboost :: bind(実際にはstd :: tr1 ::関数とバインド)がまだ使用中に削除されている状況があります。これは安全ですか?私は通常それを避けるだろうが、問題のコードは少し定着しており、私の唯一の他の選択肢は新しいスレッドを追加することです。delete boost function in use while

typedef function<int(int)> foo_type; 

foo_type* global_foo = NULL; 

int actual_foo(int i, Magic* m) 
{ 
    delete global_foo; 
    return m->magic(i); 
} 

int main() 
{ 
    Magic m; 
    global_foo = new foo_type(bind(&actual_foo, _1, &m); 

    return (*global_foo)(10) 
} 

バインドされたパラメータは、通常は整数型(実際のコードのintとポインタ)であり、参照ではありません。

+0

要約すると、実装のoperator()呼び出しが何をするかによって危険かもしれません。 。あなたは何をしているのか保証していないので、それは危険です。 boost :: functionの現在の実装は安全です。 –

答えて

5

boost::functionまたはstd::tr1::functionsはコピー可能なオブジェクトです。したがって、一般的には、それらを割り当てる理由は絶対にありません。ただ価値を渡すだけです。

彼らはよく実際の例ほとんどのために最適化されている...だから値によってそれらを渡します、あなたが示唆したコードは危険です

typedef function<int(int)> foo_type; 

foo_type global_foo; 

int actual_foo(int i, Magic* m) 
{ 
    delete global_foo; 
    return m->magic(i); 
} 

int main() 
{ 
    Magic m; 
    global_foo = bind(&actual_foo, _1, &m); 

    return global_foo(10) 
} 

このコードを実行します。

#include <boost/function.hpp> 
    #include <boost/bind.hpp> 
    #include <iostream> 
    using namespace std; 

    boost::function<void()> *glb; 

    struct Data { 
      int x; 
      Data(int _x = 0) : x(_x) { cout<<"ctor:"<<this<<endl; } 
      ~Data() { cout<<"dtor:"<<this<<endl; } 
      Data(Data const &p) {x=p.x; cout<<"ctor:"<<this<<endl; } 
      Data const &operator=(Data const &p) { x=p.x; cout<<this<<"="<<&p<<endl; return *this; } 
    }; 

    void func(Data const &x) 
    { 
      delete glb; 
      cout<<&x<<endl; 
    } 

    int main() 
    { 
      glb=new boost::function<void()>(boost::bind(func,Data(3))); 

      (*glb)(); 
      return 0; 
    } 

あなたはでしょうcout<<&x<<endlに示されている破棄されたオブジェクト(同じポインタ値を持つdtor)にfuncでアクセスしようとしていることが判明しました。

あなたが破壊するとき、あなたの関数を使用すると、また、あなたが持っている バインドさパラメータを破壊し、それがglobal_function

編集でdestoryedたbecuase Data const &xがunavailibleなるオブジェクトので:あなたが何かを持っている場合は

:コメントを Clearification like

map<string,function<void()> > calls; 

void delete_key(){ 
    calls.erase("key"); 
} 

main() 
{ 
    calls["key"]=delete_key; 

    // Wrong and dangerous 
    // You delete function while in use 
    calls["key"](); 

    // Correct and safe 
    // You create a copy of function so it would not 
    // be deleted while in use. 
    function<void()> f=calls["key"]; 
    f(); 
} 
+1

あなたはもうglobal_fooを削除しないことを確かめてください。 – Salgar

+0

私は問題をあまりにも単純化したように見えます。 global_fooは値によってマップに格納されます。呼び出されている間、関数<>をマップから削除することは可能です。パラメータの存続期間が保証されない場合(データ構造体)、危険であることはわかっています。しかし、私はバインドされたパラメータが整数型であることを明確にしました –

2

編集:(チャールズ・ベイリーに)もう少しこの回答を議論した、私はそれは、ブーストの実装::機能に依存しますので、これは安全ではないと信じています。

我々はこのようなものになりますactual_foo入力し、コールスタック:actual_fooの実行が完了したときに

actual_foo 
boost::function::operator() 
main 

だから、我々は boost::functionoperator()コードに戻ってジャンプし、このオブジェクトが削除されました。これは問題であることは保証されていません - それは delete thisを呼び出すようなビットですが、削除されたオブジェクトのメンバ関数で行うことを非常に慎重にする必要があります。仮想関数を呼び出したり、データメンバーを使用することはできません。

問題は、ラップされた関数がoperator()で実行されていることを保証していないことに気付いていません。私のプラットフォームでは危険なことは何もしていないようです(valgrindは不平を言っていません)が、別のプラットフォームで、異なる実装で、あるいは別のコンパイルフラグを使って、オブジェクトが削除されると許可されません。例えば、メンバ変数を使ってデバッグ情報を書き出すことができます。

私はこれが危険なことであると信じています。これは特定の状況下では未定義の動作を引き起こす可能性があります。

さらに注:

私はそれが関数ポインタを呼び出した後、実際にやっているブーストで簡単に見てました。ここでは:のoperator()関数の687行で、私の解釈はすぐに戻り値を返し、何もしないので、実際には、その実装では、あなたはokですが、それに関するコメントまだ危険なままである。私はこれがOKであると信じて

:以下

オリジナルの答え...私は非常によく、コードの間違ったビットを見つけ、および/またはそれが間違って理解している可能性があることに注意してください。 actual_fooと入力すると、boost::bindboost::functionオブジェクトが完了し、実際の関数actual_fooを実行しています。

valgrindでプログラムを実行して、私のプラットフォーム(gcc 4.2.4、Linux)でこれを調べました。ここで

は私が走ったプログラムは次のとおりです。

#include <boost/bind.hpp> 
#include <boost/function.hpp> 

class Magic 
{ 
public: 
    int magic(int i) 
    { 
     return 5; 
    } 
}; 

typedef boost::function<int(int)> foo_type; 

foo_type* global_foo = NULL; 

int actual_foo(int i, Magic* m) 
{ 
    delete global_foo; 
    return m->magic(i); 
} 

int main() 
{ 
    Magic m; 
    global_foo = new foo_type(boost::bind(&actual_foo, _1, &m)); 

    return (*global_foo)(10); 
} 

、ここではvalgrindの出力です:

$ valgrind ./boost_bind 
==17606== Memcheck, a memory error detector. 
==17606== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al. 
==17606== Using LibVEX rev 1804, a library for dynamic binary translation. 
==17606== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP. 
==17606== Using valgrind-3.3.0-Debian, a dynamic binary instrumentation framework. 
==17606== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al. 
==17606== For more details, rerun with: -v 
==17606== 
==17606== 
==17606== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 17 from 1) 
==17606== malloc/free: in use at exit: 0 bytes in 0 blocks. 
==17606== malloc/free: 1 allocs, 1 frees, 16 bytes allocated. 
==17606== For counts of detected errors, rerun with: -v 
==17606== All heap blocks were freed -- no leaks are possible. 

私はこれを行うために奇妙なことに思われること、しかし、言わなければなりません。可能であれば、この関数オブジェクトをスタック変数にするか、スタック変数のデストラクタで削除する(RAIIのように)、この関数オブジェクトの自動削除を使用することをお勧めします。より清潔で、安全で、恐ろしくなく、例外的に安全です。しかし、私はあなたがすでにそのことをすべて知っていると確信しています。コードをそのまま生かすのがよい理由があります。

+0

-1これは間違っています - コードは危険です – Artyom

+0

危険な理由を説明してください。あなたはそれが賢明ではない(私が同意する)、またはそれが未定義の行動を引き起こすことを意味しますか(まだ確信していません)? –

+0

あなたの答えにあなたの(Artyomの)点があることに注意してください。バインドオブジェクトが削除されていれば、actual_fooへの引数は削除されている可能性があります。しかし、引数はここで値渡しされているので、私はこれが当てはまるとは思わない。 iはintのローカルコピーであり、mはmain()で初期化されたスタック変数へのポインタのローカルコピーです。 –

関連する問題