版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jxw167/article/details/82660009
本篇博客主要是介绍关于委托的封装,本篇博客需要读者掌握如下几个技术点:
学习技术文章方法很重要,任何人都有自己不懂的地方,我们在学习时,遇到不懂的,可以查阅网络资源或者书籍,这样通过不停的迭代学习,你才能不断的进步,最后成为技术达人,引擎模块的封装,也不是一朝一夕就能搞定的事情,量的积累才能达到质的提升。
前面封装了数据结构中常用算法,接下来给读者封装一个好用的东西——委托。使用过Unity开发的人知道,我们经常在项目中使用委托,类似我们说的回调。使用C#编写委托,其实质是什么呢?其实就是把委托的函数加到一个列表中,这样方便管理适合封装。我们在实现引擎的委托时,首先要清楚我们的委托面对的是所有类型的,因此很容想到使用模板解决问题。在实现委托时,我们在这里将它们分两个模块,一个是关于委托的,一个是委托列表。在这里我们用了模板指针,C++模板这块技术,读者一定要掌握,不论是客户端引擎还是服务器引擎都会大量使用模板的封装。委托的功能包括:绑定bind,另一个是解除绑定unbind。下面我们开始编写引擎委托和委托列表。
template <typename T> class Delegate;
template <typename R> class Delegate
{
private:
typedef void* InstancePtr;
typedef R (*InternalFunction)(InstancePtr);
struct Stub
{
InstancePtr first;
InternalFunction second;
};
template <R (*Function)()> static FORCE_INLINE R FunctionStub(InstancePtr) { return (Function)(); }
template <class C, R (C::*Function)()> static FORCE_INLINE R ClassMethodStub(InstancePtr instance)
{
return (static_cast<C*>(instance)->*Function)();
}
template <class C, R (C::*Function)() const> static FORCE_INLINE R ClassMethodStub(InstancePtr instance)
{
return (static_cast<C*>(instance)->*Function)();
}
public:
Delegate()
{
m_stub.first = nullptr;
m_stub.second = nullptr;
}
template <R (*Function)()> void bind()
{
m_stub.first = nullptr;
m_stub.second = &FunctionStub<Function>;
}
template <class C, R (C::*Function)()> void bind(C* instance)
{
m_stub.first = instance;
m_stub.second = &ClassMethodStub<C, Function>;
}
template <class C, R(C::*Function)() const> void bind(C* instance)
{
m_stub.first = instance;
m_stub.second = &ClassMethodStub<C, Function>;
}
R invoke() const
{
ASSERT(m_stub.second != nullptr);
return m_stub.second(m_stub.first);
}
bool operator==(const Delegate<R>& rhs)
{
return m_stub.first == rhs.m_stub.first && m_stub.second == rhs.m_stub.second;
}
private:
Stub m_stub;
};
template <typename R, typename... Args> class Delegate<R(Args...)>
{
private:
typedef void* InstancePtr;
typedef R (*InternalFunction)(InstancePtr, Args...);
struct Stub
{
InstancePtr first;
InternalFunction second;
};
template <R (*Function)(Args...)> static FORCE_INLINE R FunctionStub(InstancePtr, Args... args)
{
return (Function)(args...);
}
template <class C, R(C::*Function)(Args...)>
static FORCE_INLINE R ClassMethodStub(InstancePtr instance, Args... args)
{
return (static_cast<C*>(instance)->*Function)(args...);
}
template <class C, R(C::*Function)(Args...) const>
static FORCE_INLINE R ClassMethodStub(InstancePtr instance, Args... args)
{
return (static_cast<C*>(instance)->*Function)(args...);
}
public:
Delegate()
{
m_stub.first = nullptr;
m_stub.second = nullptr;
}
bool isValid() { return m_stub.second != nullptr; }
template <R (*Function)(Args...)> void bind()
{
m_stub.first = nullptr;
m_stub.second = &FunctionStub<Function>;
}
template <class C, R (C::*Function)(Args...)> void bind(C* instance)
{
m_stub.first = instance;
m_stub.second = &ClassMethodStub<C, Function>;
}
template <class C, R(C::*Function)(Args...) const> void bind(C* instance)
{
m_stub.first = instance;
m_stub.second = &ClassMethodStub<C, Function>;
}
R invoke(Args... args) const
{
ASSERT(m_stub.second != nullptr);
return m_stub.second(m_stub.first, args...);
}
bool operator==(const Delegate<R(Args...)>& rhs)
{
return m_stub.first == rhs.m_stub.first && m_stub.second == rhs.m_stub.second;
}
private:
Stub m_stub;
};
以上是我们引擎的委托类封装,下面我们实现委托列表封装,如下所示:
template <typename T> class DelegateList;
template <typename R, typename... Args> class DelegateList<R(Args...)>
{
public:
explicit DelegateList(IAllocator& allocator)
: m_delegates(allocator)
{
}
template <typename C, R (C::*Function)(Args...)> void bind(C* instance)
{
Delegate<R(Args...)> cb;
cb.template bind<C, Function>(instance);
m_delegates.push(cb);
}
template <R (*Function)(Args...)> void bind()
{
Delegate<R(Args...)> cb;
cb.template bind<Function>();
m_delegates.push(cb);
}
template <typename C, R (C::*Function)(Args...)> void unbind(C* instance)
{
Delegate<R(Args...)> cb;
cb.template bind<C, Function>(instance);
for (int i = 0; i < m_delegates.size(); ++i)
{
if (m_delegates[i] == cb)
{
m_delegates.eraseFast(i);
break;
}
}
}
void invoke(Args... args)
{
for (auto& i : m_delegates) i.invoke(args...);
}
private:
Array<Delegate<R(Args...)>> m_delegates;
};
我们实现这个委托的目的跟Unity的类似,比如我们的输入系统,UI都会使用我们引擎提供的委托函数。引擎代码编写都是比较枯燥的,其实要学好C++编程不是那么容易的事情,需要大量的练习,尤其是模板的使用。对于模板不熟悉的读者多学习一下,编写引擎使用的时C++11,关于C++11的特性读者要熟悉,比如auto,智能指针等等。