当前位置:   article > 正文

UE4 代理(Delegate)源码浅析(1)_ud4 adds a generator delegate

ud4 adds a generator delegate

本文章只是我个人在学习虚幻引擎过程中的理解,不一定正确,若有说的不对的地方,欢迎指正。

以下引用的源码在Engine\Source\Runtime\Core\Public\Delegates\文件夹下的文件内均可找到

一.静态单播代理

静态单播的我们以分析DECLARE_DELEGATE_OneParam作为突破口。

1.大致框架:

先来看看他的基本定义:

#define DECLARE_DELEGATE_OneParam( DelegateName, Param1Type ) FUNC_DECLARE_DELEGATE( DelegateName, void, Param1Type )
  • 1

可以看出代理最终封装为宏,传入两个宏参数——代理名(DelegateName),参数类型(Param1Type )。再传给FUNC_DECLARE_DELEGATE宏,再来看看它的定义:

#define FUNC_DECLARE_DELEGATE( DelegateName, ... ) \
	typedef TBaseDelegate<__VA_ARGS__> DelegateName;
  • 1
  • 2

FUNC_DECLARE_DELEGATE接受代理名(DelegateName)和可变参数(那三个点就是可变参数)。它用类型重定义关键字(typedef),把传入可变参数(VA_ARGS)作为模板参数的模板类TBaseDelegate,类型重定义为DelegateName

可能有些拗口。用通俗的语言解释就是,给传入了模板参数的模板类重新起个名字,这个名字就是我们传进来的代理名(DelegateName),使用代理的第一个步骤声明代理中(《UE4 代理(Delegate)的使用》文章中说过三个步骤),生成代理对象其实就是声明一个TBaseDelegate类的实例。

现在让我们来看看TBaseDelegate模板类的大致定义:

template <typename WrappedRetValType, typename... ParamTypes>
class TBaseDelegate : public FDelegateBase
{
public:
    //……
    //一系列创建代理的函数(CreateStatic、CreateLambda、CreateWeakLambda、CreateRaw、CreateSP、CreateThreadSafeSp、CreateUFunction、CreateUObject) 
    //……

public:
    //……
    //一系列绑定函数(BindStatic、BindLambda、BindRaw、BindSP、BindThreadSafeSP、BindUFunction、BindUObject)
    //……

public:
    //执行函数
    RetValType Execute(ParamTypes... Params);
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

可以看到模板类TBaseDelegate继承自FDelegateBase(等下来说),模板类里定义了一系列的创建函数(Create**)和我们熟悉的绑定函数(**Bind),还实现了执行代理的方法——Execute

这些函数的细节部分在下一个小标题再说,现在先来看看FDelegateBase的实现:

class FDelegateBase
{
public:
    
    //……

    bool IsBound( ) const
    {
        //Do Something
    }

    //……

    void Unbind( )
    {
	//Do Something
    }

