1

私は、依存値が変更されたときに値を変更する機能的リアクティブプログラミングを利用する通常のデータ値を表すオブジェクトに取り組んでいます。つまり、var3 = var1 + var2; があるとしましょう。var1 の値を変更すると、var3 の値が自動的に更新されます。C++ ではこれは大変ですが、更新関数が別のワーカー スレッドのどこかで呼び出されているため、機能的に反応しているように見せることができます。

そこで、私の方法を以下に示しました。任意の型を持つことができる Reactive と呼ばれるテンプレート オブジェクトを作成し、演算子をオーバーロードして、これらのリアクティブのうちの 2 つを加算すると、結果の値がそれらの合計に等しいだけでなく、ラムダがこれは操作を std::function に格納します。これは、更新関数を呼び出したときにいつでも結果の値を更新するために後で再度呼び出すことができます。

いくつかの問題が発生します。依存する値の 1 つが破棄された場合はどうなりますか。結果として得られる Reactive には、有効なラムダであると見なされるものがまだありますが、ラムダが使用した引数は存在しなくなります。これを説明するために、boost::signals2 を使用してシグナルとスロットのシステムをセットアップし、依存関係のいずれかが破壊されたことを結果に通知します。結果がシグナルを受信すると、そのアフェクター関数は無効になり、更新時に呼び出されません。

リアクティブがその + 操作を実行できるようにするには、一時的なリアクティブを作成する必要があります。これには独自のシグナルがあり、次に = 演算子をオーバーロードして、一時的なデータを結果のリアクティブに移動する必要があります。ただし、シグナルはコピーできません。私は std::unique_ptr に destruct シグナルを含めることでこれを回避し、オペレーター = Reactive && を受け取ったときに std::move を使用しました。危機は回避されました。

ここで私は立ち往生しています。後で気がついたのですが、move の構築は問題なく実行できましたが、Reactive の 1 つをコピーして構築する方法がまだないことに気付きました。そして、次のようにします: var4 = var3; 次に、どうにかして var1 と var2 の破壊シグナルは、それらが破壊されたことを var4 に通知する方法を必要とします。私が最終的に思いついたのは、boost::signals2::signal を含むファンクタである Proxy というサブオブジェクトを設定し、各オブジェクトが std::shared_ptr に含まれるようにすることでした。 . Reactive がそのプロキシへの参照を持っている場合、inform destruct メソッドをそのプロキシに接続します。次に、依存関係がそのプロキシに信号を添付します。プロキシが呼び出されると、すべての接続も呼び出されます。

問題は、プロキシを依存信号に接続するには、プロキシにコピーコンストラクターが必要であるか、少なくとも msvc が私に与えているエラーです。どうやら boost::signals2::signal::connect はそのコピー コンストラクターを使用しますが、プロキシ自体にシグナルが含まれているため使用できません。これが最善の解決策かどうかまだわからないので、このすべての情報を提供します。私はシグナルとスロットに最も精通しているため、シグナルとスロットを選択しましたが、より良い解決策があれば指摘してください。それ以外の場合は、このエラーを回避してください。

ところで、Slot は、Unreact() 関数を各 Reactive に固有のファンクターにする方法にすぎません。

オブジェクトは次のとおりです。

template<class T>
class Reactive
{ 
    template<class H>
    friend class Reactive;

    class Slot : public boost::signals2::trackable
    {
    public:
        Slot(std::function<void()> & func) :
            m_Func(func)
            {}

        void operator()()
            {m_Func();}

    private:
        std::function<void()> m_Func;
    };

    class Proxy : public boost::signals2::trackable
    {
        Proxy(const Proxy & s);
        Proxy & operator=(const Proxy & s);
    public:
        Proxy(){}

        void operator()() 
            {m_Informer();}

        void attach(Slot & m_Unreacter) 
            {m_Informer.connect(m_Unreacter);}

    private:
        boost::signals2::signal<void()> m_Informer;
    };

public:
    ~Reactive()
    {
        (*m_SendDestruct)();
    }

    Reactive() :
        m_SendDestruct(new boost::signals2::signal<void()>),
        m_Proxy(new Proxy),
        m_ReceiveDestruct(std::function<void()>(std::bind(&Reactive::Unreact, this))),
        m_Affecter(nullptr)
    {
        m_Proxy->attach(m_ReceiveDestruct);
    }

