그냥 게임개발자

델리게이트 본문

Unreal스터디/Delegate

델리게이트

sudoju 2024. 1. 1. 16:24
함수를 다루는 방법에는 여러 가지가 있습니다.

가장 기본적인 방법은 함수 포인터를 활용한 콜백(callback) 함수 구현입니다.
이 방법은 가능하긴 하지만, 함수를 정의하고 사용하는 과정이 복잡하고, 안전성을 개발자가 직접 검증해야 합니다.

C++ 17 구약의 std::bind와 std::function을 활용하는 방법이 있습니다.
이 방법은 사용이 간편하지만, 실행 속도가 비교적 느린 단점이 있습니다.

C#에서는 델리게이트(delegate)라는 키워드를 제공합니다.
이를 활용하면 마치 객체처럼 함수를 다룰 수 있어 편리합니다.
델리게이트는 안정적이고 간편한 선언을 가능하게 해줍니다.
언리얼 C++에서도 델리게이트를 지원합니다.
이를 통해 느슨한 결합 구조를 간편하고 안정적으로 구현할 수 있습니다.
이러한 방법들을 통해 함수를 더욱 효율적으로 다룰 수 있습니다.

 

https://docs.unrealengine.com/5.3/ko/delegates-and-lamba-functions-in-unreal-engine/

 

 

Delegate(델리게이트)로 C++ 오브젝트 상의 멤버 함수 호출을 일반적이고 유형적으로 안전한 방식으로 할 수 있다.

 

이 말은 어떤 객체를 사용을 할 때 객체 자체에 강한 결합을 하는 것이 아니고 어떤 객체가 가지고 있는 멤버 함수와 그 다음에 delegate를 연결해서 느슨한 결합을 만들 수 있다는 것으로 이해하면 된다.

 

델리게이트 오브젝트는 복사해도 완벽히 안전하다.

가급적이면 델리게이트는 항상 참조 전달해야 한다.

 

델리게이트 선언하기

  • 이득우 선생님의 자료를 통해 수정할 예정

델리게이트 바인딩하기

  • Bind() -> 기존 델리게이트 오브젝트에 바인딩
  • BindStatic() -> raw C++ 포인터 글로벌 함수 델리게이트를 바인디
  • BindRaw() -> RawC++ 포인터 델리게이트에 바인딩. 날(Raw) 포인터는 어떠한 종류의 레퍼런스도 사용하지 않아, 만약 오브젝트가 델리게이트 치하에서 삭제된 경우 호출하기가 안전하지 않을 수도 있고 Execute() 호출시에는 조심해야 함.
  • BindSP() -> 공유포인터-기반 멤버 함수 델리게이트에 바인딩. 공유 포인터 델리게이트는 오브젝트로의 약한 레퍼런스를 유지. ExecuteIfBound()로 호출할 수 있음
  • BindUObject() -> UObject기반 멤버 함수 델리게이트를 바인딩. UObject 델리게이트는 오브젝트로의 약한 레퍼런스를 유지. ExecutIfBound()로 호출 가능
  • UnBind() -> 이 델리게이트 바인딩을 해제.

 

페이로드 데이터

  • 델리게이트에 바인딩 할 때, 페이로드 데이터를 같이 전해줄 수 있음.
  • 묶는 객체에 대한 정보를 지정해서 하나의 구문으로 편하게 묶을 수 있다는 것을 의미.
MyDelegate.BindRaw(&MyFunction, true, 20);

 

델리게이트 실행하기

  • 델리게이트에 바인딩된 함수는 델리게이트의 Execute()함수를 호출하여 실행한다.
  • 델리게이트를 실행하기 전 "바인딩"이 되었는지 반드시 확인해야 함.
  • 델리게이트가 실행해도 안전한 지는 IsBound()를 호출하여 검사할 수 있다.
  • 또한 반환값이 없는 델리게이트에 대해서는 ExecuteIfBound()를 호출할 수 있으나, 출력 파라미터는 초기화되지 않을 수 있다는 점을 주의하자.

 

