手把手教你架构3D引擎高级篇系列七

本篇博客主要是介绍关于委托的封装,本篇博客需要读者掌握如下几个技术点:
在这里插入图片描述学习技术文章方法很重要,任何人都有自己不懂的地方,我们在学习时,遇到不懂的,可以查阅网络资源或者书籍,这样通过不停的迭代学习,你才能不断的进步,最后成为技术达人,引擎模块的封装,也不是一朝一夕就能搞定的事情,量的积累才能达到质的提升。

前面封装了数据结构中常用算法,接下来给读者封装一个好用的东西——委托。使用过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,智能指针等等。

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页