블로그 이동합니다


c++ Template Metaprogramming Exercises 4-3 Metaprogramming

귀찮아서...boost 홈페이지 엑서사이즈 누가 올려놓은 걸로 대충 테스트 함.
물론 내용은 다 아니까..손구락이 아파서리

#include "stdafx.h"
#include <iostream>
#include <boost/mpl/eval_if.hpp>
#include <boost/type_traits/is_void.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/static_assert.hpp>
#include <boost/mpl/not_equal_to.hpp>
#include <boost/mpl/greater.hpp>
#include <boost/mpl/minus.hpp>
#include <boost/mpl/plus.hpp>
#include <boost/mpl/multiplies.hpp>

using namespace std;
namespace mpl = boost::mpl;
using namespace mpl::placeholders;
/*
template< typename N, typename Predicate >
struct next_if : mpl::if_<
typename mpl::apply< Predicate, N >::type,
typename mpl::next< N >::type,
N
> {};
*/
template< typename N, typename Predicate >
struct next_if : mpl::eval_if<
typename mpl::apply< Predicate, N >,
typename mpl::next< N >,
N> {};


/*
template< typename N1, typename N2 >
struct fomula : mpl::if_<
mpl::not_equal_to< N1, N2 >,
typename mpl::if_<
mpl::greater< N1, N2 >, 
typename mpl::minus< N1, N2 >::type,
N1
>::type,
typename mpl::plus<
N1, 
typename mpl::multiplies< N1, mpl::int_< 2 > >::type
>::type
>::type
{};
*/

template< typename N1, typename N2 >
struct fomula : mpl::eval_if<
mpl::not_equal_to< N1, N2 >,
typename mpl::eval_if<
mpl::greater< N1, N2 >, 
typename mpl::minus< N1, N2 >,
N1
>,
typename mpl::plus<
N1, 
typename mpl::multiplies< N1, mpl::int_< 2 > >
>
>
{};

template<typename Nullary>
struct make_class
{
template<typename>
struct apply : Nullary {};
};

typedef make_class<mpl::false_> never;
typedef make_class<mpl::true_> always;

int _tmain(int argc, _TCHAR* argv[])
{
typedef mpl::int_<1> one;
typedef next_if<one, never>::type still_one;
BOOST_STATIC_ASSERT((still_one::type::value == 1));
typedef next_if<one, always>::type two;
BOOST_STATIC_ASSERT((two::type::value == 2));
//int nextValue = next_if< mpl::int_< 4 >, mpl::equal_to< mpl::int_< 5 >, _ > >::type::value;
BOOST_STATIC_ASSERT((fomula<one, still_one>::type::value == 3));
BOOST_STATIC_ASSERT((fomula<one, two>::type::value == 1));
BOOST_STATIC_ASSERT((fomula<two, one>::type::value == 1));

char c[2] = {0,};
gets_s(c, 2);
return 0;
}

C++ Template Metaprogramming Exercise 4.2 Metaprogramming

boost::mpl::or_ 의 구현은 아래와 같이 되어있다. 책이 집에 있어서 c++ template guide 를 참고 할 수 없는데 이 책에 파라미터의 개수를 지정하고 그 개수만큼 받을 수있는 코드를 본 것 같은데...아쉽게도 당장 볼 수가 없어 아래 처럼 구현한다.

template< 
typename BOOST_MPL_AUX_NA_PARAM(T1)
, typename BOOST_MPL_AUX_NA_PARAM(T2)
, typename T3 = false_, typename T4 = false_, typename T5 = false_
>
struct or_

: aux::or_impl<
BOOST_MPL_AUX_NESTED_TYPE_WKND(T1)::value
, T2, T3, T4, T5
>

