2015년 6월 8일 월요일

[C++11] tuple


[C++11] tuple C++11 / Programming
2014.04.30. 17:33 수정 삭제
전용뷰어 보기
함수를 만들때 간혹 2개 이상의 값을 한번에 리턴해야 할 경우가 있습니다. 이럴때 기존에는 구조체를 정의하고 구조체 변수를 하나 선언한 후 값을 넣고 리턴해야 했습니다. 단지 리턴값을 여러개 받고 싶을 뿐인데 구조체를 정의해야 한다는 것이 좀 부담이었죠.

C++11에서는 이런 불편함을 없애기 위해 tuple을 도입했습니다. tuple을 사용하면 2개 이상의 변수를 한번에 묶어서 리턴할 수 있습니다. tuple을 사용하기 위해서는 #include <tuple>을 추가해야 합니다. tuple은 다음과 같이 생성하고 사용할 수 있습니다.

// int, int, string 타입으로 tuple 생성
std::tuple<int, int, std::string> items = std::make_tuple(1, 2, "test");
// std::tuple<int, int, std::string> items(1, 2, "test"); // 이렇게 해도 가능합니다

std::cout << std::get<0>(items) << std::endl; // 첫번째 값을 출력합니다. 값은 1입니다.
std::cout << std::get<1>(items) << std::endl; // 두번째 값을 출력합니다. 값은 2입니다.
std::cout << std::get<2>(items) << std::endl; // 세번째 값을 출력합니다. 값은 test입니다.

std::get<0>(items) = 5; // 첫번째 값을 5로 변경합니다.
빈 값으로 tuple을 생성하고 나중에 값을 저장할 수도 있습니다.

tuple의 데이터 개수는 tuple_size 함수를 이용해 알아낼수 있습니다. 위와 같이 tuple을 만들었다면 count가 3이 될것입니다.
auto count = std::tuple_size<decltype(items)>::value;


tie 함수를 이용하면 tuple에 데이터를 하나씩 넣지 않고 여러개를 한번에 넣거나 읽어올수도 있습니다.
std::tuple<int, int, std::string> items(1, 2, "test");

int a, b;
std::string str;
std::tie(a, b, str) = items; // a는1, b는 2, str은 test가 들어갑니다.


모두 넣지 않고 일부만 넣을수도 있습니다.
std::tie(a, std::ignore, str) = items; // a는1, str은 test가 들어갑니다.


tuple을 합치려면 tuple_cat 함수를 사용하면 됩니다.
std::tuple<int, int, std::string> items1(1, 2, "test1");
std::tuple<int, int, std::string> items2(3, 4, "test2");

auto tupleSet = std::tuple_cat(items1, items2); // tupleSet은 1, 2, test1, 3, 4, test2 가 됩니다.

[C++11] 퍼펙트 포워딩

함수의 인자로 받은 값을 변경했을때 함수의 외부로 적용되게 하려면 참조 또는 포인터로 받아야 합니다. 여기서는 참조로만 받는다고 가정합니다.
void increment(int &i)
{
i++;
}

int main()
{
int a = 1;
increment(a);
// increment(1); // 에러가 발생합니다.

return 0;
}
increment(1)을 성공하게 하려면 함수 오버로딩을 사용하면 됩니다. 하지만 귀찮죠?

C++11의 R-Value 레퍼런스를 사용하면 함수 오버로딩을 사용하지 않아도 해결할수 있습니다.
void increment(int &&i)
{
i++;
}

하지만 컴파일해보면 이번엔 increment(a)에서 에러가 발생합니다. R-Value 레퍼런스를 사용하여 인자를 넘기려면 메모리를 이동해야 하는데 이를 위해 std::move() 함수를 사용하면 됩니다.
 increment(std::move(a)); // 에러가 발생하지 않습니다

[Cocos2d-x] 화면에 구성 요소 추가

프로젝트를 생성하면 여러 파일들이 있는 것을 볼수 있습니다. 저는 한 장면에 여러 구성 요소들을 넣으면서 공부할 생각이라 화면을 관장하는 HelloWorldScene.h와 HelloWorldScene.cpp를 주로 수정할 것입니다.
멤버 변수는 HelloWorldScene.h에 넣고 구성 요소의 생성 및 등록은 HelloWorldScene.cpp의 init() 함수에서 하면 됩니다.

Cocos2d-x의 구성 요소는 모두 ClassName::create() 함수로 생성할수 있습니다. 반환값은 아시다시피 생성한 클래스의 인스턴스입니다. 나중에 소멸은 자동으로 되니 신경쓰지 않아도 됩니다.
이 제 생성을 했으니 등록을 해야겠죠? 등록은 addChild 함수를 이용하면 됩니다. 어디에다 하냐면 장면에다 해야겠죠. HelloWorldScene 클래스가 장면이니 this->addChild(instance)를 호출하면 됩니다. 이렇게 하면 장면에 추가한 구성 요소가 보입니다.

[Cocos2d-x] 좌표계와 앵커포인트

좌표계
iOS, 안드로이드와 cocos2d-x는 좌표계가 다릅니다. iOS, 안드로이드의 경우는 좌측 상단이 0,0 이고 cocos2d-x는 좌측 하단이 0,0 입니다. 따라서 iOS, 안드로이드는 오른쪽으로 가면 x값이 증가하고 아래로 내려가면 y값이 증가하는 반면 cocos2d-x는 오른쪽으로 가면 x값이 증가하고 위로 올라가면 y값이 증가합니다.

