원문 : http://www.drdobbs.com/cpp/extracting-function-parameter-and-return/240000586

주의 : 번역이 개판이므로 원문을 참조하세요.

주의 : 허락받고 번역한 것이 아니므로 언제든 내려갈 수 있습니다.

주의 : 가독성을 높이기 위해 잘 알려진 용어 및 발음이 비슷한 용어는 한글 표기합니다.


C++ 에서 함수 매개 변수와 반환값 추출하기


C++ 의 메타프로그래밍( metaprogramming ) 기능과 generic 프로그래밍을 사용하면, 매개변수 형식에 대한 우아한 파서를 생성하는 것이 가능하다. 명령줄 해석기( command-line interpreter ), 파서( parser ), 구문 검사기( syntax checker )들이 그런 응용프로그램들의 일부이다.


메타프로그래밍은 점점 더 대중적이 되어 왔으며 이제는 현대 C++ 프로그래밍의 필수적인 부분이다. 형식 정보를 이용하면, generic 프로그래밍을 구현하고 컴파일 시점 최적화 및 검사를 수행하는 것이 가능해 진다. 이 기사에서 나는 템플릿 메타프로그래밍을 사용해 함수에서 형식 정보를 추출하고 이 기법을 사용하는 응용프로그램의 예를 보여 줄 것이다.


가능한 실용적인 응용프로그램들 중 하나를 설명하기 위해서, 나는 기본적인 명령어 해석기나 REPL 과 같은 기능을 수행하는 오픈 소스 응용프로그램을 생성했다. 새로운 명령은  함수 포인터를 제공함으로써 등록된다. 이 명령 클래스는 입력 문자열을 평가하고 입력 매개변수를 분석( parse )하고( 매개 변수의 개수와 형식을 검사하고 필요하면 변환함 ), 명령을 실행하고, 결과를 제공하기 위한 충분한 정보를 가지고 있다.


여기 새로운 명령을 등록하는 코드의 일부가 있다; 이 경우 새로운 함수를 등록한다 :



( 다른 예제들은 src\cmd_test.cpp 에 연결된 파일들로부터 찾아 볼 수 있다 ) 명령줄에 앞에서 언급한 코드를 넣은 후에, 당신이 다음 문자열을 실행하려고 시도하면 : add 5 4, 명령어 해석기는 자동으로 그것을 분석하고, 부가적인 작업을 수행하고, 결과를 출력한다. 유효하지 않은 매개변수들을 넣으면, 그것은 무엇이 잘 못 되었는지 알려 줄 것이다 :


input : "add not a number"

output : "add : bad arguments, usage RV : INT add INT INT;"


Figure1 내 test 응용프로그램의 스크린샷.

Figure 1 : 실시간에 인자에 대한 형 검사를 하는 것을 보여 주는 REPL 구현. 


구현.


이 구현에서 나는 Andrei Alexandrescu 에 의해 소개된 type list 의 개념을 사용할 것이다.


여기에 type list 를 구현하는 기본 클래스가 있다 :



Type list 를 생성하기 위해, 나는 다음과 같은 방식으로 이 클래스를 사용한다 :



NullType 은 특별한데 "종료자( terminator )" 형식이라 불린다. 이는 list 의 끝을 찾는데 도움을 주며 NullType{} 이라고 단순하게 정의될 수 있다.


이제 내가 쉽게 모든 함수 형식들( 반환 형식 및 입력 매개변수 )을 열거하는 type list 를 생성할 수 있다고 가정하면, 나는 자동 형식 검사 및 문자열로 받은 데이터들에 대한 변환을 수행할 수 있을 것이다. 그래서 만약 매개변수들이 ( REPL 에서 처럼 ) 문자열로 전달되면, 그것들은 자동으로 그것들을 검사하기 위한 어떤 도구를 필요로 한다. 이를 위한 가장 쉬운 방법은 무엇일까?


나는 다음과 같이 하는 것이 가장 쉬울 것이라고 생각한다 :



이제 이 클래스를 사용해서 다음과 같이 질의를 다룰 수 있다 : pcmd->run( "5 10" );


그 클래스 자체는 매개변수들을 검사하고 매개변수들이 올바를 때 호출을 수행할 것이다. 이 기사에서 내가 문자열을 분석하고 그것을 해당 형식으로 변환하는 연산에 대해서 다루고 있지 않지만, 당신은 내 샘플 프로그램에서 그것의 예를 찾아볼 수 있다. 거기에서는 응용프로그램을 위한 디버그 콘솔을 구현함으로써 지금 설명하고 있는 기술에 대한 응용프로그램을 설명한다.