{
};
// 2013.05.28
// 아이고 회식에 결혼식에 부친상에 여러가지 문제로 한동안 뜸했네요
//소스코드 하이라이트가 안되서 많이 불편하지만...그냥 복사해서 붙여서 보시면 편합니다
그리고 그냥 답만 보는 것 보다는 몇시간, 몇일이 걸리더라도 자기가 직접 쳐보는 것이 훨씬 도움이 많이 됩니다.
저도 가끔 책을 볼 때 빨리 진도를 나가려는 욕심에 읽고 지나치고 문제를 풀지 않는 경우가 있는데, 그럼 하나도 남지 않아요.
공부는 
1. 입력
2. 이해(정리)
3. 표현(표출, 혹은 연습)
의 과정을 거쳐야 비로서 완성됩니다. 시사인 잡지에서 본 내용인데요, 너무 급하게 진도만 나가려고 1. 입력만 하면 하나도 남지
않아요. 책을 보고 잠깐 멈춰서 생각도 해보고(2. 이해, 정리) 내가 생각한 이론이 맞는지 코드로 쳐서 확인해보는 과정(3. 표현)을
거치고 나야 비로서 온전히 자신의 지식이 된다고 생각합니다. 물론 무척 어려운 일인데요, 많이 보는 것보다 한 곳을 오래 보는 것이 더 좋을 때가 있습니다. ( 세계여행자 완두콩 님이 하신 말씀이에요 ). 저도 유럽여행할 때 아내한테 끌려다니면서 많은 곳을 봐왔지만 기억에 남는 곳은 몇군데 없습니다. 가장 기억에 남는 곳은 두곳인데요.
첫번 째는 영국박물관에서 아내를 기다리다가( 전 박물관 그림이나 전시물은 별로라서요 ) 영국 할아버지가 저한테 말을 걸어 오더군요. 전 유럽여행 가기 전부터 밤마다 꿈을 꿨답니다. 영국에 가면 말이지 공원같은데 계신 할아버지랑 얘기를 하는거야. 분명 세계 1, 2차 대전을 겪은 사람이 있을거야. 그 이야기를 듣겠어!!!! 그런데 그 꿈이 영국박물관앞 광장 벤치에서 이루어 졌어요. 할아버지는 1차는 확실히 모르겠네요. 못알아 들은것인지...분명 two world war, I mean one world war, two world war 라고 하셨는데 그럼 다 겪으셨겠죠. 게다가 처음엔 cold war 라고 들었는데 몇번 듣다보니 korean war 였더군요. 저한테 한국사람을 많이 죽여서 미안하다고 하더군요. 제 입장에선 북한군을 죽인것인데 좋은거 아닌가, 왜 나한테 미안해 하지? 라는 생각이 들었는데...영국 할아버지 입장에서는 ( 한국인이 아닌 제 3자의 객관적 입장에서는 ) 그냥 다 같은 민족, 국민이였던 것이죠. 그래서 미안하다고 하신 것 같습니다. 전 많은 부분을 다시 생각하게 됬습니다. 우물안 개구리에서 우리나라의 지정학적, 정치적이나 군사적, 역사적 위치를 다양한 시각으로 바라보게 된 것 같습니다. 할아버지랑 얘기한 이후로요. 어렷을 때부터 반공이니 뭐니, 파이를 크게 키워서 나눠먹는게 좋다는 주입식 교육을 받았는데요...그게 다 사기였던 것이죠. 독재자나 국가를 멸망에 이끌었던 쓰레기들이..히틀러 같은 사람들이요. 이런 놈들이 하는 짓은 옛날이나 지금이나, 한국이나 똑같은 것 같아요. 국민을 분열시켜 정치적 이득을 취하는 것이죠. 
이게 교묘합니다. 5:5 로 나누면 안되고요, 9:1, 8:2, 정말 상황이 안되면 7:3 정도로 분열 시키면 다수가 소수를 핍박하면서 얻는 카타르시스랄까? 아무튼 이런 식으로 분열 시키더라고요. 유태인이 그렇게 당한 것이죠. 물론 역사적으로 보면 미움받을 짓을 하긴 했지만요. 한국도 똑같죠. 빨갱이라는 단어. 벌써 몇년인가요 50년은 된것 같은데, 아직도 써먹고 있네요.
유럽여행 다니면서 파업하는 것을 정말 많이 봤고요, communism, socialism 이라는 단어 정말 많이 보고 듣고 했네요.
커뮤니즘..정말 좋은 뜻처럼 느껴지잖아요? 대화를 통해 뭔가를 하겠다라는 그런 뜻같죠? communication 이 의사소통 이라는 뜻이니까요, 그런데요, 한국에서는 이걸 "공산주의" 라고 부르죠. 기분이 어때요? 뭔가 오싹하고 움츠러 들고 주위를 두리번거리게 되지 않나요? 이게 한국의 현실이에요. socialism 은 또 어떤가요? 좋은 뜻 같잖아요. 그런데 한국에서는 "사회주의" 라고 부릅니다. 또 다시 주위를 두리번 거리신다면 당신은 정말 심각하게 생각해 보셔야 되요. 내가 혹시 메트릭스에 살고 있지 않은지요. 메트릭스에서 벗어나려면 여행을 떠나세요. 공부도 하시고요.
여행은 이게 좋은 것 같아요. 내가 아닌 다른 사람의 입장, 혹은 내가 사는 곳이 아닌 다른 곳에서 바라볼 수 있게 되는 것. 그게 좋은것 같아요. 
X박이나 X혜는 개발자들을 무척이나 싫어하는 것 같습니다. 정통부도 없앴잖아요. 가장 만만하고 눈엣가시 같았을 까요? 개발자라하면 다 그런 것은 아니지만 빠른 정보를 접하게 되고( 물론 잘못된 정보를 빨리 접할수도 ) 공부도 많이 해야하고, 불합리함을 타개하려는 성향이 있잖아요? 아 이코드는 문제가 많아 좀 더 효율적으로 바꿔야겠어. 이런 것처럼요. 그래서 진보적인 성향의 분들이 많은 것 같은데..그래서 더 무시당하고 그런 것일까요?
이 글을 데브피아에도 올리고 있는데 혹시 또 강제삭제 당하려나 모르겠네요. 그럼 데브피아는 쓰레기인거죠.
작년부턴가..어느순간부터 일베 애들이 나타나기 시작했습니다. 일베 애들이 온 것인지, 신입 개발자 중에..현실을 비관하는..일베가 있는 것인지 일베 글들이 올라오더군요. 그래서 제가 데브피아 고충상담에 혹시 여기 일베가 장악했냐는 글 올리자마자 몇분만에 삭제 되더군요. 이게 현재 한국의 현실이라는 생각도 들고요. 뭐 그게 큰 잘못이라고 바로 강제삭제나 당할까요? 
데브피아에 4-2 exercise 가 빠져있다면 뻔하죠. 데브피아도 쓰레기다. 
아...아주 멀리 빠져버렸네요. 두번째로 좋았던 것은 나중에 하기로 하죠.

