C++17 표준 라이브러리의 파일 시스템 라이브러리 소개

류광, 2017/07/22 13:20
이어지는 핵심 C++ 표준 라이브러리" 부록 공개. 이번에는 C++17에 추가된 파일 시스템 라이브러리를 소개합니다.

"핵심 C++ 표준 라이브러리" 부록 A 중 C++17에 추가된 파일 시스템 라이브러리를 소개하는 글입니다.

파일 시스템 라이브러리

C++14까지 C++ 표준 라이브러리에는 파일 입출력을 위한 수단은 있었지만 파일 시스템을 위한 수단은 없었다. C++17에서 드디어 C++도 파일 시스템 라이브러리를 가지게 되었다. 이 파일 시스템 라이브러리는 원래 boost.filesystem으로 출발해서 TS(Technical Specification) 단계를 거쳐서 표준 라이브러리에까지 들어오게 된 것이다. C++ 파일 시스템 라이브러리의 모든 형식과 함수는 std::filesystem 이름공간에 속하며, 필요한 헤더는 <filesystem>이다.

파일 시스템 라이브러리에는 파일의 경로를 나타내는 path와 디렉터리의 한 항목(보통의 파일뿐만 아니라 하위 디렉터리, 기호 링크, 소켓, 파이프 등도 포함)을 나타내는 directory_entry, 파일의 종류와 상태를 나타내는 file_status, 가용 디스크 용량을 위한 space_info, 파일 시간을 위한 file_time_type 등 다양한 클래스가 있으며, 디렉터리 반복(운행)을 위한 두 반복자 클래스 directory_iteratorrecursive_directory_iterator도 제공한다. 또한, 파일 접근 권한, 파일 종류, 복사 옵션, 디렉터리 반복 운행) 옵션을 위한 열거형들(perms, file_type 등)도 갖추어져 있다.

이러한 형식들 외에, 실질적인 파일 시스템 연산들을 수행하는 다양한 비멤버 함수들이 있다. 예를 들어 directory_entry 클래스에는 디렉터리를 생성하거나 파일을 복사하는 멤버 함수가 없다. 디렉터리 생성은 비멤버 함수 create_directorycreate_directories가, 복사는 비멤버 함수 copycopy_file이 담당한다. 그 밖에 rename, resize_file, current_path, exits 등 경로와 파일, 디렉터리를 다루는 데 필요한 다양한 함수가 있다. 또한, is_directory, is_empty, is_regular_file 등 주어진 디렉터리 항목의 종류를 판정하는 술어 함수들도 있다. 표 A.1은 파일 시스템 라이브러리의 구성요소들을 나열한 것이다.

void h()
{
    namespace fs = std::filesystem;
    fs::create_directory("foo");

    // create_directories는 경로 중간의 디렉터리들까지 생성해준다.
    fs::create_directories("foo/bar/abc/123")

    // 보통의 파일을 생성하려면 파일 입출력 라이브러리가 필요하다.
    std::ofstream("foo/bar/abc/file.txt");

    // 반복자를 이용해서 foo 디렉터리와 그 하위 디렉터리의
    // 모든 항목을 훑는다.
    for(auto&& x: fs::recursive_directory_iterator('foo')) {
        cout << x.path() << endl;
    }
}

표 A.1 파일 시스템 라이브러리의 구성

		형식
path			recursive_directory_iterator	perms
filesystem_error	file_status			copy_options
directory_entry		space_info			directory_options
directory_iterator	file_type			file_time_type
		함수
absolute		create_directories		permissions
system_complete		create_hard_link		read_symlink
canonical		create_symlink			remove
weakly_canonical	create_directory_symlink	remove_all
relative		current_path			rename
proximate		exists				resize_file
copy			equivalent			space
copy_file		file_size			status
copy_symlink		hard_link_count			symlink_status
create_directory	last_write_time			temp_directory_path
		파일 종류 및 상태 판정 술어
