UE4之Delegate:动态单播

定义

#define FUNC_CONCAT( ... ) __VA_ARGS__

#define DECLARE_DYNAMIC_DELEGATE( DelegateName ) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_DELEGATE) FUNC_DECLARE_DYNAMIC_DELEGATE( FWeakObjectPtr, DelegateName, DelegateName##_DelegateWrapper, , FUNC_CONCAT( *this ), void )

定义一个动态单播,UHT会根据BODY_MACRO_COMBINE生成一个FILE_ID_LINE_DELEGATE宏,这个函数留到后面说。
先看FUNC_DECLARE_DYNAMIC_DELEGATE 宏定义

#define FUNC_DECLARE_DYNAMIC_DELEGATE( TWeakPtr, DynamicDelegateName, ExecFunction, FuncParamList, FuncParamPassThru, ... ) \
	class DynamicDelegateName : public TBaseDynamicDelegate<TWeakPtr, __VA_ARGS__> \
	{ \
	public: \
		DynamicDelegateName() \
		{ \
		} \
		\
		/** Construction from an FScriptDelegate must be explicit.  This is really only used by UObject system internals. */ \
		explicit DynamicDelegateName( const TScriptDelegate<>& InScriptDelegate ) \
			: TBaseDynamicDelegate<TWeakPtr, __VA_ARGS__>( InScriptDelegate ) \
		{ \
		} \
		\
		/** Execute the delegate.  If the function pointer is not valid, an error will occur. */ \
		inline void Execute( FuncParamList ) const \
		{ \
			/* Verify that the user object is still valid.  We only have a weak reference to it. */ \
			checkSlow( IsBound() ); \
			ExecFunction( FuncParamPassThru ); \
		} \
		/** Execute the delegate, but only if the function pointer is still valid */ \
		inline bool ExecuteIfBound( FuncParamList ) const \
		{ \
			if( IsBound() ) \
			{ \
				ExecFunction( FuncParamPassThru ); \
				return true; \
			} \
			return false; \
		} \
	};

声明一个动态委托, 并使用包装代理方法(DelegateName##_DelegateWrapper)执行委托.
再看看基类TBaseDynamicDelegate,基类里有个重要的方法,我们在用宏定义绑定函数时遇到过,就是AddDynamic宏, 它实际上被宏定义为成员函数__Internal_BindDynamic

template <typename TWeakPtr, typename RetValType, typename... ParamTypes>
class TBaseDynamicDelegate : public TScriptDelegate<TWeakPtr>
{
public:
	template< class UserClass >
	class TMethodPtrResolver
	{
	public:
		typedef RetValType (UserClass::*FMethodPtr)(ParamTypes... Params);
	};
	
	template< class UserClass >
	void __Internal_BindDynamic( UserClass* InUserObject, typename TMethodPtrResolver< UserClass >::FMethodPtr InMethodPtr, FName InFunctionName )
	{
		check( InUserObject != nullptr && InMethodPtr != nullptr );
		this->Object = InUserObject;

		// Store the function name.  The incoming function name was generated by a macro and includes the method's class name.
		this->FunctionName = InFunctionName;
		ensureMsgf(this->IsBound(), TEXT("Unable to bind delegate to '%s' (function might not be marked as a UFUNCTION or object may be pending kill)"), *InFunctionName.ToString());
	}
};

#define BindDynamic( UserObject, FuncName ) __Internal_BindDynamic( UserObject, FuncName, STATIC_FUNCTION_FNAME( TEXT( #FuncName ) ) )

TBaseDynamicDelegate继承自TScriptDelegate, TScriptDelegate里定义了绑定对象和函数名, 还有最重要的ProcessDelegate函数

template <typename TWeakPtr = FWeakObjectPtr>
class TScriptDelegate
{
public:
	void BindUFunction( UObject* InObject, const FName& InFunctionName )
	{
		Object = InObject;
		FunctionName = InFunctionName;
	}
	void Unbind()
	{
		Object = nullptr;
		FunctionName = NAME_None;
	}
	
	template <class UObjectTemplate>
	void ProcessDelegate( void* Parameters ) const
	{
		checkf( Object.IsValid() != false, TEXT( "ProcessDelegate() called with no object bound to delegate!" ) );
		checkf( FunctionName != NAME_None, TEXT( "ProcessDelegate() called with no function name set!" ) );
        
		UObjectTemplate* ObjectPtr = static_cast< UObjectTemplate* >( Object.Get() );	// Down-cast
		checkSlow( !ObjectPtr->IsPendingKill() );

		// Object *must* implement the specified function
		UFunction* Function = ObjectPtr->FindFunctionChecked( FunctionName );

		// Execute the delegate!
		ObjectPtr->ProcessEvent(Function, Parameters);
	}
protected:
	TWeakPtr Object;
	FName FunctionName;
	friend class FCallDelegateHelper;
};

对于ProcessDelegate, 通过在绑定到委托的对象上调用命名函数来执行委托. 在调用此函数之前,应始终先通过调用IsBound() 来验证委托是否可以安全执行.
通常,永远不要直接调用此函数. 而是在派生类上调用Execute().

意思就是说, ProcessDelegate应该在我们自定义的DynamicDelegateName类执行Execute(), 通过上面代码描述知道,Execute内部执行的是ExecFunction函数, 也就是宏定义里的DelegateName##_DelegateWrapper函数.

DelegateName##_DelegateWrapper函数的定义,就是UHT为我们在generated.h头文件里生成的

#define Hello_Source_Hello_Public_MyObject_h_9_DELEGATE \
static inline void FMyTestDynamicDelegate_DelegateWrapper(const FScriptDelegate& MyTestDynamicDelegate) \
{ \
	MyTestDynamicDelegate.ProcessDelegate<UObject>(NULL); \
}

可以看到, 生成的MyDelegateDemo_Source_MyDelegateDemo_MyClass_h_17_DELEGATE宏里, 就是一个FMyTestDynamicDelegate_DelegateWrapper函数, 它内部调用了ProcesDelegate函数.

MyClassDelegate_DelegateWrapper的参数是一个FScriptDelegate类引用, 通过宏定义我们知道,我们在Execute的时候传了一个FuncParamPassThru参数, 而FuncParamPassThru参数的定义是

#define FUNC_CONCAT( ... ) __VA_ARGS__

FUNC_CONCAT( *this )

因此, 我们将this指针解引用然后传过去.

FMyTestDynamicDelegate_DelegateWrapper的参数是一个FScriptDelegate参数, 而我们的代理类是一个TScriptDelegate的子类,点FScriptDelegate然后F12, 然后在WeakObjectPtr.h里,看到了typedef定义

// Typedef script delegates for convenienceconvenience.
typedef TScriptDelegate<> FScriptDelegate;

这是返回值为void的动态单播类型,类似的还有一个带返回值的动态单播FUNC_DECLARE_DYNAMIC_DELEGATE_RETVAL,唯一区别就是这个宏指定了返回值RetValType.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章