藉助可變模板實現觀察者模式的一種替代方式

陳碩在觀察者模式之繆中提到,Observe模式有本質的問題,問題的根本原因在於面向對象的設計.其中的一個問題是,observer是基類,帶來了非常強的耦合,強度僅次於友元類型,這種耦合限制了成員函數的名字,參數,返回值,還限制了成員函數所屬的類型(必須是observer的派生類).還有一些其他的問題,這裏不細述了,C++語言可以通過function/bind繞開Observer模式的限制.書中的代碼實現了一個信號曹機制來進行一對多回調(觀察者本質上就爲了方便一對多的處理),說實話代碼剛剛能看懂,理解還不夠深入,這裏記錄下,後面再逐步深入.
首先是一個簡單的可變模板實現(非線程安全),實現了一對多回調.

//SignalSlotTrivial.h
// Created by root on 1/1/20.
//

#ifndef MUDUO_SIGNALSLOTTRIVIAL_H
#define MUDUO_SIGNALSLOTTRIVIAL_H

#include <memory>
#include <vector>
#include <iostream>
#include <functional>
template<typename Signature>
class SignalTrivial;

template <typename RET, typename... ARGS>
class SignalTrivial<RET(ARGS...)>
{
public:
    typedef std::function<void (ARGS...)> Functor;

    void connect(Functor&& func)
    {
        functors_.push_back(std::forward<Functor>(func));
    }

    void call(ARGS &&... args)
    {
        // gcc 4.6 supports
        //for (const Functor& f: functors_)
        typename std::vector<Functor>::iterator it = functors_.begin();
        for (; it != functors_.end(); ++it)
        {
            (*it)(args...);
        }
    }
private:
    std::vector<Functor> functors_;
};


#endif //MUDUO_SIGNALSLOTTRIVIAL_H

線程安全的版本:

//SignalSlot.h
// Created by root on 1/1/20.
//

#ifndef MUDUO_SIGNALSLOT_H
#define MUDUO_SIGNALSLOT_H

#include <mutex>
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>

#include <vector>

namespace muduo
{

    namespace detail
    {

        template<typename Callback>
        struct SlotImpl;

        template<typename Callback>
        struct SignalImpl : boost::noncopyable
        {
            typedef std::vector<boost::weak_ptr<SlotImpl<Callback> > > SlotList;

            SignalImpl()
                    : slots_(new SlotList)
            {
            }

            void copyOnWrite()
            {
//                mutex_.assertLocked();
//                mutex_.try_lock();
                if (!slots_.unique())
                {
                    slots_.reset(new SlotList(*slots_));
                }
                assert(slots_.unique());
            }

            void clean()
            {
                std::lock_guard<std::mutex> lock(mutex_);
                copyOnWrite();
                SlotList& list(*slots_);
                typename SlotList::iterator it(list.begin());
                while (it != list.end())
                {
                    if (it->expired())
                    {
                        it = list.erase(it);
                    }
                    else
                    {
                        ++it;
                    }
                }
            }

            std::mutex mutex_;
            boost::shared_ptr<SlotList> slots_;
        };

        template<typename Callback>
        struct SlotImpl : boost::noncopyable
        {
            typedef SignalImpl<Callback> Data;
            SlotImpl(const boost::shared_ptr<Data>& data, Callback&& cb)
                    : data_(data), cb_(cb), tie_(), tied_(false)
            {
            }

            SlotImpl(const boost::shared_ptr<Data>& data, Callback&& cb,
                     const boost::shared_ptr<void>& tie)
                    : data_(data), cb_(cb), tie_(tie), tied_(true)
            {
            }

            ~SlotImpl()
            {
                boost::shared_ptr<Data> data(data_.lock());
                if (data)
                {
                    data->clean();
                }
            }

            boost::weak_ptr<Data> data_;
            Callback cb_;
            boost::weak_ptr<void> tie_;
            bool tied_;
        };

    }

/// This is the handle for a slot
///
/// The slot will remain connected to the signal fot the life time of the
/// returned Slot object (and its copies).
    typedef boost::shared_ptr<void> Slot;

    template<typename Signature>
    class Signal;

    template <typename RET, typename... ARGS>
    class Signal<RET(ARGS...)> : boost::noncopyable
    {
    public:
        typedef std::function<void (ARGS...)> Callback;
        typedef detail::SignalImpl<Callback> SignalImpl;
        typedef detail::SlotImpl<Callback> SlotImpl;

        Signal()
                : impl_(new SignalImpl)
        {
        }

        ~Signal()
        {
        }

