2

私は現在、コールバックを介して他の多くのオブジェクトに通知できるクラスを持っています:

class Callback {
   virtual NodulesChanged() =0;
   virtual TurkiesTwisted() =0;
};

class Notifier
{
  std::vector<Callback*> m_Callbacks;

  void AddCallback(Callback* cb) {m_Callbacks.push(cb); }
  ...
  void ChangeNodules() {
     for (iterator it=m_Callbacks.begin(); it!=m_Callbacks.end(); it++) {
        (*it)->NodulesChanged();
     }
  }
};

これを変更してブーストのシグナルとスロットを使用することを検討しています。これは、特に、呼び出し先が削除されたときにポインターがぶら下がる可能性を減らすのに役立つためです。ただし、現状では、boost のシグナルは、関数オブジェクトを処理する方向に向いているようです。接続と通知の側面に対処するためにシグナルとスロットを使用し、コールバック インターフェイスを引き続き使用するようにコードを適応させる最良の方法は何でしょうか?

4

3 に答える 3

2

boost::signals は、シグナルに何をバインドするかに関して非常に柔軟です。関数オブジェクトを使用できますが、関数ポインターを使用するか、boost::bind を使用して、ほぼすべての種類の関数を関数オブジェクトにすることもできます。より良い方法があるかもしれませんが、これはあなたの例がどのように見えるかです。

#include <boost/signals.hpp>

class Notifier
{
public:
    boost::signal< void() > NodulesChanged;

    void ChangeNodules()
    {
        //Just call the signal and all connected slots will be called.
        NodulesChanged();
    }
};

コールバックを追加するには、単純に

void callback1()
{
    //do callback stuff
}

void callback2()
{
    //do callback stuff
}

int main()
{
    Notifier n;
    n.NodulesChanged.connect(&callback1);
    n.NodulesChanged.connect(&callback2);

    //calls callback1 & 2.
    n.ChangeNodules();
}

メンバー関数をスロットとして引数に接続したい場合は、次のようにすることができます。

class Notifier
{
public:
    boost::signal< void ( double ) > ProgressSignal;
};

class OtherClass
{
public:
    void UpdateProgress(double pct);
};

int main()
{
    Notifier n;
    OtherClass oc;

    n.ProgressSignal.connect(boost::bind(&OtherClass::UpdateProgress, &oc, _1));

    //Calls oc.UpdateProgress(0);
    n.ProgressSignal(0);
}

警告: これはコンパイルもテストもされていません。

于 2010-06-08T14:14:16.783 に答える
2

私の他の回答と比較して、このソリューションはより一般的であり、定型コードを排除します:

#include <iostream>
#include <boost/bind.hpp>
#include <boost/signal.hpp>

///////////////////////////////////////////////////////////////////////////////
// GENERIC REUSABLE PART FOR ALL SUBJECTS
///////////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------------
template <class CallbackType>
class CallbackInvoker
{
public:
    virtual ~CallbackInvoker() {}
    virtual void operator()(CallbackType* callback) const {};
};

//-----------------------------------------------------------------------------
template <class CallbackType, class Binding>
class BoundInvoker : public CallbackInvoker<CallbackType>
{
public:
    BoundInvoker(const Binding& binding) : binding_(binding) {}
    void operator()(CallbackType* callback) const {binding_(callback);}

private:
    Binding binding_;
};

//-----------------------------------------------------------------------------
template <class CallbackType>
class CallbackSlot
{
public:
    CallbackSlot(CallbackType* callback) : callback_(callback) {}
    void operator()(const CallbackInvoker<CallbackType>& invoker)
        {invoker(callback_);}

private:
    CallbackType* callback_;
};

//-----------------------------------------------------------------------------
template <class CallbackType>
class Subject
{
public:
    virtual ~Subject() {}
    boost::signals::connection Connect(CallbackType* callback)
        {return signal_.connect(CallbackSlot<CallbackType>(callback));}

protected:
    template <class Binding> void Signal(const Binding& binding)
    {
        signal_(BoundInvoker<CallbackType,Binding>(binding));
    }

private:
    boost::signal<void (const CallbackInvoker<CallbackType>&)> signal_;
};


///////////////////////////////////////////////////////////////////////////////
// THIS PART SPECIFIC TO ONE SUBJECT
///////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
class MyCallback
{
public:
    virtual ~MyCallback() {}
    virtual void NodulesChanged() =0;
    virtual void TurkiesTwisted(int arg) =0;
};

//-----------------------------------------------------------------------------
class FooCallback : public MyCallback
{
public:
    virtual ~FooCallback() {}
    void NodulesChanged() {std::cout << "Foo nodules changed\n";}
    void TurkiesTwisted(int arg)
        {std::cout << "Foo " << arg << " turkies twisted\n";}
};