이제 자동으로 type list 를 생성하기 위해서 createCmd 를 구현하는 방식에 대해 살펴 보자. 이 함수를 구현하기 위해서 나는 템플릿을 사용할 것이다. 다음은 함수의 선언이다 :



여기에서 name 은 함수에 대한 포인터 앞의 첫 번째 매개변수로 전달된다. 이 함수를 구현하는 것은 직관적이다 : 이는 그 작업을 수행할 실제 클래스에 대한 래퍼( wrapper )로서 사용된다.


다음은 단일 매개변수 버전을 위한 구현이다( 매개변수를 가지고 있지 않은 함수들을 위해서 void 일 수 있다 ). 나는 그냥 cmdCreator 클래스에 모든 매개변수를 전달할 뿐이다.



두 개 이상의 매개변수들을 위한 구현은 다음과 유사하다 :



이제 cmdCreator 클래스를 만들어 보자.


선언은 매우 단순해 보인다 : template struct cmdCreator;


다음은 입력 매개변수가 하나 이거나 없는( void ) 함수를 위한 구현이다 :



당신도 봐서 알겠지만, static 함수인 createCmd 는 우리 시스템의 심장이다. 그것은 template 의 매개변수들에 기반해 올바른 방식으로 type list 를 생성한다. 설명을 위한 목적으로 여기에 두 개의 매개변수를 가진 함수를 구현한다 :



CmdBase 에 대한 포인터를 반환하는 동안 TypeList 클래스를 생성한다는 것에 주목하라. 나중에 보여 주겠지만, 나는 CmdBase 로부터 TypeList 를 상속해서 다형성( polymorphism )의 이점을 취했다.


이제 올바른 TypeList 를 생성할 방법을 가지고 있다. 그런데 이것을 어떻게 적용해야 할까?


앞의 예제를 고려해 보자. 먼저 자동 형식 검사를 추가하기 위해서 TypeList 를 확장하라.


단순함을 위해 우리가 이미 분석된 입력 문자열을 가지고 있고, 매개변수들의 리스트를 생성했고, 그 매개변수들은 Variant 클래스로 래핑( wrapping )되어 있다고 가정하자. Variant 클래스는 유용한 개념이다. 부가적인 정보들을 알고자 한다면 Boost.Variant 나 다른 구현들을 참고해 볼 수 있다. 다음은 형식 검사를 수행하는 방법을 보여 준다 :



또한 나는 TypeList< R, NullType > 클래스를 위한 특수화를 정의해야만 한다. 왜냐하면 우리는 위의 함수에서 재귀가 끝나기를 원하기 때문이다.



나는 NumEI 라는 도우미 템플릿 클래스를 하나 더 소개했는데, 이는 type list 안에서 type index 를 위한 카운트를 유지한다. 이것은 선언은 다음과 같다 :



다음은 구현이다 :



봐서 알겠지만, 그것은 자신의 Tail 형식에 대한 반복( recursion )을 사용한다.



이제 run() 메서드를 구현하자. 나는 executor 라는 도우미 템플릿 클래스를 사용할 것인데, 그것은 cmdCreator 클래스를 위한 기술과 동일한 기술을 적용할 것이다. 그러나 먼저 나는 run() 메서드를 제공할 것이다. 할 일은 올바른 특수화를 제공하는 executor::run 메서드를 호출하는 것이다.



executor 클래스는 다음과 같이 선언된다 : template struct executor;


첫 번째 템플릿 매개변수 T 는 함수 포인터인데, 그것은 TypeList 클래스에 의해서 전달된다. 두 번째 템플릿 매개변수는 함수 형식들의 개수이다. 보면 알겠지만, 나는 myIndex 라는 컴파일 시점 상수를 사용하는데, 그것은 NumEl 도우미 클래스의 도움으로 계산됐다.


두 개의 매개변수를 위한 구현은 다음과 같다 :



여러 개의 매개변수들을 위한 구현은 매우 직관적이다.


보면 알겠지만, 나는 myfp 를 사용하는데, 그것은 함수에 대한 포인터이다. 그러나 나는 어떻게 이 형식을 얻는 것일까? cmdCreator 에서 나는 함수 포인터를 TypeList 의 두 번째 매개변수로서 전달한다. 우리의 type list 는 그것을 위한 어떠한 템플릿 매개변수도 가지고 있지 않은데, 어떻게 우리는 것을 생성할까? 이를 위해 나는 템플릿에 기반한 기교( trick )을 사용한다.