#include "stdafx.h"
#include <iostream>
#include <boost/mpl/eval_if.hpp>
#include <boost/type_traits/is_void.hpp>


using namespace std;
namespace mpl = boost::mpl;

//------------------------------------------------------------------------------------------------
template <typename T>
struct or_void : mpl::eval_if<boost::is_void<T>, 
mpl::false_, // or 연산이기 때문에 false 로 주어야 합니다. true 일경우 무조건, 정말 무조건 true 를 반환합니다.
mpl::eval_if<T, mpl::true_, mpl::false_> > {};

// 인자가 최소한 두개 이상이여야 해서 F1, F2 는 기본 인자를 주지 않습니다. 반대로 3, 4, 5 의 기본인자를 주지 않으면 무조건 다섯개의 인자를 무조건 줘야합니다.
// 마찬가지로 인자의 기본값이 false 인 경우는 위와 같습니다.
template <typename F1, typename F2, typename F3 = mpl::false_, typename F4 = mpl::false_, typename F5 = mpl::false_> 
struct logical_or : mpl::eval_if<F1, mpl::true_,
mpl::eval_if<F2, mpl::true_,
mpl::eval_if<or_void<F3>, mpl::true_,
mpl::eval_if<or_void<F4>, mpl::true_,
mpl::eval_if<or_void<F5>, mpl::true_, mpl::false_> > > > > {};
//------------------------------------------------------------------------------------------------
template <typename T>
struct and_void : mpl::eval_if<boost::is_void<T>, 
mpl::true_, // or_void 와 반대의 이유
mpl::eval_if<T, mpl::true_, mpl::false_> > {};

// 인자가 최소한 두개 이상이여야 해서 F1, F2 는 기본 인자를 주지 않습니다. 반대로 3, 4, 5 의 기본인자를 주지 않으면 무조건 다섯개의 인자를 무조건 줘야합니다.
// 마찬가지로 인자의 기본값이 true 인 경우는 위와 같습니다. 못미더우면 false 로 테스트해보세용용죽겠지
template <typename F1, typename F2, typename F3 = mpl::true_, typename F4 = mpl::true_, typename F5 = mpl::true_> 
struct logical_and : mpl::eval_if<F1, 
mpl::eval_if<F2,
mpl::eval_if<F3,
mpl::eval_if<F4,
mpl::eval_if<F5, mpl::true_, mpl::false_>, 
mpl::false_>,
mpl::false_>,
mpl::false_>,
mpl::false_> {};
//------------------------------------------------------------------------------------------------