is_block_file		status_known			is_regular_file
is_character_file	is_fifo				is_socket
is_directory		is_other			is_symlink
is_empty
top
트랙백 0 : 의견 # + 0

C++17 표준 라이브러리의 std::variant 소개

류광, 2017/07/15 18:35
C++17 표준 라이브러리에 추가된 std::variant를 소개합니다.

저번 글에 이어 "핵심 C++ 표준 라이브러리" 부록을 방출합니다. 이번에는 std::variant입니다. 부록에 실린 것과 본질적으로 같은 내용이지만, 링크와 주석이 하나 추가되었습니다.

형식에 안전한 공용체, std::variant

std::any가 형식에 안전한 void*라면, std::variant는 형식에 안전한 공용체(union)라 할 수 있다. 필요한 헤더는 <variant>이다. 다음은 variant의 형식 안전성을 보여주는 예이다.

void f()
{
    variant<int, double, string> v = 123;

    cout << v << endl; // 123

    cout << std::get<int>(v) << endl; // 명시적으로 int를 요청.
                                      // 앞의 행과 같은 결과를 낸다.

    cout << std::get<0>(v) << endl;   // 첫 번째 형식(int)의 값을 요청.
                                      // 역시 같은 결과를 낸다.

    auto f = std::get<float>(v);      // (1) 컴파일 오류

    auto cond = std::get<string>(v);  // (2) 실행 시점 예외 발생
}

이 예제에서 vintdouble, bool을 담을 수 있는 variant 객체인데, (1)은 엉뚱하게 float 값을 요구했다. 이런 오류는 컴파일러가 잡아낼 수 있으므로 컴파일 오류가 발생한다. (2)는 v가 가질 수 있는 세 형식 중 하나인 string을 요구했으므로 컴파일 오류는 발생하지 않지만, 대신 실행 시점에서 std::bad_variant_access라는 예외가 발생한다. 이는 v가 현재 가지고 있는 값, 즉 마지막으로 설정된 값의 형식(int)이 요청된 형식(string)과 일치하지 않기 때문이다. 둘 다 union을 사용할 때 프로그래머들이 흔히 하는 실수인데, variant는 이런 실수들을 컴파일 시점에서 방지하거나 실행 시점에서 보고해준다. 이런 측면 때문에 variant를 “형식에 안전한 공용체”라고 부른다. 또한, (1)과 같은 컴파일 시점 점검 능력은 std::any에 비한 장점이기도 하다.

앞의 예제의 (2)에서 보듯이 variant를 사용할 때에는 “이 variant 객체에 어떤 형식의 값을 담을 수 있는가?”도 중요하지만 “현재 이 variant 객체에 어떤 형식의 값이 담겨 있는가?”도 중요하다. 이와 관련해서 variant는 다음과 같은 멤버·비멤버 인터페이스들을 제공한다. 다음 예를 보자.

void g()
{
    variant<int, double, bool> v = 123;

    // (1)
    auto pv = std::get_if<bool>(&v);           // pv는 nullptr

    // (2)
    auto cond = std::holds_alternative<int>(v) // cond는 true

    // (3)
    auto i = v.index();                        // i는 0
}
  1. std::get과는 달리 std::get_if는 포인터를 받으며, 형식 불일치 시 예외를 던지는 대신 널 포인터를 돌려준다.
  2. std::holds_alternative<T>(v)는 현재 v 객체의 형식이 T인지의 여부를 뜻하는 bool 값을 돌려준다.
  3. 멤버 변수 index는 현재 형식의 색인을 돌려준다. 지금 예에서 int가 0, double이 1, bool이 2이다. 이 색인은 std::get<i>(v) 형태로 활용할 수 있다.

공용체는 형식 안전성이 떨어질 뿐만 아니라, 소위 POD(plain old data) 형식들만 담을 수 있다. 반면 variant는 첫 예제(f(x))의 variant<int, double, string>에서 보듯이 POD가 아닌 본격적인 클래스도 담을 수 있다. 이 덕분에 성격이 크게 다른 형식들을 하나의 variant 객체로 공유할 수 있는데, 이러한 능력은 소위 ‘방문자 패턴(Visitor pattern)’의 구현에 도움이 된다. 실제로, 이를 위해 variant는 아예 std::visit이라는 비멤버 함수를 제공한다.[1]