사용 예제

 

아무데서나 호출했으면 하는 메서드를 가진 클래스가 있다고 가정하자.

class FLogWriter
{
     void WriteToLog(Fstring);
};

WriteToLog함수를 호출하려면, 해당 함수의 시그니처에 맞는 델리게이트 유형을 생성해야 한다.

그러기 위해서는 먼저 아래 매크로 중 하나를 사용하여 델리게이트를 선언해야 한다.

DECLARE_DELEGATE_OneParam(FStringDelegate, FString);

이는 'FStringDelegate'라는 델리게이트 유형을 생성하며, 'FString' 유형 파라미터를 하나 받는다.

클래스에서 이 'FStringDelegate'를 어떻게 사용하는가는 아래와 같다.

class FMyClass
{
    FstringDelegate WriteToLogDelegate;
};

이렇게 위 클래스의 임의 클래스 안에 있는 메서드로의 포인터로 담을 수 있다.

위 클래스가 이 델리게이트에 대해 아는 것이라고는, 함수 시그니처일 뿐이다.

 

이제 델리게이트 할당을 위해, 단순히 델리게이트 클래스의 인스턴스를 생성하고, 그 메서드를 소유하는 클래스와 함께 템플릿 파라미터로 전해준다.

TSharedRef<FLogWriter> LogWriter(new FLogWriter());

// 자기 오브젝트의 인스턴스와 그 메서드의 실제 함수 주소 역시 전해줘야 함.
WriteToLogDelegate.BindSP(LogWriter, &FLogWriter::WriteToLog);

이렇게 클래스의 메서드에 델리게이트를 동적으로 바인딩 했다.

 

BindSP의 SP 부분은 shared pointer, 공유 포인터를 뜻한다.

공유 포인터에 소유된 오브젝트에 바인딩하고 있기 때문이다.

 

이제 'FLogWriter'클래스에 대해 아무것도 모를지라도 FMyClass를 통해 "WriteToLog" 메서드를 호출할 수 있다.

자신의 델리게이트를 호출하려면, 그냥 'Execute()'메서드를 사용하면 된다.

 

WriteToLogDelegate.Execute(TEXT("델리게이트 실행"));

 

델리게이트에 함수를 바인딩 하기 전 Execute()를 호출하면 assert가 발동된다.

그런 경우를 피하기 위해 대부분 이렇게 하는 것이 좋음

WriteToLogDelegate.ExecuteIfBound(TEXT("함수가 바인딩 되었을 때만 실행!"));

 

 

 

 

DECLARE_{델리게이트유형}DELEGATE{함수정보}

  • 델리게이트 유형 : 어떤 유형의 델리게이트인지 구상
    • 일대일 형태로 C++만 지원한다면 유형은 공란으로 둔다.
      DECLARE_DELEGATE
    • 일대다 형태로 C++만 지원한다면 MULTICAST를 선언한다.
      DECLARE_MULTICAST
    • 일대일 형태로 블루프린트를 지원한다면 DYNAMIC을 선언한다.
      DECLARE_DYNAMIC
    • 일대다 형태로 블루프린트를 지원한다면 DYNAMIC과 MULTICAST를 조합한다.
      DECLARE_DYNAMIC_MULTICAST
  • 함수 정보 : 연동 될 함수 형태를 지정한다.
    • 인자가 없고 반환값도 없으면 공란으로 둔다.
      ex) DECLARE_DELEGATE
    • 인자가 하나고 반환값이 없으면 OneParam으로 지정한다.
      ex) DECLARE_DELEGATE_OneParam
    • 인자가 세 개고 반환값이 있으면 RetVal_ThreeParams로 지정한다.
      ex) DECLARE_DELEGATE_RetVal_ThreeParams (MULTICAST는 반환값을 지원하지 않음)
    • 최대 9개까지 지원함

'Unreal스터디 > Delegate' 카테고리의 다른 글

발행 구독 디자인 패턴  (0) 2024.01.01