int _tmain(int argc, _TCHAR* argv[])
{
/*-------------------------------------------------------------------------------------------------
4-2. Extend the implementation of logical_or and logical_and metafunctions from exercise 4-1 to accept
up to five arguments.
-------------------------------------------------------------------------------------------------*/
cout << "logical_or 1 :  " << logical_or<mpl::true_, mpl::true_>::value << endl;
cout << "logical_or 2 :  " << logical_or<mpl::false_, mpl::true_, mpl::false_>::value << endl;
cout << "logical_or 2 :  " << logical_or<mpl::false_, mpl::false_, mpl::false_, mpl::false_, mpl::true_>::value << endl;
cout << "logical_or 2 :  " << logical_or<mpl::false_, mpl::false_, mpl::false_>::value << endl;
cout << "logical_and 1 :  " << logical_and<mpl::true_, mpl::true_, mpl::true_, mpl::true_, mpl::true_>::value << endl;
cout << "logical_and 2 :  " << logical_and<mpl::false_, mpl::true_, mpl::true_, mpl::true_, mpl::true_>::value << endl;
cout << "logical_and 2 :  " << logical_and<mpl::true_, mpl::true_, mpl::true_, mpl::false_, mpl::false_>::value << endl;
cout << "logical_and 2 :  " << logical_and<mpl::true_, mpl::true_, mpl::true_>::value << endl;


//cout << "" << << endl;
//BOOST_STATIC_ASSERT(( ));
//quantity<float, velocity> bugs = m2 * a;

char c[2] = {0,};
gets_s(c, 2);
return 0;
}


c++ metaprogramming Exercise 4-1. Implement binary metafunctions called logical_or .. Metaprogramming

#include "stdafx.h" 
#include <iostream>
#include <boost/mpl/eval_if.hpp>


using namespace std;
namespace mpl = boost::mpl;

template <typename F1, typename F2>
struct logical_or : mpl::eval_if<F1, mpl::true_, mpl::eval_if<F2, mpl::true_, mpl::false_> > {};

template <typename F1, typename F2>
struct logical_and : mpl::eval_if<F1, mpl::eval_if<F2, mpl::true_, mpl::false_>, mpl::false_> {};

int _tmain(int argc, _TCHAR* argv[])
{
/*-------------------------------------------------------------------------------------------------
4-1. Implement binary metafunctions called logical_or and logical_and that model the behavior of
mpl::or_ and mpl::and_, correspondingly. Use tests from exercise 4-0 to verify your implementation.
-------------------------------------------------------------------------------------------------*/

cout << "logical_or 1 : " << logical_or<mpl::true_, mpl::true_>::value << endl;
cout << "logical_or 2 : " << logical_or<mpl::false_, mpl::true_>::value << endl;
cout << "logical_or 2 : " << logical_or<mpl::false_, mpl::false_>::value << endl;

cout << "logical_and 1 : " << logical_and<mpl::true_, mpl::true_>::value << endl;
cout << "logical_and 2 : " << logical_and<mpl::false_, mpl::true_>::value << endl;
cout << "logical_and 2 : " << logical_and<mpl::false_, mpl::false_>::value << endl;
//cout << "" << << endl;

//BOOST_STATIC_ASSERT(( ));

//quantity<float, velocity> bugs = m2 * a;

char c[2] = {0,};
gets_s(c, 2);
return 0;
}

뭐 간단하게 구현됬다. 단지 좀 더 깊게 파보아야 할 내용은 왜 if_ 대신에 eval_if 를 사용했냐는 점이다. 
http://www.boost.org/doc/libs/1_53_0/libs/mpl/doc/refmanual/refmanual_toc.html 에서 보면 if_ 는 T 자체를 인자로 사용
하는데 eval_if 는 metafuntion 을 사용한다. 물론 내부적으로 if_ 를 사용해 type 을 선언한다. 자세한 내용은 위 메뉴얼을 보면된다.

그리고 다른 방법으로도 구현이 가능한데 C++ template guide 라는 책에 보면 ifthenelse 클래스가 있다. 특스화를 통해서도
logical_or, and 를 구현할 수 있다. 실제 구현은 생략하고 참고용으로 ifthenelse 를 아래 첨부한다.

// ifthenelse
template <bool C, typename Ta, typename Tb>
class IfThenElse;

//부분특수화 : true 이면 두번째 인자 사용
template <typename Ta, typename Tb>
class IfThenElse<true, Ta, Tb> {
public:
typedef Tb ResultT;
};

// 부분특수화 : false 이면 세번째 인자 사용
template <typename Ta, typename Tb>
class IfThenElse<false, Ta, Tb> {
public:
typedef Ta ResultT;
};

       