    //……
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

FDelegateBase实现了判断是否绑定的方法(IsBound)和解除绑定的方法(Unbind)。来看看类图:
在这里插入图片描述

2.实现细节:

还记得前文所说的TBaseDelegate模板类实现的那些绑定函数吗?我们在代理声明步骤生成了一个TBaseDelegate的类实例,在绑定函数步骤调用了它的绑定函数(Bind~)。绑定函数的实现大致相同,我们以BindUObject为例,先来看看引擎源码:

template <typename UserClass, typename... VarTypes>
inline void BindUObject(UserClass* InUserObject, typename TMemFunPtrType<true, UserClass, RetValType (ParamTypes..., VarTypes...)>::Type InFunc, VarTypes... Vars)
{
	*this = CreateUObject(InUserObject, InFunc, Vars...);
}
  • 1
  • 2
  • 3
  • 4
  • 5

可以看出BindUobject是模板函数:

有两个模板参数——绑定函数所在类的类型(UserClass)和传给绑定函数的一系列参数的类型(VarTypes);

有三个函数参数——绑定函数所在类的实例(InUserObject)、绑定函数(InFunc)、传给绑定函数的一系列参数(Vars)。

BindUobject调用CreateUObject(前文提到过),传入自身所有参数。CreateUObject根据传入的参数创建并返回一个TBaseDelegate的实例赋值给this指针,进去看看CreateUObject

TBaseDelegate<RetValType, ParamTypes...> CreateUObject(UserClass* InUserObject, typename TMemFunPtrType<true, UserClass, RetValType (ParamTypes..., VarTypes...)>::Type InFunc, VarTypes... Vars)
{
	TBaseDelegate<RetValType, ParamTypes...> Result;
	TBaseUObjectMethodDelegateInstance<true , UserClass, TFuncType, VarTypes...>::Create(Result, InUserObject, InFunc, Vars...);
	return Result;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

CreateUObject先创建一个TBaseDelegate类实例(Result)用来存放结果,使用TBaseUObjectMethodDelegateInstance内的Create方法,修改Result的数据,最终返回Result

关于TBaseUObjectMethodDelegateInstance,在TBaseDelegate类内创建函数(Create**)和绑定函数(**Bind)的上方,有关于TBaseUObjectMethodDelegateInstance一系列子类的代码(源码中每一个都写成一行,我整理成熟悉的格式):

template <typename UserClass, typename Var1Type> 
struct TUObjectMethodDelegate_OneVar : TBaseUObjectMethodDelegateInstance<false, UserClass, TFuncType, Var1Type> 
{
	typedef TBaseUObjectMethodDelegateInstance<false, UserClass, TFuncType, Var1Type> Super; 

	TUObjectMethodDelegate_OneVar(UserClass* InUserObject, typename Super::FMethodPtr InMethodPtr, Var1Type Var1) 
		: Super(InUserObject, InMethodPtr, Var1) 
	{} 
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

注:源码不少,只列举其中之一,具体可去源文件查看

根据代理函数参数的个数定义了不同的TBaseUObjectMethodDelegateInstance子类,在CreateUObject函数中,根据传入的参数自动匹配合适的TBaseUObjectMethodDelegateInstance子类,进去看看它的源码和”亲戚“:

template <bool bConst, class UserClass, typename WrappedRetValType, typename... ParamTypes, typename... VarTypes>
class TBaseUObjectMethodDelegateInstance<bConst, UserClass, WrappedRetValType (ParamTypes...), VarTypes...> : public IBaseDelegateInstance<typename TUnwrapType<WrappedRetValType>::Type (ParamTypes...)>
{
	// ……
public:
	TBaseUObjectMethodDelegateInstance(UserClass* InUserObject, FMethodPtr InMethodPtr, VarTypes... Vars)
		: UserObject(InUserObject)
		, MethodPtr (InMethodPtr)
		, Payload   (Vars...)
		, Handle    (FDelegateHandle::GenerateNewHandle)
	{
		// ……
	}

	// ……

protected:

	// Pointer to the user's class which contains a method we would like to call.
	TWeakObjectPtr<UserClass> UserObject;

	// C++ member function pointer.
	FMethodPtr MethodPtr;

	// Payload member variables (if any).
	TTuple<VarTypes...> Payload;

	// ……
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

在这里插入图片描述

TBaseUObjectMethodDelegateInstance其实是真正保存绑定函数和函数所在类的地方,构造函数接收Create传过来的参数并保存——UserObject记录函数所在类的实例、MethodPtr记录绑定函数、Payload记录参数。

到这里绑定就基本完成了,现在再来看看执行(Execute):

FORCEINLINE RetValType Execute(ParamTypes... Params) const
{
	TDelegateInstanceInterface* LocalDelegateInstance = GetDelegateInstanceProtected();

	return LocalDelegateInstance->Execute(Params...);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

GetDelegateInstanceProtected函数获取前面提到的保存绑定函数的TBaseUObjectMethodDelegateInstance子类实例,再调用其中的Execute方法传入参数,该方法借助保存参数的类型——TTuple,调用ApplyAfter,并传入保存绑定函数指针来调用绑定函数:

virtual RetValType Execute(ParamTypes... Params) const override final
{
	// ……
	return Payload.ApplyAfter(TMemberFunctionCaller<MutableUserClass, FMethodPtr>(MutableUserObject, MethodPtr), Params...);
}
  • 1
  • 2
  • 3
  • 4
  • 5

二.本章小结:

好了,用了这么多笔墨只讲了个单播 ,浅析了一下单播代理中的DECLARE_DELEGATE_OneParam,了解了它其他类型的单播也是这个实现思路,一一列出太冗余了,可以的话还是希望大家能去源码看看。

笔者越看越佩服虚幻引擎的开发者,真的把C++用的出神入化,由于笔者水平有限,可能很多地方没讲清楚,本章就到这,谢谢观看 。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/127233
推荐阅读
  

闽ICP备14008679号