    template<class H>
    Reactive(const H & data) :
        m_SendDestruct(new boost::signals2::signal<void()>),
        m_Proxy(new Proxy),
        m_ReceiveDestruct(std::function<void()>(std::bind(&Reactive::Unreact, this))),
        m_Affecter(nullptr),
        m_Data(data)    
    {
        m_Proxy->attach(m_ReceiveDestruct);
    }

    Reactive(const Reactive & reac) :
        m_SendDestruct(new boost::signals2::signal<void()>),
        m_Proxy(reac.m_Proxy),
        m_ReceiveDestruct(std::function<void()>(std::bind(&Reactive::Unreact, this))),
        m_Affecter(reac.m_Affecter),
        m_Data(reac.m_Data)
    {
        m_Proxy->attach(m_ReceiveDestruct);
    }

    Reactive(Reactive && reac) :
        m_SendDestruct(std::move(reac.m_SendDestruct)),
        m_Proxy(reac.m_Proxy),
        m_ReceiveDestruct(std::function<void()>(std::bind(&Reactive::Unreact, this))),
        m_Affecter(reac.m_Affecter),
        m_Data(reac.m_Data)
    {
        m_Proxy->attach(m_ReceiveDestruct);
    }

    Reactive & operator=(const T & data)
    {
        m_Data = data;

        return *this;
    }

    Reactive & operator=(const Reactive & reac)
    {
        m_Proxy = reac.m_Proxy;
        m_Proxy.attach(m_ReceiveDestruct);
        m_Affecter = reac.m_Affecter;
        m_Data = reac.m_Data;
    }

    Reactive & operator=(Reactive && reac)
    {
        m_SendDestruct = std::move(reac.m_SendDestruct);
        m_Proxy = reac.m_Proxy;
        m_Affecter(reac.m_Affecter);
        m_Data(reac.m_Data);
    }

    template<class H>
    Reactive & operator+(const H & rhs)
    {
        m_Data += rhs;

        return *this;
    }

    template<class H>
    auto operator+(Reactive<H> & rhs) -> Reactive<decltype(m_Data + rhs.m_Data)> &&
    {
        Reactive<decltype(m_Data + rhs.m_Data)> m_temp;
        std::function<decltype(m_Data + rhs.m_Data)()> func;

        if (!rhs.m_Affecter)
            func = [&](){ return m_Data + rhs.m_Data;};
        else
            func = [&](){return m_Data + rhs.m_Affecter();};

        m_SendDestruct->connect((*m_temp.m_Proxy));
        rhs.m_SendDestruct->connect((*m_temp.m_Proxy));

        return std::forward<Reactive<decltype(m_Data+rhs.m_Data)> &&>(m_temp);
    }

    template<class H>
    Reactive && operator+(Reactive<H> && rhs)
    {
        Reactive && m_Temp
    }

    T & Get()
    {
        return m_Data;
    }

    void Update()
    {
        if(m_Affecter)
            m_Data = m_Affecter();
    }

    void Unreact()
    {
        m_Affecter = nullptr;
        (*m_SendDestruct)();
    }

private:
    std::unique_ptr<boost::signals2::signal<void()> > m_SendDestruct;
    std::shared_ptr<Proxy> m_Proxy;
    Slot m_ReceiveDestruct;
    std::function<T()> m_Affecter;
    T m_Data;
};

そして簡単なテスト

int main()
{
    Reactive<int> vel(10);
    Reactive<int> acc(5);
    Reactive<int> time(5);

    Reactive<int> result = vel + acc + time;

    system("PAUSE");
    return 0;
}

ここに警告/エラーがあります:

