C++ 03 까지의 enum 은 여러 가지 문제를 가지고 있었다. 그래서 그 문제들을 해결하기 위해 C++ 11은 enum class 라는 것을 새로 만들었다. 이제부터 기존의 enum 에 어떤 문제가 있었고, 이것을 enum class 에서 어떻게 해결하였는지 살펴볼 것이다. 우선 기존의 enum 은 전방 선언할 수 없었다. 그 이유는 enumerator에 어떤 값이 들어있을지 알 수 없으면 그 크기를 정할 수 없기 때문 이다. 하지만 enum class 는 underlying type을 명시하지 않으면 int 타입과 같은 크기의 변수로 선언되고, int 값 안에 들어가지 못할 값을 지정하면 컴파일 에러를 발생시킨다. 만약 int 를 벗어난 범위의 값을 사용하고 싶다면, underlying type을 명시해주어야 한다. 기존 enum 의 또 다른 문제는 enumerator의 이름의 범위가 한정되지 않는다는 것이다. 예를 들어 아래와 같은 코드를 보자. IO 함수의 결과와 Parse 함수의 결과를 enum 으로 표현해 보았다. 하지만 이 코드는 컴파일되지 않는다. IOResult 의 Error , Ok 가 ParseResult 의 Error , Ok 와 겹치기 때문이다. 이를 해결하기 위해서는 다음과 같이 enumerator의 이름을 다르게 하거나 아래와 같이 namespace 를 이용해야 했다. 하지만 enum class 는 enumerator의 이름이 enum class 안으로 한정되기 때문에 이런 복잡한 과정이 필요 없이 그저 enum class 를 선언하여 사용하면 된다. 무엇보다 기존 enum 의 가장 큰 문제는 정수형 변수로 암시적으로 변환되는 약 타입(weak type) 변수라는 것이다. 하지만 enum class 는 정수형 변수로 암시적 변환이 되지 않는다. enum class 를 정수형 변수처럼 사용하려고 하면 컴파일 에러를 발생시킨다. 만약 정수형 변수로 사용하고 싶으면 static_cast 를 이용해
https://docs.google.com/presentation/d/12A5RlMCVDN6tA_zUnLrZ0TN5v08gz2GhlRdzxy3T3mU/edit?usp=sharing 회사에서 최근에 Log aggregator system으로 무엇을 사용해야 할지 조사해본 자료다. 우선 log aggregator가 무엇인지 한 문장으로 설명하면, 여러 머신에서 쌓인 로그들을 한 번에 분석할 수 있도록 수집하여 주는 시스템을 말한다. 요새는 특히나 클라우드 시스템이 유행하면서 같은 일을 하는 시스템임에도 다른 머신에서 돌아가는 일이 많아지면서 필요성이 크게 증가하였다. 이번 조사로 알게 된 것들을 적어보도록 하겠다. Scribe 우선 scribe 는 Facebook에서 제작하고 사용하던 log aggregator system이다. scribe: https://www.cnblogs.com/brucewoo/archive/2011/12/13/2285482.html 다른 로그 수집 시스템들을 보면 알겠지만, Scribe는 다른 시스템보다 간단한 구조로 되어 있다. Scribe는 일종의 message queue와 message queue에 쌓인 message를 DB에 저장해 주거나, DB가 실패하였으면 local에 저장하였다가 DB가 복구되었을 때 다시 DB에 저장해 주는 것만을 책임진다. 다시 말하면, message queue에 실제로 메시지를 보내는 부분은 사용자가 직접 작성하여야 한다는 것이다. 흔히들 말하는 scribe의 장점은 성능이다. C++로 만든 만큼 다른 시스템들의 3~5배 정도의 성능을 보여준다는 것이다. 하지만 실제 scribe 사용자들은 무엇보다도 Facebook이 실제로 사용하였던 솔루션인 만큼 성능과 안정성에서 신뢰도가 있다는 것을 장점으로 뽑는다. 하지만 나는 scribe를 사용하는 것을 추천하지는 않는다. 일단 가장 큰 문제는 더이상 Facebook이 Scribe를 사용하지 않는다는 것이다. Facebook은 이미 Ja
SpeechSynthesis 는 Web Speech API의 하나로 주어진 텍스트를 소리로 바꿔주는 TTS API이다. SpeechSynthesis 이전에도 TTS 서비스가 있었지만, 이들은 유료이거나 웹에서 사용하기 불편한 경우가 대부분이었는데 SpeechSynthesis 의 경우 브라우저에 내장되는 API이므로 무료로 쉽게 사용할 수 있다는 장점이 있다. 물론 Web Speech API 자체가 아직 draft 에 해당하기 때문에 브라우저가 지원해야만 사용 가능하다는 문제는 있지만, 2016년 8월 15일 현재 데스크탑에서는 크롬과 사파리, 안드로이드 기본 브라우저인 크롬, 아이폰의 기본 브라우저인 사파리에 지원하고, 파이어폭스는 9월에 지원할 예정이므로 대부분의 모던 브라우저에서는 사용할 수 있다. speechSynthesis 는 5개의 함수를 가지고 있다. 그중 4개는 speak , cancel , pause , resume 이다. 이를 이용해서 재생할 음성을 추가하거나 취소하거나 일시 정지할 수 있다. 이 중 speak 함수는 SpeechSynthesisUtterance 를 인자로 받는다. speak 함수를 호출했을 때, 이미 재생 중인 utterance 가 없고 speechSynthesis 가 pause 되어 있지 않으면, 요청된 utterance 는 즉시 재생된다. 하지만 이미 재생 중인 utterance 가 있거나 speechSynthesis 가 pause 되어 있다면, utterance 는 바로 재생되지 않고 queue에 저장되었다가 후에 재생된다. 따라서 실제로 언제 재생되는지는 utterance 의 콜백을 통해서만 알 수 있다. 이벤트의 종류에 따라서 onstart , onend , onerror , onpause , onresume 등의 콜백을 등록할 수 있다. 또한, SpeechSynthesisUtterance 는 6개의 속성을 가지고 있다. 첫 번째 속성은 text 로 읽을 텍스트를 지정한다. 객체를 생성할
최적화는 귀찮다. 눈에 띄는 실수를 한 게 아니면 어떻게 고쳐야 할지 감이 오지도 않고, 대부분의 최적화는 가독성을 떨어뜨리기 때문에 버그가 발생할 확률이 늘어난다. 하지만 어떤 최적화 테크닉은 코드를 크게 수정하지 않고 큰 성능 향상을 가져온다. 메모이제이션 이 그 대표적인 예제다. 계산이 무겁거나, 디스크의 값을 읽거나, 네트워크 통신처럼 근본적으로 시간이 오래 걸리는 일은 그 실행 결과를 저장했다 재사용하는 것만으로 큰 성능향상을 가지고 온다. 파이썬은 메모이제이션을 쉽게 적용할 수 있는 데코레이터 를 제공한다. functools 모듈의 lru_cache 데코레이터 가 이것이다. 이 데코레이터를 붙이면 함수의 실행 결과를 캐싱해준다. 캐시의 크기는 maxsize 로 지정할 수 있다. 저장할 실행 값이 이 개수를 넘어가는 경우 LRU 알고리즘 에 따라 가장 오래전에 사용한 결과를 지우고 새 값을 캐싱한다. lru_cache 를 사용하면 쉽게 최적화할 수 있지만 아무 함수에나 사용할 수 있는 건 아니다. 함수의 인자를 캐시키로 사용하기 때문에 함수의 실행 결과가 함수의 인자 이외에 다른 요소에 의존적인 함수에는 사용하지 못한다. 즉, 랜덤 요소가 들어가거나 시간에 따라 결괏값이 변하는 함수에는 사용하면 안 된다. 결정성이 보장되는 함수에만 사용할 수 있다는 것은 모든 캐시의 공통적인 특성이다. 여기에 더해 파이썬이 제공하는 lru_cache 는 그 구현상의 문제로 한 가지 제약이 더 있다. 이 데코레이터는 값을 저장하기 위해 인자를 키로 가지는 dictionary 를 사용한다. 따라서 모든 인자가 hashable 타입이어야 한다. 다시 말해 mutable 하지 않은 dictionary, set, list 등을 인자로 받는 함수는 이 데코레이터를 사용해 캐싱할 수 없다. 이런 타입을 인자로 받던 함수는 그 인자를 frozenset 이나 tuple 같은 immutable 타입으로 변환해야 한다. 게다가 keyword argument 를
RAII 는 C++에서 자주 쓰이는 idiom으로 자원의 안전한 사용을 위해 객체가 쓰이는 스코프를 벗어나면 자원을 해제해주는 기법이다. C++에서 heap에 할당된 자원은 명시적으로 해제하지 않으면 해제되지 않지만, stack에 할당된 자원은 자신의 scope가 끝나면 메모리가 해제되며 destructor가 불린다는 원리를 이용한 것이다. 원래는 exception 등으로 control flow가 예상치 못하게 변경될 때를 대비하기 위해서 쓰이던 기법이다. 아래의 예제를 보자. 첫 번째 코드는 위험하다. thisFunctionCanThrowException() 함수에서 exception을 발생시킨다면 resource를 해제하지 못한다. 두 번째 코드는 일단은 resource 를 해제하고 있다. 하지만 보기에 좋은 코드는 아니다. 그리고 유지하기도 어려워진다. 세 번째 코드는 RAII를 위해 c++ 11의 스마트 포인터인 unique_ptr 을 이용하는 방법이다. unique_ptr 은 소멸할 때 자신이 들고 있는 메모리를 해제시켜주기 때문에 함수 밖으로 나가면 resource 가 해제되는 것이 보장된다. 여기서 말하는 자원은 단순히 heap 메모리만을 말하는 것이 아니다. heap 메모리 이외에도 파일이나 database connection과 같은 것들도 전부 RAII를 이용해 안전하게 사용할 수 있다. 여기에 더 나아가서 특정 scope를 벗어나면 반드시 실행돼야 하는 코드들도 RAII를 이용해 처리할 수 있다. 즉, 다른 언어에서 finally 에 해당하는 구문을 RAII를 이용해서 처리할 수 있다. 실제로 C++의 아버지이자 RAII라는 용어를 처음 만든 Bjarne Stroustrub는 c++에 finally 를 집어넣지 않는 이유를 "RAII가 있는데 굳이 있을 필요가 없다." 라고 말하고 있다.