[1] 이 부분은 variant의 활용에 중요하지만 지면 제한 때문에 간단하게만 언급했는데, 언제 std::variant와 방문자 패턴, 그리고 '꼬리표 달린 공용체(tagged union)'에 관해 보충 글을 써보겠습니다.

top
트랙백 0 : 의견 # + 0

C++17 표준 라이브러리의 std::any 소개

류광, 2017/07/08 19:58
C++17 표준 라이브러리에 추가된 std::any를 소개합니다.

다음은 6월에 출간된 "핵심 C++ 표준 라이브러리"의 부록 A의 일부입니다. 원래는 블로그 글을 먼저 쓰고 그걸 다듬어서 부록으로 수록하려 했는데(stringview 소개글optional 소개글이 그런 시도의 결과입니다) 책이 먼저 나와버렸습니다.

어쨌거나, 핵심 C++ 표준 라이브러리 번역서 출간과 연계해서 이 블로그에서 C++17 표준 라이브러리를 소개한다는 애초의 계획을 실행하기 위해 부록의 나머지 부분을 이곳에 차츰 올리도록 하겠습니다. 첫 타자는 std::any입니다. 블로그 말투가 아니라 단행본 문체라 좀 어색할 수도 있지만, 그냥 그대로 갑니다!

아무 형식이나 담을 수 있는 std::any

std::any 형식의 객체에는 말 그대로 ‘아무(any)’ 형식의 값을 담을 수 있다. 단, 복사가 가능한(copyable) 형식이어야 한다. 필요한 헤더는 <any>이다.

struct Position { double x, y, z; };

void f()
{
   any a = std::string("C++ 문자열");

   a = 123;                // 새 값이 배정되기 전에 기존 값(string 객체)이
                           // 파괴된다(소멸자 호출 및 메모리 해제).

   a = Position{1, 2, 3};  // 복사할 수 있는 형식이면 어떤 형식도 가능.
}

이런 식으로 한 변수에 임의의 형식의 값을 담아야 할 때 흔히 쓰이는 수단은 무형식 포인터(typeless pointer) void*reinterpret_cast(또는 C 스타일 캐스팅)이다. 그러나 그 둘만으로는 형식 안전성을 보장하기 힘들다. 형식 안전성을 위해서는 any 객체에 새 값이 배정되기 전에 기존 값이 제대로 파괴되어야 하는데, void*만으로는 그것을 보장하기 힘들다. 그러나 앞의 예제의 주석에서 보듯이 any는 객체의 적절한 파괴(소멸)를 보장한다. 이런 측면 때문에 std::any를 “형식에 안전한(typesafe) void*”라고 표현하기도 한다.

any 객체에 담긴 값을 꺼낼 때는 std::any_cast라는 함수를 사용한다.

any a = string("C++ 문자열");

cout << any_cast<string>(a) << endl // C 문자열

int i = any_cast<int>(a);           // 형식이 일치하지 않음:
                                    // bad_any_cast 예외 발생.

위의 예처럼 any 객체 자체를 인수로 받는 any_cast는 만일 요청된 형식과 자신에 담긴 자료의 형식이 일치하지 않으면 std::bad_any_cast 형식의 예외를 던진다. 이러한 형식 점검은 std::any를 “형식에 안전한 void*”라고 부르는 또 다른 이유이다.

형식이 일치하지 않을 때 예외가 발생하는 것을 원하지 않는다면, 다음처럼 포인터를 받고 포인터를 돌려주는 버전의 any_cast를 호출하면 된다. 이 버전은 형식이 일치하지 않으면 널 포인터(nullptr)를 돌려준다.

