在C++11中,借助variadic template,实现最简单的(trivial)的一对多回调。
#include "../SignalSlot.h"
#include "../SignalSlotTrivial.h"
#include <boost/bind.hpp>
#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");
}
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();
}
BOOST_AUTO_TEST_CASE(testSignalSlotTrivialOne)
{
SignalTrivial<void(int)> signal;
printf("========\n");
signal.call(50);
signal.connect(&Foo::sone);
printf("========\n");
signal.call(51);
Foo f;
signal.connect(boost::bind(&Foo::one, &f, _1));
printf("========\n");
signal.call(52);
const Foo cf;
signal.connect(boost::bind(&Foo::onec, &cf, _1));
printf("========\n");
signal.call(53);
}
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");
}
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();
}
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);
}
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();
}
}
#ifndef MUDUO_BASE_SIGNALSLOT_H
#define MUDUO_BASE_SIGNALSLOT_H
#include "Mutex.h"
#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();
if (!slots_.unique())
{
slots_.reset(new SlotList(*slots_));
}
assert(slots_.unique());
}
void clean()
{
MutexLockGuard 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;
}
}
}
MutexLock 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;
{
MutexLockGuard 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_);
{
MutexLockGuard lock(impl.mutex_);
impl.copyOnWrite();
impl.slots_->push_back(slot);
}
}
const boost::shared_ptr<SignalImpl> impl_;
};
}
#endif // MUDUO_BASE_SIGNALSLOT_H
#ifndef MUDUO_BASE_SIGNALSLOTTRIVIAL_H
#define MUDUO_BASE_SIGNALSLOTTRIVIAL_H
#include <memory>
#include <vector>
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_BASE_SIGNALSLOTTRIVIAL_H
网友评论