번역서 정보 | 번역서 질문&의견 | 번역 이야기 | 문서 창고 | 방명록

C++ RAII(Resource Acquisition Is Initialization)의 해석

류광, 2015/08/18 22:16
RAII라는 이름 자체에 대한 "꿈보다 해몽"

조만간 번역서가 출판될 Effective Modern C++ 어딘가에 다음과 같은 문구가 나옵니다:

[인용]

(RAII 자체는 “Resource Acquisition Is Initialization”을 뜻하지만, 사실 이 기법의 핵심은 초기화가 아니라 파괴이다.)

관련해서 기억나는, 10년도 더 된 논의가 하나 있습니다: GpgStudy 포럼 “클래스 생성시 ... ”

특히 조순현 님의 답글제 답글이 이번 블로그 글과 관련이 있는데요. 제 답글에 등장하는 http://tinyurl.com/4ogc8는 현재는 깨진 링크이고, 원래 의도한 해당 뉴스그룹 글은 이것입니다.

당시는 “RAII의 핵심은 초기화가 아니라 파괴”라는 관점에서 답글을 달았지만, 지금은 조순현 님(당시 닉은 ‘수렁’)의 “자동변수뿐만 아니라 자원 획득은 거의 모든 경우에 있어서 초기화(준비 완료)여야 한다고 생각합니다.”라는 의견을(곁들여서 C++의 예외에 대해서도) 좀 더 논의했다면 좋았겠다고 후회하고 있습니다.

한편, 그 논의와는 무관하게 전부터 RAII라는 용어의 유래에 대해 궁금해했습니다. ‘함흥차사’가 무슨 뜻인지가 아니라 그런 뜻에 왜 ‘함흥차사’라는 이름이 왜 붙었는지 궁금해하는 것과 비슷하게요. 겸사겸사 해서 지금까지 생각한 것들을 장황하게나마 써보겠습니다.

“Resource Acquisition Is Initialization”이라는 문장은 다음 두 가지로 해석할 수 있습니다.

  1. “자원 획득은 초기화이다”
  2. “자원 획득이 초기화이다”

토씨 하나만 다른 문장들이지만 의미는 사뭇 다릅니다. 1번은 “자원 획득은 무엇인가?”에 대한 답이고, 2번은 “무엇이 초기화인가?”에 대한 답입니다.

C++ 객체의 수명 주기를 참작할 때, 그리고 답의 ‘초기화’라는 단어를 생각할 때, “자원 획득은 무엇인가?”라는 질문에는 “자원 획득은 언제 일어나야 하는가?”라는 질문이 포함되어 있다고 생각할 수 있습니다. 비슷한 논리로, “무엇이 초기화인가?”라는 질문에는 “초기화 시점에서 주로 수행해야 할 작업은 무엇인가?”라는 질문이 포함되어 있겠고요.

그 두 질문의 답을 합친다면, “자원 획득은 초기화 시점에서 일어나야 하며, 초기화 시점에서 해야 할 중요한 작업은 자원 획득이다”가 되겠습니다. 그런데 이러한 답이 의미가 있으려면, 객체의 유효성(앞의 포럼 논의에서는 Testors 님이 여러 번 강조하신 ‘인스턴스의 무결성’)에 대한 다음과 같은 관점이 필요합니다.

  1. 생성이 완료된 객체는 유효한 객체이어야 한다. 즉, 유효하지 않은 객체가 생성되어서는 안 된다.
  2. 필요한 자원들을 획득하지 못한 객체는 유효한 객체가 아니다.

이러한 관점에서 “자원 획득은/이 초기화이다”는 모든 자원은 생성자 안에서 완전하게 획득해야 하며, 획득하지 못한 자원이 하나라도 있는 상태에서 생성자가 완료되어서는 안 된다는 뜻이라 할 수 있습니다. 그리고 대칭성에 의해, 파괴(소멸)되어서 더 이상 유효하지 않게 된 객체는 그 어떤 자원도 가지고 있지 않아야 합니다. 이로부터 흔히 통용되는 RAII의 뜻, 즉 “(예외가 발생해도 누수가 일어나지 않도록)소멸자에서 자원을 해제하는 기법”을 이끌어 낼 수 있고요.