c++ metaprogramming Exercise 4-0. Write tests for mpl::or_ and mpl::and_ metafunctions ... Metaprogramming

// 4-0. Write tests for mpl::or_ and mpl::and_ metafunctions that use their short-circuit behavior. 
cout.setf(std::ios::boolalpha);
typedef mpl::lambda< mpl::or_<mpl::int_<0>, mpl::int_<1> > > or1;
cout << "or1 : " << or1::type::value << endl;

typedef mpl::lambda< mpl::or_<mpl::int_<0>, mpl::int_<0> > > or2;
cout << "or2 : " << or2::type::value << endl;

typedef mpl::lambda< mpl::or_<mpl::int_<1>, mpl::int_<0> > > or3;
cout << "or3 : " << or3::type::value << endl;



뭐 아주 간단한 예제이다. short-circuit behavior  에 대해 생각해보라는 뜻인데, or 연산이기 때문에 첫번째 인자가 참이라면 뒤에 인자를 검사해볼 필요가 없다. or1 은 두번째 인자까지 검사하고 or2 도 두번째 인자까지 검사하지만 or3 은 첫번재 인자가 참이므로 검사를 마친다.

c++ metaprogramming Exercise 3-7 Metaprogramming

3-7. What do you think would be the semantics of the following constructs:

typedef mpl::lambda<mpl::lambda<_> >::type t1;
typedef mpl::apply<_1, mpl::plus<_1, _2> >::type t2;
typedef mpl::apply<_1, std::vector<int> >::type t3;
typedef mpl::apply<_1, std::vector<_1> >::type t4;
typedef mpl::apply<mpl::lambda<_1>, std::vector<int> >::type t5;
typedef mpl::apply<mpl::lambda<_1>, std::vector<_1> >::type t6;
typedef mpl::apply<mpl::lambda<_1>, mpl::plus<_1, _2> >::type t7;
typedef mpl::apply<_1, mpl::lambda<mpl::plus<_1, _2> > >::type t8;


Show the steps used to arrive at your answers and write tests verifying your assumptions. Di the library behavior match your reasoning? If not analyze the failed tests to discover the actual expression semantics. Explain why your assumptions were deffent, what behavior you find more coherent, and why.



typedef mpl::lambda<mpl::lambda<_> >::type t1; 
// mpl::lambda 는 ::type 을 만들어주는 래퍼이다. 그래서 T 를 받는 type의 type
typedef mpl::lambda<mpl::lambda<int> >::type t11;
BOOST_STATIC_ASSERT((boost::is_same<mpl::lambda<int>::type, t11::type>::value));
BOOST_STATIC_ASSERT((boost::is_same<mpl::lambda<int>, t11>::value));
// mpl::lambda<_> 에는 metafunction 이 아닌 것들이 들어갈 수 있다.
/*
t113는 mpl::lambda 이다. boost::add_pointer<int> 는 t113::type 이다.
결국 실제로 int* 는 typename t113::type::type 인 것이다.
그래서 type depth 에 따라서 매칭되는 값이 달라지는 것이다. meta function의 parameter 를 넘길 때는 정확한 nested type, 즉 ::type 을 넘겨주어야한다.
*/

typedef mpl::lambda< boost::add_pointer<int> >::type::type t111;
BOOST_STATIC_ASSERT((boost::is_same<int*, t111>::value));

typedef mpl::lambda< boost::add_pointer<int> >::type t112;
BOOST_STATIC_ASSERT((boost::is_same<int*, t112::type>::value));

typedef mpl::lambda< boost::add_pointer<int> > t113;
BOOST_STATIC_ASSERT((boost::is_same<int*, typename t113::type::type>::value));
//---------------------------------------------------------------------------------
/*
apply<F, Args> 는 metafunction F 에 Arg 를 엮어주는(invoke) 기능을 수행한다.
typedef mpl::apply<_1, mpl::plus<_1, _2> >::type t2; t2 자체로는 어떠한 의미도 갖지 않는다. 인스턴스화 되지 않았기 때문에
*/

