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

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

前面封装了数据结构中常用算法,接下来给读者封装一个好用的东西——委托。使用过Unity开发的人知道,我们经常在项目中使用委托,类似我们说的回调。使用C#编写委托,其实质是什么呢?其实就是把委托的函数加到一个列表中,这样方便管理适合封装。我们在实现引擎的委托时,首先要清楚我们的委托面对的是所有类型的,因此很容想到使用模板解决问题。在实现委托时,我们在这里将它们分两个模块,一个是关于委托的,一个是委托列表。在这里我们用了模板指针,C++模板这块技术,读者一定要掌握,不论是客户端引擎还是服务器引擎都会大量使用模板的封装。委托的功能包括:绑定bind,另一个是解除绑定unbind。下面我们开始编写引擎委托和委托列表。

template  class Delegate;

template  class Delegate
{
private:
	typedef void* InstancePtr;
	typedef R (*InternalFunction)(InstancePtr);
	struct Stub
	{
		InstancePtr first;
		InternalFunction second;
	};

	template  static FORCE_INLINE R FunctionStub(InstancePtr) { return (Function)(); }

	template  static FORCE_INLINE R ClassMethodStub(InstancePtr instance)
	{
		return (static_cast(instance)->*Function)();
	}

	template  static FORCE_INLINE R ClassMethodStub(InstancePtr instance)
	{
		return (static_cast(instance)->*Function)();
	}

public:
	Delegate()
	{
		m_stub.first = nullptr;
		m_stub.second = nullptr;
	}

	template  void bind()
	{
		m_stub.first = nullptr;
		m_stub.second = &FunctionStub;
	}

	template  void bind(C* instance)
	{
		m_stub.first = instance;
		m_stub.second = &ClassMethodStub;
	}

	template  void bind(C* instance)
	{
		m_stub.first = instance;
		m_stub.second = &ClassMethodStub;
	}

	R invoke() const
	{
		ASSERT(m_stub.second != nullptr);
		return m_stub.second(m_stub.first);
	}

	bool operator==(const Delegate& rhs)
	{
		return m_stub.first == rhs.m_stub.first && m_stub.second == rhs.m_stub.second;
	}

private:
	Stub m_stub;
};


template  class Delegate
{
private:
	typedef void* InstancePtr;
	typedef R (*InternalFunction)(InstancePtr, Args...);
	struct Stub
	{
		InstancePtr first;
		InternalFunction second;
	};

	template  static FORCE_INLINE R FunctionStub(InstancePtr, Args... args)
	{
		return (Function)(args...);
	}

	template 
	static FORCE_INLINE R ClassMethodStub(InstancePtr instance, Args... args)
	{
		return (static_cast(instance)->*Function)(args...);
	}

	template 
	static FORCE_INLINE R ClassMethodStub(InstancePtr instance, Args... args)
	{
		return (static_cast(instance)->*Function)(args...);
	}

public:
	Delegate()
	{
		m_stub.first = nullptr;
		m_stub.second = nullptr;
	}

	bool isValid() { return m_stub.second != nullptr; }

	template  void bind()
	{
		m_stub.first = nullptr;
		m_stub.second = &FunctionStub;
	}

	template  void bind(C* instance)
	{
		m_stub.first = instance;
		m_stub.second = &ClassMethodStub;
	}

	template  void bind(C* instance)
	{
		m_stub.first = instance;
		m_stub.second = &ClassMethodStub;
	}

	R invoke(Args... args) const
	{
		ASSERT(m_stub.second != nullptr);
		return m_stub.second(m_stub.first, args...);
	}

	bool operator==(const Delegate& rhs)
	{
		return m_stub.first == rhs.m_stub.first && m_stub.second == rhs.m_stub.second;
	}

private:
	Stub m_stub;
};

以上是我们引擎的委托类封装,下面我们实现委托列表封装,如下所示:

template  class DelegateList;

template  class DelegateList
{
public:
	explicit DelegateList(IAllocator& allocator)
		: m_delegates(allocator)
	{
	}

	template  void bind(C* instance)
	{
		Delegate cb;
		cb.template bind(instance);
		m_delegates.push(cb);
	}

	template  void bind()
	{
		Delegate cb;
		cb.template bind();
		m_delegates.push(cb);
	}

	template  void unbind(C* instance)
	{
		Delegate cb;
		cb.template bind(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> m_delegates;
};

我们实现这个委托的目的跟Unity的类似,比如我们的输入系统,UI都会使用我们引擎提供的委托函数。引擎代码编写都是比较枯燥的,其实要学好C++编程不是那么容易的事情,需要大量的练习,尤其是模板的使用。对于模板不熟悉的读者多学习一下,编写引擎使用的时C++11,关于C++11的特性读者要熟悉,比如auto,智能指针等等。

你可能感兴趣的:(3D引擎)