주의 : 공부하면서 정리한 내용이기 때문에 오류가 있을 수 있습니다.


실습 과제 3-7* :


다음 구성물들의 의미론은 무엇일까?



독자의 답에 도달한 단계들을 보이고, 독자의 가정들을 확인하는 테스트 코드를 작성하라. 라이브러리의 행동이 독자의 분석과 일치하는가? 그렇지 않다면, 실패한 테스트들을 분석해서 실제의 표현식들의 의미론을 파악해 볼 것. 그리고 독자의 가정들이 왜 달랐는지, 또 독자가 생각하기에 어떤 행동이 더 합당한지와 그 이유를 말해 보라.




풀이 :


이 역시 쉬운 듯하면서 절대 쉽지 않은 문제이다. 지금까지 배웠던 람다 표현식에 대해서 얼마나 이해하고 있는지를 검사하는 문제이다. 게다가 근거까지 명확하게 대야 하기 때문에 별표가 안 붙을 수 없다.




typedef mpl::lambda< mpl::lambda< _1 > >::type t1;


mpl::lambda 메타 함수는 자리표 표현식을 위한 메타 함수 클래스를 만들어 내는 역할을 하고 있다고 알고 있었다. 그리고 결국에는 메타 함수 자체를 반환한다고 생각했다. 


_1 은 arg< 1 > 이라는 메타 함수 클래스이고 _1 로 평가되기 때문에 첫 번째 lambda 에서 _1 을 반환하고, 두 번째 lambda 에서 다시 _1 을 반환해 t1 은 _1 을 반환한다고 생각했다. 그래서 예상한 결과는 다음과 같았다.



그러나 이는 전혀 평가되지 않는 문장을 만들어 냈다. 아래 에러와 같이 그냥 문자열처럼 해석해 버린다.


error C2338: ( boost::is_same< t1::type, _1 >::value )


생각해 보니 t1 이라는 것 자체가 어떤 메타 함수 클래스를 나타내기는 하지만, 실제로 어떤 형식을 넣어 특수화하지 않으면 아무런 의미도 없겠다는 생각이 들었다. 그래서 다음과 같이 고쳐 봤다. 자리표 표현식으로부터 메타 함수 클래스를 생성해 주는 것이기 때문에 apply 를 호출해 보았다.



위의 코드 역시 평가되지 않았다.


결국 기본부터 다시 생각해 보기로 했다. 


mpl::lambda 의 의미부터 다시 파악해 볼 필요가 있다. mpl::lambda 는 자리표 표현식이나 메타 함수 클래스를 받아서 메타 함수 클래스를 만들어 준다. 만약 boost::add_pointer 라는 메타 함수를 위한 메타 함수 클래스를 만들기 위해서는 다음과 같이 해야 한다.



자 그러면 아무런 메타 함수나 넣어서 호출할 수 있게 해주는 메타 함수를 만들기 위해서는 어떻게 해야 할까? 다음과 같은 형태를 생각할 수 있다.



??? 자리에는 람다 표현식( 자리표 표현식이나 메타 함수 클래스 )이 들어 와야 할 것이다. 메타 함수 자체는 일급 메타 자료가 아니기 때문이다. 그런데 공급될 것이 메타 함수인지 람다 표현식인지 알 수 없기 때문에, 모든 가능성을 생각하면 무엇이 들어 오든 람다 표현식으로 만들어 줄 필요가 있다. 그러므로 다음과 같이 표현할 수 있다.



위 코드는 메타 함수나 람다 표현식을 메타 함수 클래스로 만들어 주는 메타 함수 클래스를 정의한 것이라고 할 수 있다. 이를 실제로 사용해 보도록 하겠다.





typedef mpl::apply< _1, mpl::plus< _1, _2 > >::type t2;


이것은 mpl::apply 에 의해 내부의 mpl::plus 를 평가한 결과를 외부의 mpl::apply 에 적용하는 메타 함수로 보인다. 일반적으로 mpl::plus 에 들어 오는 것은 수치 래퍼일 것이기 때문에 그것의 ::type 은 자기 자신을 반환할 것이다. 결국 이는 mpl::plus 와 동일한 결과를 낼 것이다.



one 과 two 를 mpl::plus 의 _1 과 _2 에 할당하고 나면 mpl::int_< 3 > 이라는 결과가 나올 것이고, 그것이 다시 mpl::apply 의 _1 에 들어 가서 그것을 평가하면 그 자신을 반환한다.




typedef mpl::apply< _1, std::vector< int > >::type t3;


일단 std::vector 가 메타 함수인지를 확인해 봐야 한다. 그런데 std::vector 는 type 이라는 내포된 형식을 가지고 있지 않았다. 그걸 봤을 때 메타 함수는 아니라고 생각했으며, 이를 인스턴스화하면 에러가 날 것이라 생각했다.


하지만 실제로 해 봤을 때 다음과 같은 코드가 유효했다.



그런데 책에 이미 내용이 나와 있었다. 


자리표에 대해 아직 이야기하지 않은 세부사항이 하나 있다. MPL 은 보통의 템플릿들을 메타 프로그램에 통합시키기 쉽게 만들기 위한 하나의 특별한 규칙을 사용한다. 어떤 규칙이냐 하면, 모든 자리표들이 실제 인수들로 치환된 후의 템플릿 특수화 X 에 내포된 ::type 이 존재하지 않는다면, 그 람다의 결과는 X 자체라는 것이다.

- C++ Template MetaProgramming, 3.5.3 람다와 비 메타함수 템플릿, 77 페이지.


역시 책만 읽고 "아 그렇구나" 하고 넘어가면 안 된다는 점을 다시 한 번 느끼게 됐다.




typedef mpl::apply< _1, std::vector< _1 > >::type t4;


이것은 어떤 형식을 받아 vector 를 만들어 주는 메타 함수이다.





typedef mpl::apply< mpl::lambda< _1 >, std::vector< int > >::type t5;


std::vector< int > 을 입력으로 mpl::lambda< _1 > 가 지정하는 메타함수를 실행해 주는 메타함수라고 생각했다. 하지만 실제 해 보니 t5 에는 apply 라는 메타 함수가 존재하지 않고 그냥 std::vector< int > 와 같았다.



좀 이해할 수 없는 결과였다. 그런데 가만히 생각해 보니, 내부에 있는 mpl::lambda< _1 > 이라는 것이 평가되면 그냥 _1 이 되기 때문에 결국에는 t3 와 같은 결과가 되는 것이었다. 




typedef mpl::apply< mpl::lambda< _1 >, std::vector< _1 > >::type t6;


이것 역시 내부의 mpl::lambda< _1 > 이 _1 로 평가되면서 t4 와 같은 결과를 낸다.





typedef mpl::apply< mpl::lambda< _1 >, mpl::plus< _1, _2 > >::type t7;


이것 역시 내부의 mpl::lambda< _1 > 이 _1 로 평가되면서 t2 와 같은 결과를 낸다.





typedef mpl::apply< _1, mpl::lambda< mpl::plus< _1, _2 > > >::type t8;


mpl::lambda< mpl::plus< _1, _2 > > 는 mpl::plus< _1, _2 > 로 평가되고, 이것은 mpl::apply 의 _1 에 그대로 들어 가기 때문에 최종적으로는 mpl::plus< _1, _2 > 만 남는다.





다른 사람의 해석도 살펴 보기 바란다.


http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?CPPTM_Answers_-_Exercise_3-7

+ Recent posts