optional처럼 any에도 빈 객체를 생성하는 기본 생성자가 있으며, 멤버 함수 has_value, emplace, reset도 있다. any의 멤버 함수 typeany 객체에 담긴 자료의 형식을 나타내는 type_info 객체(에 대한 참조)를 돌려준다. 빈 any 객체의 경우 그 type_info 객체는 typeid(void)에 해당한다.

any a;                              // 빈 객체

cout << a.has_value() << endl;      // false

a.emplace<string>("Hello, world!"); // 제자리 생성.

cout << a.type().name << endl;      // 구체적인 형식 이름은
                                    // 컴파일러마다 다를 수 있음.

opt2.reset();                       // 이제 has_value()는 false.
top
트랙백 0 : 의견 # + 0

[종료] "핵심 C++ 표준 라이브러리" 출간 기념 독자 증정 이벤트

류광, 2017/07/01 13:48

며칠 전 "핵심 C++ 표준 라이브러리" 출간 소식에서 이야기했던 독자 증정 이벤트 소식입니다. 선착순이니 서두르세요~

주의: 이번 이벤트는(그리고 아마도 이후에도) 이전에 이곳과 GpgStudy.com에서 진행한 제 번역서의 증정 이벤트에 당첨된 적이 없는 분들만 대상으로 합니다. 기존 당첨자분들은 기회를 양보해 주시길 부탁합니다!

아래 규칙에 따라 GpgStudy 포럼에 글 올려 주신 선착순 세 분께 "핵심 C++ 표준 라이브러리"를 한 권씩 보내 드리겠습니다.

  1. http://www.gpgstudy.com/catalog/etc에 있는 '글 쓰기' 버튼을 통해서 글을 올려 주세요.
  2. 글 제목에 반드시 "핵심 C++ 표준 라이브러리"와 "이벤트"라는 문구를 포함해 주세요.
  3. GpgStudy 포럼 비회원은 글 내용에 반드시 식별&연락 가능한 이메일 주소를 포함해 주세요. @를 _AT_이나 '골뱅이' 등으로 바꾸는 것은 좋습니다.
  4. 포럼에 로그인한 회원분은 글 내용에 이메일을 안 남기셔도 됩니다. 단, 그런 경우 당첨 시 회원 정보에 등록된 이메일 주소로 연락을 드리니 그 이메일 주소가 유효한지 반드시 확인하세요.

이상의 규칙과 맞지 않는 글은 선착순 판정에서 제외되니 주의하세요. 실수가 있었다면 새로 글을 올리거나 수정하시면(로그인한 경우)면 됩니다. 단, 선착순은 항상 마지막 글을 또는 마지막 수정 일시를 기준으로 합니다.

당첨자가 결정되면 이곳과 포럼 스레드에 공지한 후 주소를 묻는 메일이 보내겠습니다. 세 분께 한꺼번에 책을 보내기 때문에 한 분이라도 답장이 늦으면 다른 분들이 기다려야 하니, 당첨 사실을 확인하신 분들은 메일을 자주 확인해 주시길 부탁합니다.

이 글을 보고 계신다면 이미 선착순 이벤트가 시작된 것입니다. 이 좋은 토요일 오후에 이 썰렁한 블로그에 방문해 주신 분들께 행운이 따르길 기원합니다!


선착순 세 분 결정되었습니다. pwn.kerman 님, st1tch 님, 삽질러 님 축하합니다. 곧 메일 보내겠습니다.


세 분께 책 보냈습니다. 이로써 이번 이벤트를 마무리합니다.

이 이벤트에 직, 간접적으로 참여해주신 모든 분과 GpgStudy.com을 통해서 이 이벤트를 후원해 주신 넷텐션, 그리고 좋은 책을 내 주신 도서출판 인사이트에 감사 인사 전합니다!

top
트랙백 0 : 의견 # + 0

번역서 "핵심 C++ 표준 라이브러리" 나왔습니다.

류광, 2017/06/26 17:52
번역서 "핵심 C++ 표준 라이브러리" 출간 소식입니다.

본문 열기

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

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