デリゲート は、汎用的かつ型安全な方法で C++ オブジェクト上でメンバ関数の呼び出しを可能にします。デリゲートを使用すると、呼び出す側がオブジェクトのタイプを知らない場合でも、任意のオブジェクトのメンバ関数を動的に結合し、未来の時間でオブジェクト上に関数を呼び出すことができます。デリゲート オブジェクトは完全に安全にコピーできます。デリゲートは値渡しすることができますが、ヒープ上にメモリを割り当てる必要があるので通常はお勧めしません。可能な限り、デリゲートは常に参照渡しが望ましいです。デリゲートは可能な限り参照によって渡されることが望ましいです。エンジンは 3 種類のデリゲートをサポートしています。
- シングルキャスト
- マルチキャスト
- ダイナミック (Uobject、シリアライズ可能)
デリゲートの宣言
提供された宣言用マクロの一つを使ってデリゲートを宣言します。デリゲートと結合する関数のシグネチャで使用するマクロが決定されます。これらの汎用的な関数シグネチャの様々な組み合わせを事前に定義します。これを使って、必要な型がどれであっても、戻り値とパラメータに型名を入れてデリゲートの型を宣言することができます。現在、以下の組み合わせを使用しているデリゲートシグネチャに対応しています。
- 値を返す関数
const
として宣言された関数- 4 つまでの "ペイロード" 変数
- 8 つまでの関数パラメータ
下表を使用してデリゲートの宣言に使う宣言用マクロを探してください。
関数シグネチャ | 宣言用マクロ |
---|---|
void Function() |
DECLARE_DELEGATE(DelegateName) |
void Function(Param1) |
DECLARE_DELEGATE_OneParam(DelegateName, Param1Type) |
void Function(Param1, Param2) |
DECLARE_DELEGATE_TwoParams(DelegateName, Param1Type, Param2Type) |
void Function(Param1, Param2, ...) |
DECLARE_DELEGATE_<Num>Params(DelegateName, Param1Type, Param2Type, ...) |
<RetValType> Function() |
DECLARE_DELEGATE_RetVal(RetValType, DelegateName) |
<RetValType> Function(Param1) |
DECLARE_DELEGATE_RetVal_OneParam(RetValType, DelegateName, Param1Type) |
<RetValType> Function(Param1, Param2) |
DECLARE_DELEGATE_RetVal_TwoParams(RetValType, DelegateName, Param1Type, Param2Type) |
<RetValType> Function(Param1, Param2, ...) |
DECLARE_DELEGATE_RetVal_<Num>Params(RetValType, DelegateName, Param1Type, Param2Type, ...) |
デリゲート関数は UFunctions と同じ 指定子 をサポートしますが、UFUNCTION
の代わりに UDELEGATE
マクロを使用します。たとえば、以下のコードは BlueprintAuthorityOnly
指定子を FInstigatedAnyDamageSignature
デリゲートに追加します。
UDELEGATE(BlueprintAuthorityOnly)
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FInstigatedAnyDamageSignature, float, Damage, const UDamageType*, DamageType, AActor*, DamagedActor, AActor*, DamageCauser);
マルチキャスト、動的、ラップされたデリゲート用に、上記のマクロのバリエーションも提供されます。
- DECLARE_MULTICAST_DELEGATE...
- DECLARE_DYNAMIC_DELEGATE...
- DECLARE_DYNAMIC_MULTICAST_DELEGATE...
- DECLARE_DYNAMIC_DELEGATE...
- DECLARE_DYNAMIC_MULTICAST_DELEGATE...
名前空間やクラス宣言内でも、デリゲート シグネチャ宣言はグローバルスコープで存在することが可能です。これらの宣言は関数ボディ内には存在しない場合があります。
これらのタイプのデリゲートの宣言についての詳細は「動的デリゲート」と「マルチキャスト デリゲート」を参照してください。
デリゲート関数は UFunctions と同じ 指定子 をサポートしますが、UFUNCTION
の代わりに UDELEGATE
マクロを使用します。
デリゲートの結合
デリゲートシステムはある一定のオブジェクト型を理解し、これらのオブジェクトを使用する場合に追加機能が有効になります。UObject のメンバあるいはシェアードポインタ クラスにデリゲートを結合させた場合、
デリゲート システムはそのオブジェクトへの弱い参照を保つことができるので、
オブジェクトがデリゲート側から破棄されても、IsBound()
関数または ExecuteIfBound()
関数を呼び出せば処理することができます。サポートされているオブジェクトの型別の特別な結合記法に注意してください。
関数 | 説明 |
---|---|
Bind |
既存のデリゲート オブジェクトに結合します。 |
BindStatic |
生の C++ ポインタのグローバル関数デリゲートを結合します。 |
BindRaw |
生の C++ ポインタ デリゲートを結合します。生のポインタはいかなるタイプの参照も使用しないので、ターゲット オブジェクトを削除後の Execute または ExecuteIfBound の呼び出しは安全ではありません。 |
BindLambda |
functor を結合します。一般的にラムダ関数に使用されます。 |
BindSP |
シェアードポインタ ベースのメンバ関数デリゲートを結合します。シェアードポインタのデリゲートはオブジェクトに対する弱い参照を保ちます。ExecuteIfBound を使用して呼び出します。 |
BindUObject |
UObject メンバ関数デリゲートを結合します。 UObject デリゲートはターゲットにする UObject に対して弱い参照を保ちます。ExecuteIfBound を使用して呼び出します。 |
UnBind |
このデリゲートの結合を解除します。 |
これらの関数のバリエーション、引数、実装については、DelegateSignatureImpl.inl
(場所は「..\UE4\Engine\Source\Runtime\Core\Public\Templates\
」) を確認してください。
ペイロード データ
デリゲートに結合する時、ペイロード データも一緒に渡すことができます。呼び出されると結合した関数に直接渡される任意の変数です。これは、 結合時にデリゲートそのものにパラメータを格納することができるので、非常に便利です。全てのデリゲート型 (動的な型を除く) は、ペイロード変数に自動的に対応しています。 このサンプルでは、 bool と int32 という 2 つのカスタム変数をデリゲートに渡します。デリゲートが呼び出されると、これらのパラメータが結合関数に渡されます。追加の変数による引数は、 必ず、デリゲート型に必要な引数の後に置く必要があります。
MyDelegate.BindRaw( &MyFunction, true, 20 );
デリゲートの実行
デリゲートと結合している関数は、デリゲートの Execute()
関数を呼ぶと実行されます。デリゲートが結合しているかどうかを実行前に確認する必要があります。デリゲートに戻り値があり、初期化されておらず、後でアクセスされる出力パラメータを持つ場合があるため、
コードをより安全にする目的で行います。結合していないデリゲートを実行すると、
場合によってはメモリ上に実際に書き込まれることがあります。デリゲートの実行が安全かどうかは IsBound()
を呼び出して確認できます。デリゲートに戻り値がない場合も ExecuteIfBound()
を呼び出せますが、
初期化されていない可能性のある出力パラメータに注意してください。
実行関数 | 説明 |
---|---|
Execute |
結合をチェックせずにデリゲートを実行します |
ExecuteIfBound |
デリゲートが結合されているかを確認し、結合している場合は Execute を呼び出します |
IsBound |
デリゲートが結合されているかを確認します (Execute の呼び出しを含むコードの前の場合が多い) |
マルチキャスト デリゲートの実行に関する詳細は「マルチキャスト デリゲート」を参照してください。
使用例
どこからでも呼び出せるメソッドでクラスを持つとします。
class FLogWriter
{
void WriteToLog(FString);
};
WriteToLog 関数を呼び出すには、その関数のシグネチャに対してデリゲート型を作成する必要があります。そのためには、まず以下のマクロの 1 つを使ってデリゲートを 宣言します。例えば、以下は簡単なデリゲート型です。
DECLARE_DELEGATE_OneParam(FStringDelegate, FString);
これで FString
という型の単一パラメータを受け取る FStringDelegate
と呼ばれるデリゲート型が作成されます。
以下はクラスで FStringDelegate
を使用する例です。
class FMyClass
{
FStringDelegate WriteToLogDelegate;
};
これにより、任意のクラスのメソッドに対してクラスはポインターを保持できるようになります。クラスは、このデリゲートが関数シグネチャであるということしか分かっていません。
デリゲートを割り当てるには、デリゲート クラスのインスタンスを作成し、テンプレートパラメータとしてそのメソッドを所有しているクラスと共に渡します。オブジェクトのインスタンスと
メソッドの実際の関数アドレスも渡します。FLogWriter
クラスのインスタンスを作成し、
そのオブジェクトのインスタンスの WriteToLog
メソッドに対してデリゲートを作成します。
TSharedRef<FLogWriter> LogWriter(new FLogWriter());
WriteToLogDelegate.BindSP(LogWriter, &FLogWriter::WriteToLog);
これで、クラスのメソッドにデリゲートを動的に結合しました。とてもシンプルです。
シェアードポインタが所有するオブジェクトへ結合したので、BindSP
の SP の部分は「シェアードポインタ」を表していることにご注意ください。BindRaw() や BindUObject() など
オブジェクト型には異なるバージョンがあります。
WriteToLog
メソッドは、 FLogWriter
クラスについて何も知らなくても FMyClass で呼び出すことができます。デリゲートの呼び出しには、以下のように Execute()
メソッドを使うだけです。
WriteToLogDelegate.Execute(TEXT("Delegates are great!"));
関数をデリゲートに結合する前に Execute() を呼び出すと、アサーションがトリガーされます。以下を使いたくなる場合が多いでしょう。
WriteToLogDelegate.ExecuteIfBound(TEXT("Only executes if a function was bound!"));