4월, 2017의 게시물 표시

[Effective Modern C++] Chapter 6. 람다 표현식 [항목 31~34]

Chapter 6. 람다 표현식 항목 31. 기본 갈무리 모드를 피하라. 참고로, 갈무리는 'capture'를 의미한다. (첨엔 어색했으나 나도 이제 완전히 이 번역에 익숙해져버렸다.)
기본 갈무리 모드 (default capture mode)는 '참조 갈무리 모드'와 '값 갈무리 모드'가 있다. 기본 갈무리 모드를 피해야 하는 이유는 크게 dangling reference 위험과 명시적이지 않다는 점(그래서 착각/실수 할 수 있다는 점) 때문이다.
일단, 참조 갈무리 모드에서 dangling reference 문제가 발생할 수 있다는 것은 너무 당연하고, 값 갈무리 모드에서도 pointer가 capture되는 경우에 그러한 문제가 발생 할 수 있다는 것은 당연하다.
사실 결정적인 문제는 명시적이지 않다는 점이다. 이로 인해 문제가 발생 할 수 있을 만한 상황을 정리하면 다음과 같다.
1. this pointer의 캡처
    - 숨겨져있던 this pointer가 캡처됨으로 인해 dangling pointer 등의 문제가 발생할 수 있다.
2. 람다표현식을 복붙(Copy&Paste) 할 경우, 실수할 가능성이 커진다.
    - 유용한 람다표현식을 그대로 다른 곳에서 쓸 경우가 있을 수 있다. 이 경우, capture list가 명시적이지 않아서 문제들이 발생할 소지가 있다.
3. 전역 변수나 static 같이 global scope를 가진 변수들 (즉, non-automatic variable들)의 값이 복사로서 capture될 것이라는 착각을 할 수 있다.
    - 오직 automatic variable들(즉, 지역변수들) 만 capture될 수 있는데, 기본 값 갈무리 모드를 사용하면, non-automatic variable들도 값 복사로서 capture될 것이라는 착각을 할 여지가 있다.

즉, 기본 갈무리 모드의 문제는 결국 '명시적이지 않다'는 것이다. 필요한 것들만 명시적으로 …

PyPy RPython Translation Toolchain

이미지
그 동안 PyPy를 단순히 JIT을 지원하는 빠른 python implementation 이라고만 알고 있었다.그런데 알고 보니 단순한 python implementation 이상의 의미를 갖는 상당히 멋있는 프로젝트였다.

PyPy는 2가지 파트로 나뉜다.
1. RPython 문법으로 작성된 Python Interpreter
2. RPython Translation Toolchain

1번의 python interpreter를 2번의 toolchain에 넣으면, JIT, garbage collector 등의 기능이 첨가된 C언어로 된 interpreter가 결과로서 나온다.
???!! 매우 놀라울 것이다. 2번의 toolchain은 Python 소스 코드나 syntax trees 에 대한 정보는 전혀 가지고 있지 않는다. 그런데 JIT과 garbage collector를 비롯한 기능들을 첨가해준다니?!
그래서 내가 멋있다고 한 것이다...


(출처 : https://www.toptal.com/python/why-are-there-so-many-pythons)

PyPy의 구조를 그림으로 보자면 위와 같다. (PyPy Interpreter가 1번, PyPy compiler가 2번에 해당한다.)


(출처 : http://rpython.readthedocs.io/en/latest/translation.html)

RPython translation toolchain의 실제 플로우를 그림으로 보자면 위와 같다. 기본적으로 RPython으로 작성된 interpreter 코드가 필요하고 추가적으로 low level hint들을 통해 세부적인 과정에 참여할 수 있다. 그리고 translation toolchain의 backend는 현재 C언어만 가능하지만, 설계 상 더 추가가 가능한 구조이다.
그리고 이러한 RPython Translation Toolchain을 이용하면 Python 뿐만 아니라 모든 언어들의 'JIT'ed Interpreter를 얻을 수 있다.
https://bit…

[Effective Modern C++] Chapter 5. 오른값 참조, 이동 의미론, 완벽 전달 [항목 23~30]

Chapter 5. 오른값 참조, 이동 의미론, 완벽 전달 항목 23. std::move와 std::forward를 숙지하라.#include <iostream> class Example { public: Example() = default; Example(const Example &) { std::cout << "copy constructor" << std::endl; } Example(Example &&) { std::cout << "move constructor" << std::endl; } Example(const Example &&) { std::cout << "const move constructor" << std::endl; } }; /* std::forward is meaningful only for universal reference in template. */ void test1(Example ex) { std::cout << "Test 1" << std::endl; Example a(std::forward<Example>(ex)); // move constructor Example b(std::forward<Example&>(ex)); // copy constructor Example c(std::forward<Example&&>(ex)); // move constructor } void test2(Example& ex) { std::cout << "Test 2" << std::endl; Example a(std::forward<Example…

[Effective Modern C++] Chapter 4. 똑똑한 포인터 (Smart Pointer) [항목 18~22]

Chapter 4. 똑똑한 포인터 (Smart Pointer) 항목 18. 소유권 독점 자원의 관리에는 std::unique_ptr를 사용하라. 보통 smart pointer를 처음 접한 사람들은 std::shared_ptr만을 남용(?)하는 경향이 있다. 그러나 std::shared_ptr이 매우 강력한 존재이긴 하지만 크게 2가지 측면에서 단점이 있다. 첫 번째는 overhead이다. 참조 계수를 관리하기 위해 어쩔 수 없이 overhead가 존재한다. 두 번째는 돌이킬 수 없다는 점이다. 한번 std::shared_ptr에 pointer를 물리고 나면 다시는 일반 pointer로 복귀할 수 없다. (단순히 .get()을 쓰면 raw pointer를 받을 수 있을 뿐이다. 여전히 프로그램 어딘가에서 포인터를 참조할 수 있을 수 있다.)
이러한 단점들 때문에 나는 기본적으로 std::unique_ptr을 사용한다. std::unique_ptr은 덜 강력하지만 위 2가지 단점이 없다. 추가적인 메모리를 요구하는 커스텀 삭제자를 지정하지 않는다면 performance는 raw pointer와 사실상 동일하고, std::unique_ptr를 사용하다가 적합하지 않은 상황이 오면 언제든지 정책을 raw pointer나 std::shared_ptr등으로 바꿀 수 있다.
참고) raw pointer, std::unique_ptr, std::shared_ptr의 '생성 및 삭제' 성능 비교 : http://www.modernescpp.com/index.php/memory-and-performance-overhead-of-smart-pointer
특히 어떻게 쓰일 지 모르는 포인터를 반환해야 하는 경우는 std::unique_ptr를 쓰는 것이 좋다. std::unique_ptr은 어떤 형태로든지 변환이 가능하기 때문이다. 특별한 목적이 있는 경우가 아니라면, 팩토리 함수등에서 std::shared_ptr 을 반환하는 것은 좋지 않은 습관이다. 하지만 s…