물론 이는 “꿈보다 해몽”입니다. 사실 “Resource Acquisition Is Initialization”이라는 문구를 만든(The Design and Evolution of C++ 참고) 비야네 스트롭스트룹 본인은 RAII가 어떤 중심 개념을 대표하는 이름치고는 ‘clumsy’한(서투른, 재치 없는, 꼴사나운 등등) 이름이라고 말합니다(Bill Venners와의 인터뷰). 다른 인터뷰에서도 비슷한 말을 한 것으로 기억하며, 그렇다고 더 나은 이름을 제시한 적도 없는 것으로 알고 있습니다. 어쨌든, 비록 자원 획득이 초기화 시점에서 일어나지 않았다고 해도 소멸자에서 자원을 해제하는 기법은 여전히 유효하고 유용하다는 점에서[1], 그런 기법을 RAII라고 부르는 것은 그리 적합하지 않은 것은 사실입니다. 그래서 “scope guard” 같은 용어를 사용하는 경우도 있고요.

덧붙여 말하자면, 앞에서 말한 ‘관점’이 단지 RAII를 제대로 해석하는 데 필요한 것은 물론 아닙니다. 이런 관점은 거창하게 말하면 코드의 정확성 검증이나 보장에 큰 도움이 되고, 실용적으로 표현하자면 “제대로 사용하기는 쉽고 잘못 사용하기는 어려운” 클래스를 만드는 데에도 도움이 될 것입니다. 이 주제에 대해서도 언제 한 번 글을 써보겠습니다.

마지막으로 한 마디 더…. 언급한 포럼 논의에서는 예외와 오류 처리에 관한 논의를 자제해 달라고 했지만[2], 사실 RAII를 이야기하면서, 그리고 “생성이 완료된 객체는 유효한 객체이어야 하고 유효하지 않은 객체가 생성되어서는 안 된다”는 관점으로 객체를 고찰할 때 예외에 대한 이야기를 빼놓는 것은 예외 입장에서는 서운한 일일 것입니다. 이와 관련해서 comp.lang.c++.moderated에 Andrei Alexandrescu(Loki 라이브러리와 Modern C++ Design 저자)가 올린 질문 “Are throwing default constructors bad style, and if so, why?”에서 시작된 논의가 있는데, 곱씹어 볼만한(그리고 앞에서 언급한 GpgStudy 포럼 논의에서 다루지 못한) 부분들이 많이 있습니다. 앞의 ‘관점’에 대한 글을 쓰게 된다면(아마도), 예외에 대한 이야기도 당연히 포함될 것입니다.


[1] 물론 문자 그대로의 의미에서의 RAII가 지켜진다면 필요하지 않았을 추가 점검을 소멸자에서(그리고 어쩌면 생성 이후에 자원을 획득하는 여러 멤버 함수들에서도) 수행해야 하겠지만요.

[2] 아마도 당시 있었던 어떤 소모적인 논쟁 때문이었거나, 흔히 쓰이던 계통구조(트리) 스타일 게시판이 아닌 소위 ‘플랫(flat)’ 스타일의 게시판인 phpBB 시스템의 특성 때문이었을 것입니다.

top
트랙백 0 : 의견 # + 0

루아(Lua)의 사용자 정의 리터럴

류광, 2015/06/24 22:25
루아의 간결한 함수 호출 구문을 이용한 사용자 정의 리터럴 흉내내기

예전부터 C++는 리터럴에 접미사를 붙여서 리터럴의 구체적인 형식을 명시하는 표기법을 지원했습니다. 0.5f123L이 그러한 예입니다. C++11과 C++14에서는 그런 리터럴 접미사의 종류가 많이 늘어났고, 또 다양한 종류의 문자열을 구분하기 위한 문자열 리터럴 '접두사'들도 많이 생겼습니다. 이를테면 u8"UTF-8 문자열" 같은 문자열 리터럴이 있습니다.

더 나아가서, C++11에는 아예 리터럴 접미사를 사용자가 직접 만들어 쓸 수 있게 하는 사용자 정의 리터럴(user-defined literal) 기능이 도입되었습니다. 사용자 정의 리터럴을 잘 활용하면 "캐주얼한" C++ 코드의 안전성을 높일 수 있습니다. 이를테면 이런 코드가 가능합니다.

// Meter와 Mile이라는 사용자 정의 수치 형식들이 있다고 가정
Meter operator "" _meter(double len) {return Meter(len); }
Mile operator "" _mile(double len) {return Mile(len); }

template<typename T>
T hypotenuse(T b, T h); // 밑변과 높이로부터 빗변을 계산
                        // 구현은 생략
...

auto h1 = hypotenuse(3.0_meter, 4.0_meter);

