定義
#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.