        Slot connect(Callback&& func)
        {
            boost::shared_ptr<SlotImpl> slotImpl(
                    new SlotImpl(impl_, std::forward<Callback>(func)));
            add(slotImpl);
            return slotImpl;
        }

        Slot connect(Callback&& func, const boost::shared_ptr<void>& tie)
        {
            boost::shared_ptr<SlotImpl> slotImpl(new SlotImpl(impl_, func, tie));
            add(slotImpl);
            return slotImpl;
        }

        void call(ARGS&&... args)
        {
            SignalImpl& impl(*impl_);
            boost::shared_ptr<typename SignalImpl::SlotList> slots;
            {
                std::lock_guard<std::mutex> lock(impl.mutex_);
                slots = impl.slots_;
            }
            typename SignalImpl::SlotList& s(*slots);
            for (typename SignalImpl::SlotList::const_iterator it = s.begin(); it != s.end(); ++it)
            {
                boost::shared_ptr<SlotImpl> slotImpl = it->lock();
                if (slotImpl)
                {
                    boost::shared_ptr<void> guard;
                    if (slotImpl->tied_)
                    {
                        guard = slotImpl->tie_.lock();
                        if (guard)
                        {
                            slotImpl->cb_(args...);
                        }
                    }
                    else
                    {
                        slotImpl->cb_(args...);
                    }
                }
            }
        }

    private:
        void add(const boost::shared_ptr<SlotImpl>& slot)
        {
            SignalImpl& impl(*impl_);
            {
                std::lock_guard<std::mutex> lock(impl.mutex_);
                impl.copyOnWrite();
                impl.slots_->push_back(slot);
            }
        }
        const boost::shared_ptr<SignalImpl> impl_;
    };
}
#endif //MUDUO_SIGNALSLOT_H

//
// Created by root on 1/1/20.
//

#include "SignalSlotTrivial.h"
#include "SignalSlot.h"
#include <iostream>

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



#if 0
class String
{
public:
    String(const char* str)
    {
        printf("String ctor this %p\n", this);
    }

    String(const String& rhs)
    {
        printf("String copy ctor this %p, rhs %p\n", this, &rhs);
    }

    String(String&& rhs)
    {
        printf("String move ctor this %p, rhs %p\n", this, &rhs);
    }
};

class Foo : boost::noncopyable
{
public:
    void zero();
    void zeroc() const;
    void one(int);
    void oner(int&);
    void onec(int) const;
    void oneString(const String& str);
    // void oneStringRR(String&& str);
    static void szero();
    static void sone(int);
    static void soneString(const String& str);
};

void Foo::zero()
{
    printf("Foo::zero()\n");
}

void Foo::zeroc() const
{
    printf("Foo::zeroc()\n");
}

void Foo::szero()
{
    printf("Foo::szero()\n");
}

void Foo::one(int x)
{
    printf("Foo::one() x=%d\n", x);
}

void Foo::onec(int x) const
{
    printf("Foo::onec() x=%d\n", x);
}

void Foo::sone(int x)
{
    printf("Foo::sone() x=%d\n", x);
}

void Foo::oneString(const String& str)
{
    printf("Foo::oneString\n");
}

void Foo::soneString(const String& str)
{
    printf("Foo::soneString\n");
}

int main()
{
    SignalTrivial<void()> signal;

//    signal.connect(&Foo::szero);

    Foo f;
    signal.connect(std::bind(&Foo::one, &f, 42));

    const Foo cf;
    signal.connect(boost::bind(&Foo::zeroc, &cf));
    signal.connect(boost::bind(&Foo::onec, &cf, 128));
    printf("========\n");
    signal.call();

    return 0;
}

#endif


#if 1

#define BOOST_TEST_MAIN
#ifdef BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#else
#include <boost/test/included/unit_test.hpp>
#endif
class String
{
public:
    String(const char* str)
    {
        printf("String ctor this %p\n", this);
    }

    String(const String& rhs)
    {
        printf("String copy ctor this %p, rhs %p\n", this, &rhs);
    }

    String(String&& rhs)
    {
        printf("String move ctor this %p, rhs %p\n", this, &rhs);
    }
};

class Foo : boost::noncopyable
{
public:
    void zero();
    void zeroc() const;
    void one(int);
    void oner(int&);
    void onec(int) const;
    void oneString(const String& str);
    // void oneStringRR(String&& str);
    static void szero();
    static void sone(int);
    static void soneString(const String& str);
};

void Foo::zero()
{
    printf("Foo::zero()\n");
}

void Foo::zeroc() const
{
    printf("Foo::zeroc()\n");
}