auto h2 = hypotenuse(3.0_meter, 4.0_mile); // 컴파일 오류

(참고로 _meter_mile_은 필수입니다. _이 없는 접미사는 표준만 사용할 수 있습니다.)

h2의 경우 형식 불일치 때문에(좀 더 정확하게는, 컴파일러가 첫 인수와 둘째 인수의 형식이 다른 버전의 hypotenuse를 찾지 못해서) 오류가 납니다. 이렇게 하면 단위가 다른 두 수치를 함께 계산해서 생기는 논리적 버그를 미리 방지할 수 있습니다(실제로 이런 종류의 버그 때문에 큰 사고가 난 적이 있지요). 어떤 경우이든 단위 불일치에 의한 버그를 방지할 수 있습니다.

그런데 루아에도 이런 사용자 정의 리터럴 기능이 있습니다. 단, 루아의 사용자 정의 리터럴에는 접미사가 아니라 접두사가 쓰이고, 리터럴을 반드시 따옴표나 중괄호로 감싸야 합니다. 그리고 _는 필수가 아닙니다.

예:

local dec = b"1010" -- dec는 십진수로 10 (=이진수 1010)
local h = m{100.0} -- h는 100미터

루아를 제대로 공부하신 분은 "루아의 사용자 정의 리터럴"이 거짓말임을 금방 알아챘을 것입니다. 루아를 굵직한 부분만 훑고 넘어간 분이라면 루아에 이런 기능이 있었나 싶을 것이고요.

사실 b"1010"이나 m{100.0}은 사용자 정의 리터럴이 아니라 그냥 함수 호출입니다. 루아에서는 함수 호출의 인수가 하나이면, 그리고 그 인수가 문자열 리터럴이나 테이블 리터럴이면, 호출 표기에서 괄호를 생략할 수 있습니다. 즉, 앞의 두 줄은 다음에서 괄호를 생략한 것입니다.

local dec = b("1010")
local h = m({100.0})

루아에는 이처럼 뭔가 복잡해 보이는 기능이 사실은 간단한 "표기 상의 요령"일 뿐인 경우가 종종 있는데, 이런 점이 루아의 매력이라고 생각합니다(루아만의 특징은 아니겠지만요). "사용자 정의 리터럴 지원 기능을 언어 자체에 추가하자"가 아니라, "호출 시 괄호를 생략할 수 있게 해서 호출 구문을 마치 사용자 리터럴처럼 사용할 수 있게 하자"인 것이죠.

리터럴 이야기를 하는 김에 한 가지 더: 루아에는 문자열 처리를 위한 string이라는 형식이 있지만, 문자열 리터럴이 그 자체로 string 객체는 아닙니다. 예를 들어 다음은 적법한 코드이지만,

local s = "hello %s"
local result = s:format("world")

다음은 구문 오류입니다.

local result = "hello %s":format("world") -- 오류!

문자열 리터럴을 string 객체로 만들려면 괄호로 한 번 감싸주면 됩니다:

local result = ("hello %s"):format("world")

문자열 리터럴 자체가 string 객체인 경우(파이썬이나 JavaScript처럼)에 비하면 여분의 괄호가 필요하지만, 앞에서처럼 변수를 따로 두거나 다음과 같이 string을 명시하는 호출보다는 타자량이 적습니다.

local result = string.format("hello %s", "world")

한편, obj.func(obj, args)obj:func(args)로 표기할 수 있다는 것 역시 앞에서 말한 루아의 매력적인 표기 상의 요령인데요. 기회가 되면 언제 한 번 글을 써 보겠습니다.


Programming In Lua 같은 책에 다 나와 있는 이 이야기를 굳이 한 것은, 조금 전에 언급했듯이 루아를 그냥 굵직한 부분만 공부하거나 좋게 말해서 사례 기반으로 공부한 분들이 좀 있는 것 같아서입니다. 아직도 그냥 게임 스크립트용으로만 쓰이는 언어라는 이미지가 강한 것 같은데, 루아는 진지하게 파고 들어가 볼만한, 꽤나 깊이가 있는 언어라고 생각합니다. 늦더라도 Programming In Lua 제3판 번역서가 나오길 기대하며 이만 줄입니다.

(2015-07-08 추가) 마지막 문장을 다음과 같이 수정합니다: Programming In Lua 제3판 번역서('프로그래밍 루아') 많이들 읽으시길 기대하며 이만 줄입니다. 출간 소식 알려주신 남수진님 감사합니다.