using namespace mpl;
typedef mpl::apply<_1, mpl::plus<_1, _2> >::type t2;
typedef mpl::apply<_1, mpl::plus<int_<1>::type, int_<2>::type> >::type t2result; //placeholder 와 함께 쓰이기 때문에 인자 생략 가능
cout << "t2result::value 1 : " << t2result::value << endl;
//cout << "t2result::type" << t2result << endl; // error
cout << "t2result::value 2 : " << mpl::apply<_1, mpl::plus<int_<1>::type, int_<2>::type> >::type::value << endl;
cout << "t2result::value 3 : " << mpl::apply<_1, mpl::plus<int_<1>, int_<2> > >::type::value << endl; // plus<arg, arg> 는 ::type 이 없다면 그 자체를 인식한다. 물론 int_<T> 는 ::type 을 갖는다.

typedef int_<1> one;
cout << "t2result::value 4 : " << mpl::apply<t2, one, int_<2> >::type::value << endl;
//cout << "t2result::value 5 : " << t2::value << endl; // t2 는 실체가 없다. not instantiated but just a template
//---------------------------------------------------------------------------------
/*
After all of the placeholder have been replaced with actual arguments, if the resulting template specialization X dosen't have a nested ::type, the result of lambda is just X itself.
std::vector<int> 는 nested::type 이 없기 때문에 인자 추론시에 itself, std::vector<int> 가 된다. std::vector<int> 는 다시 apply 에 의해 _1 에 invoke 된다.
*/

typedef mpl::apply<_1, std::vector<int> > t3;
t3::type vecInstantiated;
vecInstantiated.push_back(1);
//---------------------------------------------------------------------------------
// 위와 흡사하다. 다만 parameter fundamental type 이 가변적이라는 것이다.
typedef mpl::apply<_1, std::vector<_1> >::type t4;
typedef mpl::apply<t4, double> dvectorType;
dvectorType::type vecDouble;
vecDouble.push_back(0.1);
//---------------------------------------------------------------------------------
typedef mpl::apply<mpl::lambda<_1>, std::vector<int> >::type t5;
t5 vect5;
vect5.push_back(0);
//---------------------------------------------------------------------------------
/*
아래 문법들의 동작은 거의 비슷하다. 단지 차이라면 placeholder / lambda 의 차이일 뿐이다. 여기서 정확하게 짚고 가자.
왜 아래 세문장을 생각해 보라고 했을까? lambda 와 placeholder 의 동작원리의 차이점을 반드시 기억하자.
lambda 의 경우에는 parameter 가 placeholder 일 경우 metafunction 을 반환하고 identical argument 일 경우 그 자체를 반환한다.
그리고 lambda 는 내부적으로 bind 에 의해 arguments 가 invoke 된다. 그리고 nested ::type 을 갖는다. 아래 구현이다.
typedef protect< bind<
quoten<X,tag>
, lambda<a1,tag>::type,... lambda<an,tag>::type
> > f;

placeholder 의 경우에는 apply_wrap 에 의해 invoke 된다는 차이점이 있다.
*/

typedef mpl::apply<mpl::lambda<_1>, std::vector<_1> >::type t6;
//---------------------------------------------------------------------------------
typedef mpl::apply<mpl::lambda<_1>, mpl::plus<_1, _2> >::type t7;
//---------------------------------------------------------------------------------
typedef mpl::apply<_1, mpl::lambda<mpl::plus<_1, _2> > >::type t8;
//---------------------------------------------------------------------------------

c++ metaprogramming Exercise 3-6 Metaprogramming

3-6. Build a lambda expression that has functionality equivalent to twice. Hint: mpl::apply is a metafunction!

기존의 twice 와 같은 기능을 람다표현식을 이용해 구현하란다... 그래서 아래처럼 구현해봤다. 

template <typename T>
void LambdaTwice() {
typedef mpl::lambda<boost::add_pointer<_> > add_pointer_exp;
typedef mpl::apply<add_pointer_exp::type, typename mpl::apply<add_pointer_exp::type, T>::type > lambdatwice; // look like twice
BOOST_STATIC_ASSERT((boost::is_same<T**, lambdatwice::type>::value));
};

LambdaTwice<int>();
LambdaTwice<char>();
LambdaTwice<double>();

뭐 딱히 어려울 것은 없는 문제. 번역이 문제임 ㅠㅠ

c++ metaprogramming Exercise 3-5 Metaprogramming

c++ metaprogramming Exercise 3-5

There's still a problem with the dimensional analysis code in section 3.1 Hint: What happens when you do :
f = f + m * a;
Repair this example using techniques shown in this chapter.

// Meta.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/int.hpp>
#include <boost/static_assert.hpp>
#include <boost/mpl/plus.hpp>
#include <boost/mpl/minus.hpp>
#include <boost/mpl/multiplies.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/type_traits.hpp>