void Foo::szero()
{
    printf("Foo::szero()\n");
}

void Foo::one(int x)
{
    printf("Foo::one() x=%d\n", x);
}

void Foo::onec(int x) const
{
    printf("Foo::onec() x=%d\n", x);
}

void Foo::sone(int x)
{
    printf("Foo::sone() x=%d\n", x);
}

void Foo::oneString(const String& str)
{
    printf("Foo::oneString\n");
}

void Foo::soneString(const String& str)
{
    printf("Foo::soneString\n");
}
#if 0
BOOST_AUTO_TEST_CASE(testSignalSlotTrivialZero)
{
    SignalTrivial<void()> signal;

    printf("========\n");
    signal.call();

    signal.connect(&Foo::szero);

    printf("========\n");
    signal.call();

    Foo f;
    signal.connect(boost::bind(&Foo::zero, &f));

    printf("========\n");
    signal.call();

    signal.connect(boost::bind(&Foo::one, &f, 42));

    printf("========\n");
    signal.call();

    const Foo cf;
    signal.connect(boost::bind(&Foo::zeroc, &cf));

    printf("========\n");
    signal.call();

    signal.connect(boost::bind(&Foo::onec, &cf, 128));

    printf("========\n");
    signal.call();

    printf("========\n");
    signal.call();
}
#endif

#if 0
BOOST_AUTO_TEST_CASE(testSignalSlotTrivialOne)
{
    SignalTrivial<void(int)> signal;

    printf("========\n");
    signal.call(500);

    signal.connect(&Foo::sone);

    printf("========\n");
    signal.call(510);

    Foo f;
    signal.connect(boost::bind(&Foo::one, &f, _1));

    printf("========\n");
    signal.call(520);

    const Foo cf;
    signal.connect(boost::bind(&Foo::onec, &cf, _1));

    printf("========\n");
    signal.call(530);
}
#endif

#if 0
BOOST_AUTO_TEST_CASE(testSignalSlotTrivialString)
{
    SignalTrivial<void(const String&)> signal;
    signal.call("hello");

    signal.connect(&Foo::soneString);

    printf("========\n");
    signal.call("hello");

    Foo f;
    signal.connect(boost::bind(&Foo::oneString, &f, _1));

    printf("========\n");
    signal.call("hello");
}
#endif

#if 0
BOOST_AUTO_TEST_CASE(testSignalSlotZero)
{
    muduo::Signal<void()> signal;

    printf("==== testSignalSlotZero ====\n");
    signal.call();

    muduo::Slot s1 = signal.connect(&Foo::szero);

    printf("========\n");
    signal.call();

    Foo f;
    muduo::Slot s2 = signal.connect(boost::bind(&Foo::zero, &f));

    printf("========\n");
    signal.call();

    muduo::Slot s3 = signal.connect(boost::bind(&Foo::one, &f, 42));

    printf("========\n");
    signal.call();

    const Foo cf;
    muduo::Slot s4 = signal.connect(boost::bind(&Foo::zeroc, &cf));

    printf("========\n");
    signal.call();

    muduo::Slot s5 = signal.connect(boost::bind(&Foo::onec, &cf, 128));

    printf("========\n");
    signal.call();

    s1 = muduo::Slot();
    printf("========\n");
    signal.call();


    s4 = s3 = s2 = muduo::Slot();

    printf("========\n");
    signal.call();

}
#endif

#if 1
BOOST_AUTO_TEST_CASE(testSignalSlotOne)
{
    muduo::Signal<void(int)> signal;

    printf("========\n");
    signal.call(50);


    muduo::Slot s4;
    {
        muduo::Slot s1 = signal.connect(&Foo::sone);

        printf("========\n");
        signal.call(51);

        Foo f;
        muduo::Slot s2 = signal.connect(boost::bind(&Foo::one, &f, _1));

        printf("========\n");
        signal.call(52);

        const Foo cf;
        muduo::Slot s3 = signal.connect(boost::bind(&Foo::onec, &cf, _1));

        printf("========\n");
        signal.call(53);

        s4 = s3;
    }

    printf("========\n");
    signal.call(54);
}
#endif

#if 1
BOOST_AUTO_TEST_CASE(testSignalSlotLife)
{
    muduo::Slot s1;

    {
        muduo::Signal<void()> signal;
        s1 = signal.connect(&Foo::szero);

        printf("========\n");
        signal.call();

        Foo f;
        boost::function<void()> func = boost::bind(&Foo::zero, &f);

        s1 = signal.connect(func);

        printf("========\n");
        signal.call();
    }
}
#endif

#endif
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章