#pragma once
#include <functional>
#include <type_traits>
#include <vector>
#include <memory>
#include <tuple>
/*
Multicast Delegate의 구성 ...
1. Delegate 객체 인스턴스화
2. shared_ptr로 참조되는 오브젝트 인스턴스와 함께, 델리게이트에 등록할 함수를 지정하면 내부 배열에
3. 이후 Delegate에서 인자와 함께 매 Execute(...) 호출 시마다, weak_ptr로 저장된 오브젝트의 유효성을 검증, 자동으로 유효하지 않은 참조를 제거한다. 이 때 순서는 유지되지 않음.
*/
// Helper macro which deduces source type of object pointer
#define AddDynamic(Object, Func) __AddDynamic<std::remove_reference_t<decltype(*Object)>>(Object, Func)
template<typename ... PARAMS>
class Delegate
{
private:
// Refrences source object as weak_ptr<void> since it only tracks validation of object refrence
using DelegateInternalDataType = std::tuple<int, std::weak_ptr<void>, std::function<void( PARAMS... )>>;
std::vector<DelegateInternalDataType> Functions;
public:
// Adds dynamically bound funciton. It returns IdCode which can identify specific function and enables remove it.
template<typename OBJECT>
unsigned __int64 __AddDynamic( std::weak_ptr<OBJECT> Object
, void( OBJECT::*function ) ( PARAMS... ) )
{
static unsigned __int64 ID_NUM = 0;
// wrapper class to enwrap member function pointer as functor
class __FUNCTION
{
public:
std::weak_ptr<OBJECT> ObjectInstance;
void( OBJECT::*BoundFunction ) ( PARAMS... );
void operator()( PARAMS ... args )
{
( ( *ObjectInstance.lock() ).*BoundFunction )( args... );
}
};
// instanciates functor and store ID/object/functor tuple to Functions array
__FUNCTION func;
func.ObjectInstance = Object;
func.BoundFunction = function;
Functions.emplace_back( std::make_tuple( ID_NUM, Object, func ) );
return ID_NUM++;
}
// Broadcast arguments to stored functions
void Execute( PARAMS ... args )
{
for ( size_t i = 0; i < Functions.size(); ++i )
{
DelegateInternalDataType& Tuple = Functions[i];
// removes expired objects' function from array
if ( std::get<1>(Tuple).expired() )
{
Tuple = Functions.back();
Functions.pop_back();
--i;
}
else
{
std::get<2>(Tuple)( args... );
}
}
}
};
대충 주석을 달아놓긴 했는데, 요지는 shared_ptr에 의해 참조되는 객체가 파괴됐을 때 자동적으로 리스트에서 해당 오브젝트와 바인딩된 함수를 제거하는 멀티캐스트 델리게이트의 구상입니다. ID Code도 만들어놨는데, 이건 일단 프로토타입이라 더 이상 손 안 대고 weak_ptr이 아닌, 조금 더 추상화된 약 참조 객체를 받는 인터페이스를 구상할까 싶어용.
아래는 용례입니다
#include <cstdio>
#include "Delegate.hxx"
class SampleObject
{
int Count;
public:
SampleObject( int cnt ) : Count( cnt ) {}
void ShowInt( int cnt ) { printf( "My: %d, got: %d\n", Count, cnt ); }
};
int main( char* args )
{
Delegate<int> dele;
auto smpl1 = std::make_shared<SampleObject>( 3 );
{
auto smpl2 = std::make_shared<SampleObject>( 7 );
dele.AddDynamic( smpl1, &SampleObject::ShowInt );
dele.Execute( 7 );
printf( "\n" );
dele.AddDynamic( smpl2, &SampleObject::ShowInt );
dele.Execute( 2 );
}
printf( "\n" );
dele.Execute( 6 );
dele.AddDynamic( smpl1, &SampleObject::ShowInt );
dele.AddDynamic( smpl1, &SampleObject::ShowInt );
dele.AddDynamic( smpl1, &SampleObject::ShowInt );
printf( "\n" );
dele.Execute( 9 );
getchar();
return 0;
}
////////// 실행 ///////////////
My: 3, got: 7
My: 3, got: 2
My: 7, got: 2
My: 3, got: 6
My: 3, got: 9
My: 3, got: 9
My: 3, got: 9
My: 3, got: 9
///////////////////////////////
functional 뒀다뭐함?
?? 그게 모에요
std::function 찾아봐라 - dc App
c++ 함수 포인터
그리고 매크로는 뭐하러 쓰냐;; 메타함수 하나 정의하면 되지 - dc App
으허잉 매크로 안 쓰니까 타입 추론을 못 하던뎅 ...
아 저것두 std::funciton 한참 찾아보구 쓴건데 std::function엔 클래스 non-static member function 바인딩은 못하지 않나여 ??
그래서 일부러 functor 클래스 템플릿을 둬서 전달받은 멤버 함수 포인터를 래핑한건뎅 ...
ㄴ 아니 되는데 std::function 에다가 멤버함수 호출을 랩핑하는 람다 넣으면 됨 - dc App
아 그러네여.. 곰곰이 생각해보니까 제가 함수 안에 선언해둔 저건 그냥 자동 생성되는 람다 풀어 쓴 꼴이나 마찬가지였네여 ...
흑흑 울면서 TC++PL 정독하러 갑니다
야 눈마주치지마 어색해진다