using namespace std;
namespace mpl = boost::mpl;
using namespace mpl::placeholders;

template <typename T, typename D>
struct quantity {
explicit quantity(T x) : m_value(x) {}
template <typename OtherDimensions>
quantity(quantity<T, OtherDimensions> const& rhs) 
: m_value(rhs.value()) {
BOOST_STATIC_ASSERT((
mpl::equal<D, OtherDimensions>::type::value
));
}
T value() const { return m_value; }
private:
T m_value;
};


template< class T, class D1, class D2 >
quantity< T, D1 > operator+( quantity< T, D1 > x, quantity< T, D2 > y )
{
BOOST_STATIC_ASSERT( ( mpl::equal< D1, D2 >::value ) );

return quantity< T, D1 >( x.value() + y.value() );
}


template< class T, class D1, class D2 >
quantity< T, D1 > operator-( quantity< T, D1 > x, quantity< T, D2 > y )
{
BOOST_STATIC_ASSERT( ( mpl::equal< D1, D2 >::value ) );

return quantity< T, D1 >( x.value() - y.value() );
}


template <typename T, typename D1, typename D2>
quantity<T, typename mpl::transform<D1, D2, mpl::plus<_1, _2> >::type>
operator*(quantity<T, D1> x, quantity<T, D2> y) {
typedef typename mpl::transform<D1, D2, mpl::plus<_1, _2> > dim;
return quantity<T, dim::type>(x.value() * y.value());
}

template <typename T, typename D1, typename D2>
quantity<T, typename mpl::transform<D1, D2, mpl::minus<_1, _2> >::type>
operator/(quantity<T, D1> x, quantity<T, D2> y) {
typedef typename mpl::transform<D1, D2, mpl::minus<_1, _2>> dim;
return quantity<T, dim::type>(x.value() / y.value());
}


int _tmain(int argc, _TCHAR* argv[])
{
typedef mpl::vector_c<int, 1, 0, 0, 0, 0, 0, 0> mass;
typedef mpl::vector_c<int, 0, 1, 0, 0, 0, 0, 0> length;
typedef mpl::vector_c<int, 0, 0, 1, 0, 0, 0, 0> time;
typedef mpl::vector_c<int, 0, 0, 0, 1, 0, 0, 0> charge;
typedef mpl::vector_c<int, 0, 0, 0, 0, 1, 0, 0> temperature;
typedef mpl::vector_c<int, 0, 0, 0, 0, 0, 1, 0> intensity;
typedef mpl::vector_c<int, 0, 0, 0, 0, 0, 0, 1> amount_of_substance;
typedef mpl::vector_c< int, 0, 1, -1, 0, 0, 0, 0 > velocity;
typedef mpl::vector_c< int, 0, 1, -2, 0, 0, 0, 0 > acceleration;
typedef mpl::vector_c< int, 1, 1, -1, 0, 0, 0, 0 > momentum;
typedef mpl::vector_c< int, 1, 1, -2, 0, 0, 0, 0 > force;

quantity<float, mass> m(5.0f);
quantity<float, acceleration> a(9.8f);
quantity<float, force> f(1.0f) ;
f = f + m * a;

cout << f.value() << endl;
//char c[2] = {0,};
//gets_s(c, 2);
return 0;
}

f = f + m * a; 코드가 에러가 난다. T 는 같지만 D 가 틀렸기 때문이다. 위와 같이 돌리면 잘 된다. 코드를 유심히 보면 최대한 ::type 을 늦게 사용했다. typedef 에 ::type 은 최대한 자제하고 실제 호출할 때 ::type 을 사용하도록 했다. 

vector_c 가 뭔지 잘 모르겠다면 아래 주소를 참조하면 된다. 별거없다. 연속된 integral sequance conatiner 일 뿐이다.


c++ metaprogramming Exercise 3-3, 3-4 Metaprogramming

template <typename T1, typename T2>
struct twice : mpl::apply<T1, typename mpl::apply<T1, T2>::type > {};

typedef twice<typename mpl::lambda<boost::add_pointer<_1> >::type, twice<boost::add_pointer<_1>, int>::type >::type result;
BOOST_STATIC_ASSERT((boost::is_same<int****, result>::value));

typedef twice<boost::add_pointer<_1>, int>::type p1;
typedef twice<boost::add_pointer<_1>, p1>::type p2;
BOOST_STATIC_ASSERT((boost::is_same<int****, p2>::value));
typedef mpl::lambda<boost::add_pointer<_1> > add_pointer_f;
typedef twice<twice<add_pointer_f::type, _1>, int> result2;
BOOST_STATIC_ASSERT((boost::is_same<int ****, result2::type>::value));

