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.

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