借助可变模板实现观察者模式的一种替代方式

陈硕在观察者模式之缪中提到,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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值