#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

///////////////////////////////