//-----------------------------------------------------------------------------
class BarCallback : public MyCallback
{
public:
    virtual ~BarCallback() {}
    void NodulesChanged() {std::cout << "Bar nodules changed\n";}
    void TurkiesTwisted(int arg)
        {std::cout << "Bar " << arg << " turkies twisted\n";}
};

//-----------------------------------------------------------------------------
class MySubject : public Subject<MyCallback>
{
public:
    void OnNoduleChanged()
        {this->Signal(boost::bind(&MyCallback::NodulesChanged, _1));}
    void OnTurkiedTwisted(int arg)
        {this->Signal(boost::bind(&MyCallback::TurkiesTwisted, _1, arg));}
};

///////////////////////////////////////////////////////////////////////////////
// CLIENT CODE
///////////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------------
int main()
{
    MySubject subject;
    FooCallback fooCb;
    BarCallback barCb;

    subject.Connect(&fooCb);
    subject.Connect(&barCb);

    subject.OnNoduleChanged();
    subject.OnTurkiedTwisted(42);
}

万歳boost::bind!:-)

于 2010-06-08T16:41:22.227 に答える
2

このソリューションにより、のメソッドのシグネチャが異なるsignal場合でも、同じオブジェクトを使用できます。Callback

#include <iostream>
#include <boost/signal.hpp>

//------------------------------------------------------------------------------
class Callback
{
public:
   virtual void NodulesChanged() =0;
   virtual void TurkiesTwisted(int arg) =0;
};

//------------------------------------------------------------------------------
class FooCallback : public Callback
{
public:
   void NodulesChanged() {std::cout << "Foo nodules changed\n";}
   void TurkiesTwisted(int arg) {std::cout << "Foo " << arg << " turkies twisted\n";}
};

//------------------------------------------------------------------------------
class BarCallback : public Callback
{
public:
   void NodulesChanged() {std::cout << "Bar nodules changed\n";}
   void TurkiesTwisted(int arg) {std::cout << "Bar " << arg << " turkies twisted\n";}
};

//------------------------------------------------------------------------------
class CallbackInvoker
{
public:
    virtual void operator()(Callback* callback) const {};
};

//------------------------------------------------------------------------------
class NoduleChangedInvoker : public CallbackInvoker
{
public:
    void operator()(Callback* callback) const {callback->NodulesChanged();}
};

//------------------------------------------------------------------------------
class TurkiesTwistedInvoker : public CallbackInvoker
{
public:
    TurkiesTwistedInvoker(int arg) : arg_(arg) {}
    void operator()(Callback* callback) const {callback->TurkiesTwisted(arg_);}

private:
    int arg_;
};

//------------------------------------------------------------------------------
class CallbackSlot
{
public:
    CallbackSlot(Callback* callback) : callback_(callback) {}

    void operator()(const CallbackInvoker& invoker) {invoker(callback_);}

private:
    Callback* callback_;
};

//------------------------------------------------------------------------------
class Subject
{
public:
    typedef boost::signal<void (const CallbackInvoker&)> SignalType;

    boost::signals::connection Connect(Callback* callback)
            {return signal_.connect(CallbackSlot(callback));}
    void OnNoduleChanged() {signal_(NoduleChangedInvoker());}
    void OnTurkiedTwisted(int arg) {signal_(TurkiesTwistedInvoker(arg));}

private:
    SignalType signal_;
};

//------------------------------------------------------------------------------
int main()
{
    Subject subject;
    FooCallback fooCb;
    BarCallback barCb;

    subject.Connect(&fooCb);
    subject.Connect(&barCb);

    subject.OnNoduleChanged();
    subject.OnTurkiedTwisted(42);
}

これは以下を出力します:

Foo nodules changed
Bar nodules changed
Foo 42 turkies twisted
Bar 42 turkies twisted

CallbackSlotに格納されている関数オブジェクトでboost::signal、具象Callbackオブジェクトへのポインタが含まれています。を呼び出すときは、コールバック引数をバンドルし、適切なメソッドを呼び出す方法を知っている具体的なオブジェクトboost::signalを渡す必要があります。CallbackInvokerCallback

Boost.Lamda を使用してボイラープレート コードを回避する方法があるかもしれませんが、CallbackInvoker私はその Boost ライブラリにあまり詳しくありません。

おそらく、メモリ リークやダングリング ポインターを避けるためboost::shared_ptr<Callback>に代わりに使用することをお勧めします。Callback*

于 2010-06-08T15:10:56.883 に答える