c++ metaprogramming Exercise 3-3, 3-4 에 관한 내용을 테스트 해봤다. 여기서 중요한점은 붉은 색으로 표시를 해두었다.

3.5.4 The Importance of Being lazy 에 나오는 내용이다.

struct add_pointer_f {
template <class T>
struct apply : boost::add_pointer<T> {};
}

tepedef mpl::vector<int, char*, double&> seq;
typedef mpl::tansform<seq, boost:add_pointer<_> > calc_ptr_seq;

위 내용의 핵심은 ::type  을 최대한 늦게 사용한다는 것이다. 지연사용이란 용어도 되겠다. nested type 사용을 늦출 수록 좋다는 것인데 실제로 해당 타입이 사용될 때 계산하도록 하는 것이다. 매우 중요한 것이다. 메타프로그래밍 컴파일 타임은 굉장이 오래걸리는데 사용하지도 않을 타입을 선언해 버리면 필요 없는 연산을 수행하게 되는 것이다. 이펙티브 c++ 에도 나오는 내용으로 변수선언은 늦출 수록 좋다는 것이다. 어쨋든 원문을 보자면

Note that calc_ptr_seq is a nullary metafunction, since it has transform's nested ::type. A C++ template is not instantiated until we actually "look inside it"" though. just naming calc_ptr_seq does not cause it to be evaluated, since we haven't accessed its ::type yet.

앞으로 ::type 을 늦출 수 있을 때 까지 늦추는 코딩 습관을 갖추어야 한다.

아..원서 보기가 짜증나네..

Exercise 3-2 Metaprogramming

Turn vector_c<int, 1, 2, 3> into a type sequence with elements (1, 4, 9) using transform.

Synopsis

template<      typename Sequence    , typename Op    , typename In = unspecified    >struct transform{    typedef unspecified type;};template<      typename Seq1    , typename Seq2    , typename BinaryOp    , typename In = unspecified    >struct transform{    typedef unspecified type;};

하다보니 transform 이 어떻게 동작되는지 궁금했다. 이놈과 placeholder 는 어떤 관계인지도.

#include <boost/static_assert.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/multiplies.hpp>

int _tmain(int argc, _TCHAR* argv[])
{
namespace mpl = boost::mpl;
using namespace mpl::placeholders;

typedef mpl::vector_c<int, 1, 2, 3> vc;
//typedef mpl::transform<vc, mpl::multiplies<_1, _1 > >::type result; // vc 에 vc 를 더한다. transform 의 퍼래머터가 multiplies 의 인자로 넘어간다. mpl::multiplies<vc, vc >
//typedef mpl::transform<vc, vc, mpl::multiplies<_1, _2 > >::type result; // 이것도 가능
//typedef mpl::transform<vc, vc, mpl::multiplies<_, _ > >::type result; 이것도 가능하지
//typedef mpl::transform<vc, vc, mpl::multiplies<_, _ > >::type result;
typedef mpl::multiplies<vc, vc>; // 이놈은 신기하게 안되다. ::type 이 없다. 
mpl::plus< mpl::integral_c<short,-1>, mpl::integral_c<long,10> >::type; // 근데 이놈은 된다.
mpl::plus< 1, 1 >; // 이놈도 역시 안된다.
typedef mpl::vector_c<int, 1,4,9> compare;

BOOST_STATIC_ASSERT(mpl::equal<result, compare>::type::value);

return 0;
}

왜 multiplies 에 placeholder 인자로 넘어간 것과 integral_c 의 인자로 넘어간 metadata 만 ::type 을 갖는가?
placeholder 에서 apply_wrapper 를 호출하고 args 를 넘긴다. 여기서 type 을 만들어 준다. 아 근데 왜 안되나...봤는데,
plus, multiplies 등의 인자(parameter 퍼래머터) 는 integral 타입이어야 한단다. 그래서 lambda 함수로 강제로 type 값을 줘도 소용이 없었다. placeholder 에서는 퍼래머터 값을 integral 로 변경하나 보다. 확인은 안했다.  졸리기 때문에.

typedef mpl::lambda<mpl::multiplies<mpl::lambda<vc>, mpl::lambda<vc> > >::type result1;

BOOST_STATIC_ASSERT(mpl::equal<result1, compare>::type::value); // 에러 

1 2 3 4 5 6 7 8 9 10