사전적 의미의 delegate는 '대리자'로써 뭔가를 대신 해준다는 의미고, C#에서의 delegate는 일종의 콜백 함수 '리스트'로써 입력과 출력이 동일한 함수들을 일괄 호출하는데 사용된다.


이게 C++ 관점에서 보면 함수 포인터 리스트를 들고 있는 간단한 클래스 정도인데, 개념적으로도 어렵지 않고 구현하는 것도 그렇게 어렵지 않다. 말 그대로 함수 포인터 리스트를 만들어도 되고, 인터페이스 클래스를 정의하고 그걸 상속 받은 클래스 리스트를 만들어도 된다. 다만...필요할때 마다 매번 만드는게 은근 귀찮아 template과 operator overriding을 이용하여 재사용 가능한 delegate 클래스를 만들어 보도록 하겠다(c++ 11 이상).


우선 C# delegate의 인터페이스를 살펴 보면 :

  • 콜백 함수를 등록하기 위한 operator '+='
  • 등록된 콜백 함수를 제거하기 위한 operator '-='
  • 등록된 콜백 함수를 개별로 호출 하기 위한 iterate 인터페이스
정도로 정리가 된다.

이 포스트에서는 전체 코드는 살펴 보지 않고 부분 별 구현 내용만 살짝씩 살펴 볼 예정이다. 전체 코드는 다음 링크를 타고가면 된다(전체 코드 보기 : https://github.com/ChoiIngon/gamnet/blob/master/Gamnet/Library/Delegate.h)

클래스 선언 :
template <class T> class Delegate; /* undefiend */
template <class R, class... ARGS> class Delegate<R(ARGS...)>
template <class... ARGS> class Delegate<void(ARGS...)>
기본적으로 std::function과 비슷한 문법으로 사용하기 위해서 템플릿 특수화를 적용했다. 선언만 하고 정의는 구현되지 않은 기본 클래스를 만들고, 그 후 템플릿 특수화가 적용된 Delegate 클래스를 선언한다. std::function 처럼 사용 할 수 있도록 '리턴타입(인자 리스트)' 와 같은 형태로 선언하는데 여기서 눈여겨 봐야 할 부분은 리턴 타입이 void 형인지 아닌지에 따라 특수화를 나눴다는 것이다. 콜백 함수 리스트를 호출하는 부분에서 리턴 타입에 따라 구현이 달라져야 하기 때문에 클래스 자체를 템플릿 특수화를 통해 구분했다.


함수 포인터 리스트 :

std::list<std::function<R(ARGS...)>> funcs;

일반 함수 포인터 뿐만 아니라 클래스 멤버 함수도 저장 할수 있도록 std::function 리스트를 사용 했다.


operator += : 

Delegate<R(ARGS...)>& operator += (std::function<R(ARGS...)> const& arg)

...자세한 설명은 생략한다...


operator -= :

기존에 등록된 함수 포인터 주소를 비교하여 리스트에서 삭제해주는 오퍼레이터다. std::function::target 템플릿 멤버 함수를 사용하는데 여기서 주의 해야 할 부분이 템플릿 클래스의 템플릿 멤버 함수를 호출하게 되면 컴파일러가 타입을 제대로 추론하지 못해 템플릿 멤버 함수에 대해서 컴파일 오류를 발생 시킨다. 이럴 경우 명시적으로 템플릿 함수라는 것을 지정하기 위해 함수의 호출 부 앞에 'template' 키워드를 붙여 준다.

Delegate<R(ARGS...)>& operator -= (std::function<R(ARGS...)> const& arg)

{

R (*const* ptr)(ARGS...) = arg.template target<R(*)(ARGS...)>();

...

참고 : [진리는어디에] - 'template' 키워드를 한정자로 사용하기(C++)


사용 :

#include <iostream>


int f_1(int a, int b)

{

return a + b;

}


int g_1(int a, int b)

{

return a - b;

}


void f_2(int a, int b)

{

std::cout << a + b << std::endl;

}


void g_2(int a, int b)

{

std::cout << a - b << std::endl;

}


int main()

{

Delegate<int(int, int)> delegate_1;

delegate_1 += f_1;

delegate_1 += g_1

std::cout << delegate_1(100, 10) << std::endl; // print 90 only


for(auto itr=delegate_1.begin(); itr != delegate_1.end(); itr++)

{

std::cout << (*itr)(100, 10) << std::endl; // print 110 and 90

}


Delegate<void(int, int)> delegate_2;

delegate_2 += f_2;

delegate_2 += g_2

delegate_1(100, 10); // print 110 and 90


return 0;

}


기본적으로 위와 같이 구현 되어 있고 설명에 언급 되지 않은 것은 리턴 타입이 void인 경우를 위한 클래스를 하나 더 만든 부분이다.


전체 코드 보기 : https://github.com/ChoiIngon/gamnet/blob/master/Gamnet/Library/Delegate.h


Posted by kukuta

댓글을 달아 주세요

템플릿 멤버 함수를 특별하게 구분해야 할 필요가 있는 경우 'template' 키워드를 사용한다. 아래 예제 코드를 보고 언제 'template' 키워드를 한정자로 사용해야 하는지 살펴 보도록 하자 :


class A

{

public :

template<class T> T function_m() {};

};


template<class U> void function_n(U argument)

{

char object_x = argument.function_m<char>();

}


위 예제에서, 컴파일러는 템플릿 인자 U가 클래스 A의 인스턴스라는 것을 추론하지 못하기 때문에 'function_m'이 템플릿 멤버 함수라는 것을 알지 못하고 '<'를 비교연산자로 처리 버려 컴파일 오류가 발생한다. 컴파일러에게 이것이 템플릿 함수를 호출하는 것이라는 것을 알리기 위해선 아래와 같이 'template' 한정자를 함수 호출부 앞에 추가 해주어야 한다 :

char objec_x = argument.template function_m<char>();


만일 템플릿 특수화 멤버 함수의 이름이 ., -> 또는 :: 연산자 뒤에 나온다면 , 그리고 그 이름이 명시적으로 한정된 템플릿 인자라면, 'template' 키워드를 꼭 앞에 붙여 줘야 한다. 다음 예제를 살펴 보자 :

#include <iostream>

using namespace std;


class X

{

public :

template<int j> struct S

{

vodi h()

{

cout << "member template's member function:" << j << endl;

}

};


template<int i> void f()

{

cout << "primary: " << i << endl;

}

};


template<> void X::f<20>()

{

cout << "specialize, not-type argument = 20" << endl;

}


template<class T> void g(T* p)

{

p->template f<100>();

p->template f<20>();

typename T::template S<40> s; //멤버 템플릿 앞에 스코프 오퍼레이터를 사용한다

s.h();

}


int main()

{

X temp;

g(&temp);

}


위 예제의 결과물을 보자 :

primary: 100

specialized, non-type argument = 20

member template's member function: 40


위에서 'template' 키워드를 사용하지 않았다면 컴파일러는 '<'를 비교 연산자로 해석한다. 아래는 오류를 발생 시키는 예다 :

p->f<100>();

컴파일러는 템플릿 멤버 함수 f를 일반 함수로 해석하고 '<'를 비교 연산자로 해석하여  오류를 발생 시킨다.


결론 :

  • 템플릿 형태로 넘겨 받은 인자의 템플릿 클래스 인스턴스의 템플릿 함수를 호출 할때는 'template' 키워드를 한정자로 붙여야 한다.

  • 템플릿 멤버 함수를 썼는데 오류가 발생하면 일단 'template'을 앞에 붙여 보자.


원문 : 

https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.cbclx01/keyword_template_qualifier.htm

Posted by kukuta
TAG C++, template

댓글을 달아 주세요

linux command

도구의발견 2018.03.30 12:08

check cpu

$ cat /proc/cpuinfo

$ dmesg | grep cpu


check cpu architecture(x86, x64, x86_64)

$ arch


check 'RAM' memory size :

"free" to see RAM information in KB.

"free -m" to see RAM information in MB

"free -g" to see RAM information in GB


$ cat /proc/meminfo


check 'Disk' storge

"df -h" to see disk partition and storage information

"fdisk -l" to see hard disk information

"du -sk" to see usage of current directory


$ cat /proc/ide/hda/model    -- hda

monitor processes and system resource usage :

"top"

https://www.booleanworld.com/guide-linux-top-command/





Posted by kukuta
TAG Command, Linux

댓글을 달아 주세요



티스토리 툴바