1>main.cpp(86): warning C4355: 'this' : used in base member initializer list
1>          main.cpp(83) : while compiling class template member function 'Reactive<T>::Reactive(Reactive<T> &&)'
1>          with
1>          [
1>              T=int
1>          ]
1>          main.cpp(174) : see reference to class template instantiation 'Reactive<T>' being compiled
1>          with
1>          [
1>              T=int
1>          ]
1>main.cpp(66): warning C4355: 'this' : used in base member initializer list
1>          main.cpp(174) : see reference to function template instantiation 'Reactive<T>::Reactive<int>(const H &)' being compiled
1>          with
1>          [
1>              T=int,
1>              H=int
1>          ]
1>main.cpp(56): warning C4355: 'this' : used in base member initializer list
1>          main.cpp(53) : while compiling class template member function 'Reactive<T>::Reactive(void)'
1>          with
1>          [
1>              T=int
1>          ]
1>c:\program files (x86)\boost\boost_1_53_0\boost\signals2\detail\slot_template.hpp(156): error C2248: 'Reactive<T>::Proxy::Proxy' : cannot access private member declared in class 'Reactive<T>::Proxy'
1>          with
1>          [
1>              T=int
1>          ]
1>          main.cpp(32) : see declaration of 'Reactive<T>::Proxy::Proxy'
1>          with
1>          [
1>              T=int
1>          ]
1>          main.cpp(30) : see declaration of 'Reactive<T>::Proxy'
1>          with
1>          [
1>              T=int
1>          ]
1>          c:\program files (x86)\boost\boost_1_53_0\boost\signals2\detail\slot_template.hpp(81) : see reference to function template instantiation 'void boost::signals2::slot0<R,SlotFunction>::init_slot_function<F>(const F &)' being compiled
1>          with
1>          [
1>              R=void,
1>              SlotFunction=boost::function<void (void)>,
1>              F=Reactive<int>::Proxy
1>          ]
1>          main.cpp(135) : see reference to function template instantiation 'boost::signals2::slot0<R,SlotFunction>::slot0<Reactive<T>::Proxy>(const F &)' being compiled
1>          with
1>          [
1>              R=void,
1>              SlotFunction=boost::function<void (void)>,
1>              T=int,
1>              F=Reactive<int>::Proxy
1>          ]
1>          main.cpp(178) : see reference to function template instantiation 'Reactive<T> &&Reactive<T>::operator +<int>(Reactive<T> &)' being compiled
1>          with
1>          [
1>              T=int
1>          ]
1>
1>Build FAILED.
1>
1>Time Elapsed 00:00:04.20
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
4

1 に答える 1

2

設計に問題があると思います。

リアクティブ変数の論理的な有効期間は、それを指定する C++ 変数の有効期間に関連付けるべきではありません。

このパターンを使用pImplして、各リアクティブ変数の有効期間を、それを指定する C++ 変数がある限り、またはそれを参照する別のリアクティブ変数が有効である限り維持します。( std::shared_ptr、とにかくリアクティブグラフでは悪いモジョである循環参照を検出するための何かがあるかもしれません)。

移動するときは、それ自体pImplではなく内の状態を移動します。 には、リアクティブ状態への独自のポインター (a ) があり、一時的なリアクティブ ( など) に非常に役立ちます。pImplpImplppImplA+B

に依存するとA、実際には に依存しA->pImpl、その参照カウントが増加します。

を設定した場合、それは に依存A = Bすることを意味し、の反応状態は の反応状態のコピーではありません。これは微妙な違いです。A->pImplB->pImplA->pImplB->pImpl

これには、変更を反映させるための努力が必要です。weak_ptrあなたに依存している人々への逆参照とSetIsDirty、ツリーを介して逆方向に伝播する. 汚れているものはすべて、読み取り時に再計算されます。それ以外の場合は、キャッシュされた値が使用されます。

の効果をのコピーにしたい場合は、別の構文を使用します (C++ism を盗むために のようA = Bに)。これは、状態をコピーする必要がある唯一の状況です!A->pImplB->pImplA = *BpImpl

A = std::move(B)状態を移動する必要があるpImplため、 を使用ppImplして最適化のために状態を保存することに注意してください。

余分なスレッドやスロット/ソケットが本当に必要ないことがわかるはずです。また、 unary を実装しない限りoperator*、特定の状態をコピーする必要はありませんpImpl

循環依存関係に関する私の率直なコメントが重要であることに注意してください。循環依存関係を持つリアクティブ グラフは、上記の設計ではリークし、さらに重要なことに、値を計算することが論理的に不可能になる可能性があります。

于 2013-04-11T19:48:04.267 に答える