그것이 작동하는 방식을 이해하기 위해서 소스 코드를 좀 더 파 보자. TypeList 가 생성되는 지점으로 돌아가면( cmdCreator::createCmd ), 우리는 실제로 필요로 하는 모든 정보를 가지고 있다 : R, T1, T2 등. 우리는 단지 TypeList 를 R (*fptr )( T1, T2, ... ) 으로 변환하는 어떤 방법이 필요할 뿐이다.


내가 이를 변환하기 위해서 사용하는 도우미 클래스들은 TypeConv( TypeConv1, TypeConv2 등. 제공된 형식의 개수만큼 )이다.


종료( terminal ) 클래스들을 위한 가장 단순한 특수화로부터 시작할 것이다. 왜냐하면 그것이 이 아이디어를 가장 명확하게 보여 주기 때문이다. 이 클래스는 단지 하나의 typedef 만을 가지며, 그것이 다이다 :



하나의 매개변수를 가지는 함수들은 다음과 같이 보인다 :



이제 TypeConv 의 generic 버전을 실험해 보자 : 



이 코드는 기본적으로 typedef 를 재귀적인 방식으로 생성한다. TypeConv1 의 첫 번째 매개변수는 TypeList 의 Head 형식이며, 두 번째는 TypeConv 의 템플릿 매개변수 U 이다.그것은 TypeList 로서 재형식화( reformatted )되었다. 다음은 TypeConv1 클래스이다 :



이 코드는 ( 위의 것과 비슷해 보이는 ) TypeConv2 를 호출한다. 예를 들어 U 가 NullType 이었다면, 종료 버전의 특수화가 사용된다. 그렇지 않으면 일반적인 접근이 사용되며 U 가 NullType 이 될 때까지 재귀가 계속된다.


그러한 클래스들을 구현하기 위해, 나는 TypeList 클래스에다가 단지 두 줄의 코드만을 추가할 수 있다 :



그렇다!


나는 분명히 함수의 형식을 위한 다른 템플릿 매개변수들을 추가할 수 있지만, 우리가 이미 가지고 있는 정보들을 사용해 같은 결과를 획득하기 위해서 템플릿 메타프로그래밍을 사용하는 방법을 보여주고 싶었다. 이 "형식 변환"은 다른 목적으로 사용될 수도 있다.


같은 방식으로  나는 함수에 대한 사용 정보를 자동으로 출력해 주는 기능을 추가할 수 있다. 하나의 가능한 구현이 아래에 나와 있다 :



나는 여기에서 같은 접근법을 사용했다 : Tail 형식에 대한 재귀 호출. Type2String 은 단지 type 을 위한 문자열 이름을 반환하는 단순한 클래스일 뿐이다. 나는 그것을 여기에서 보여 주지는 않을 것이다. 왜냐하면 그것의 구현은 매우 단순하지만, 당신은 첨부한 코드에서 전부 볼 수 있기 때문이다.


요약을 위해 TypeList 클래스에 대한 전체 코드를 다음과 같이 제공한다 :



개선 사항.


반환값을 사용하면 어떻게 해야 할까? 그것은 당신에게 달려 있다. 나는 단지 그것을 디버그 콘솔 출력에다가 찍기만 한다. 그래서 사용자들은 실행된 명령의 결과를 볼 수 있을 것이다. If you try to use the output value, recall that it might be void.


Another thing you can add is a compile-time check for function parameters to be of a fundamental class, if you would like to constrain them.


결론.


함수를 위한 형식 추출을 제공함으로써, 이제 나는 다양한 요구를 위해 함수 형식 매개변수들을 점검하기( inspect ) 위한 유연한 메커니즘을 가지게 되었다.


이 기사에서 제공되는 코드는 ( 많은 게임들이 그런 것처럼 ) 디버그 콘솔을 생성하기 위해 적용되었다. 그리고 여기에서 코드를 찾아 볼 수 있다 : http://dconsole.googlecode.com




Sergii Biloshytski 는 임베디드 프로그래머로서 경력을 시작했지만, 길을 바꿔서 게임 프로그래머가 되었다. Ubisoft 에서 그는 Blazing Angles, From DUST, Assassins Creed 등과 같은 타이틀들과 관련한 작업을 해 왔다.

+ Recent posts