top
트랙백 0 : 의견 # + 0

Functional Programming in Scala 번역서 나왔습니다

류광, 2015/04/07 17:57
Functional Programming in Scala 번역서 "스칼라로 배우는 함수형 프로그래밍" 출간 소식

소식이 조금 늦었습니다. Functional Programming in Scala 번역서가 저번 달에 출간되었습니다.

스칼라로 배우는 함수형 프로그래밍

스칼라 입문서는 아니니 주의하세요! 함수형 프로그래밍의 매력이 무엇인지 보여주는 것이 주된 목적인 책입니다.

"함수형"이라는 용어에 대해 글을 한 번 쓰겠다고 했는데, 일단 역자의 글 중 일부의 일부를 인용합니다:

[인용]

번역하면서 용어 선택과 관련해서 고민했던 사항이 두 가지 있었는데, 이 책의 주제 인 함수형 프로그래밍의 이해에도 도움이 될 것 같아서 독자와 공유하고자 합니다. 첫째는 functional programming의 functional을 어떻게 번역할 것인가였습니다. 흔히 쓰이는 ‘함수 형 프로그래밍’은 일상적인 조어법에서 ‘~nal’을 ‘~적’으로 옮기는(논리적, 전형적, 물리적 등 등) 것이 압도적이라는 이유에서 그리 마음에 들지 않습니다. ‘~적’의 남용이 문제이긴 하지 만, 그렇다고 ‘~적’ 대신 ‘~형’을 쓰는 것은 장기적으로 ‘~형’을 제2의 ‘~적’으로 만들 위험이 있다고 생각합니다. 그러나 관계형 데이터베이스처럼 함수형 프로그래밍도 꽤 확립된 용어 라서 무작정 무시할 수도 없는 노릇입니다. 결국 선택한 절충은, functional programming 은 관례대로 ‘함수형 프로그래밍’이라고 하되 programming 이외의 용어에 붙는 functional 은 ‘함수적’으로 옮긴다는 것입니다(함수적이 언젠가는 함수형에 버금가는 관례로 자리 잡길 바 라면서). 본문에 나오는 함수적 자료구조, 함수적 접근방식 등은 이러한 고민의 산물입니다. 이는 자료구조 tree를 관례상 ‘트리’로 표기하되, root, leaf, branch는 나무의 비유를 잘 살린 뿌리, 잎, 가지로 옮기는 전략과 비슷합니다.

'~적' 자체에 대해서도 이야기할 것이 좀 있는데, AIMA3 번역 마치고 좀 더 자세한 글을 써보겠습니다.

top
TAG 번역서
트랙백 0 : 의견 # + 0

웹 항해일지, 2015-03-16

류광, 2015/03/16 18:25

오랜만의 웹 항해일지이자, 실로 오랜만'긴 글' 항해일지입니다.

top
트랙백 0 : 의견 # + 0

2015년 새해 복 많이 받으세요!

류광, 2015/01/18 19:38

저를 아는 모든 분께 늦게나마 새해 인사 올립니다. 새해 복 많이 받으세요!

작년 한해 제 번역서와 블로그, GpgStudy 포럼과 게임 개발 소식을 매개로 많은 분과 직, 간접적으로 인연을 맺었습니다. 항상 고맙습니다.

올 초에는 Functional Programming in Scala의 번역서가 나올 예정이고, 그후에는 Artificial Intelligence 3rd (AIMA3) 번역서와 Effecitve Modern C++ 번역서가 기다리고 있습니다. 작년에는 게임 개발서를 번역하지 못해서 아쉬웠는데, 앞에서 언급한 책들을 마친 후에는 게임 개발서를 최우선으로 고려할 생각입니다.

번역 외 활동과 관련해서 작년 초에 했던 다짐들은 부끄럽게도 대부분 지켜지지 않아서 스스로 좀 실망인데요. 그래도 다시금 마음을 다잡으면서 작년 새해 인사 중 한 부분을 복붙해 봅니다:

[인용]

"... 좀더 구체적으로, 루아, 닥북과 epub, C++, 번역어, 프로그래밍 등에 대해 분야별로 적어도 글 두어 개 정도는 쓰려고 합니다."

다시 한 번, 모두 새해 복 많이 받으시고 항상 건강하세요!

top
트랙백 0 : 의견 # + 0

◀ PREV : [1] : [2] : [3] : [4] : [5] : ... [54] : NEXT ▶