앵커포인트
메 뉴나 스프라이트 등의 구성 요소가 좌표에 지정될때 구성 요소의 어느 부분을 기준으로 좌표에 지정할지 결정하는 값입니다. 예를 들어 (100, 100)의 좌표에 스프라이트를 넣을때 스프라이트의 좌측 하단을 지정된 좌표로 할지 스프라이트의 가운데를 지정된 좌표로 할지 정할수 있습니다.
앵커포인트의 범위는 (0.0, 0.0) 에서 (1.0, 1.0) 이며 (0.0, 0.0)이면 좌측 하단이고 (0.5, 0.5)이면 가운데 입니다.

[C++11] 이동 시맨틱

기존 C++의 성능(주로 STL)을 개선하기 위해 도입된 개념입니다. vector를 예로 들자면 기존 C++의 경우 계속 항목을 추가하다 보면 공간이 부족해지고 공간을 늘리기 위해 다음 절차를 수행합니다.
1. 이전보다 큰 메모리 공간을 할당한다.
2. 기존 영역의 항목을 모두 복사한다.
3. 복사가 끝나면 기존 영역의 메모리를 해제한다.
깊은 복사가 일어나기 때문에 매우 비효율적입니다.

이 런 비효율을 극복하기 위해 C++11에서는 전에 언급한 R-Value 레퍼런스를 이용해 이동 시맨틱을 구현했습니다. vector 내부의 구현을 보면 기존에 메모리를 복사하는 부분이 메모리를 이동하는 것으로 바뀐것을 알수 있습니다. 따라서 vector 공간이 늘어나도 수행 시간이 거의 소요되지 않습니다. vector 뿐만 아니라 기타 다른 STL도 이런 식으로 구현이 변경되었습니다. 전체적으로 성능이 많이 향상되었겠죠?
std::vector<int> vec1;
std::vector<int> vec2;

vec1.push_back(1);
vec1.push_back(2);

vec2 = std::move(vec1); // 이동 시맨틱을 지원하기 위해 추가된 함수입니다. 객체를 
    // 복사하지 않고 이동합니다.

PS. 임베디드 리눅스에서 C프로그래밍을 주업무로 하는 저로써는 이런거 구현안하고 메모리 포인터만 넘겨주는게 훨씬 편할것 같습니다. 어차피 객체 소유권 이전도 내부적으로 보면 메모리 포인터 변경이니까요. 소유권 이전하면 기존 객체는 사용 못하고... 참조와 템플릿을 주로 사용하는 C++ 입장에서 보면 저런 개념이 필요할것 같긴 합니다.
조작을 위해 기존 객체를 새로 복사해서 쓰는 경우가 많은데 기존 객체도 유지되고 동일한 새로운 객체도 생성되는데 수행 시간이 0이라면? +_+ (불가능하겠죠?)

[C++11] R-Value 레퍼런스


[C++11] R-Value 레퍼런스 C++11 / Programming
2014.04.29. 17:16 수정 삭제
전용뷰어 보기
C++11에서는 R-Value 라는 용어가 새로 등장했습니다. 이와 함께 L-Value 라는 용어도 등장했는데 L-Value는 사실 기존 C++ 부터 사용하던 레퍼런스의 다른 말입니다. R-Value 레퍼런스와 기존 레퍼런스를 구분하기 위해 L-Value 라는 용어를 사용한 것이죠.
그렇다면 L-Value와 R-Value가 뭘까요? 그냥 보면 왼쪽에 있는 값과 오른쪽에 있는 값 같습니다. 저도 처음에는 그렇게 생각했습니다.

정확하게 정의하자면 L-Value는 왼쪽 오른쪽 어디에도 올수 있는 값이고 R-Value는 오른쪽에만 올수 있는 값입니다. 그리고 L-Value는 참조가 가능하고 R-Value는 참조가 불가능합니다.
int a = 1; // a는 L-Value이고 1은 R-Value 입니다.
int b = a; // b는 L-Value입니다.
// 1 = a; // 에러가 발생합니다. 1은 R-Value이므로 왼쪽에 올수 없습니다.

대충 이해가 가시나요? 값을 참조하는 경우를 살펴봅시다.
int a = 1;
int *b = &a; // a는 L-Value이므로 참조가 가능합니다.
// int c = &1; // 1은 R-Value이므로 참조가 불가능합니다.


그런데 C++11에서 R-Value를 참조 가능하게 만들었습니다. 이동 시맨틱과 퍼펙트 포워딩을 위해 적용한 개념이라고 합니다. 이동 시맨틱과 퍼펙트 포워딩은 추후 정리하겠습니다.
그럼 R-Value를 어떻게 참조할까요? R-Value를 참조하기 위해 약간 다른 참조 연산을 사용합니다.

int &&a = 1; // &&가 R-Value 참조 연산자이고 에러가 발생하지 않습니다.
L-Value 처럼 아래와 같은 코드도 가능합니다.
int a = 1;
int &&b = 2;
b = a; // b의 값이 1로 바뀝니다.


[인도베다수학] 뺄셈

두 자릿수의 뺄셈
뺄셈도 덧셈과 마찬가지로 일의 자리의 수를 0으로 만들면 암산이 쉬워집니다.
35 - 19 = 35 - 20 + 1 = 15 + 1 = 16

세 자릿수의 뺄셈
빼는 수의 십의 자리와 일의 자리의 수를 0으로 만들면 됩니다.
247 - 189 = 247 - 200 + 11 = 47 + 11 = 58