====== Boost.PropertyTree ====== 문서 주소 : [[http://www.boost.org/doc/libs/1_51_0/doc/html/property_tree.html#boost_propertytree.intro]] 프로퍼티트리란? * 데이터 구조를 저장하는 라이브러리. * 데이터 구조 중에, 트리 형식으로 저장 되는 것들을 다룬다. * 트리의 형식은, * 깊이에 대한 제한이 없고 트리의 각 단계가 키로 정렬되는 형식을 갖는다. * 각 노드는 고유 값이나 정렬된 상태의 서브 노드를 갖을 수 있다. * 이 트리는 각 노드에 접근할 때 키의 이름들이 연결 해서 찾으려는 노드에 접근하는 방식을 취한다. * 궁극적으로는 트리형식인, XML, INI, JSON에 대한 파서(읽기)와 생성자(제네레이터)를 제공한다. 프로퍼티 트리는 다재다능한 데이터 구조체로 특히, 설정 데이터를 저장하는데 적합하다. \\ 트리는 각 트리에 맞는 인터페이스를 갖고 있고, 각 노드들은 STL호환되는 시퀀스이다. \\ 개념적으로는 각 노드는 이런 형식이 된다. struct ptree { data_type data; // 노드의 데이터 list< pair > children; // 새로운 키와 새 리스트 'key_type'과 'date_type'은 설정에 따르게 된다. 하지만 보통은 std::string이다. 많은 sw 프로젝트에서 이와 비슷한 툴을 개발하는데, 프로퍼티 트리가 다시 만들어야 하는 굴레를 없앴으면 한다. ====== Five Minute Tutorial ====== 문서 : [[http://www.boost.org/doc/libs/1_51_0/doc/html/boost_propertytree/tutorial.html]] 이 튜토리얼은 xml을 사용하지만 다른 형식의 데이터(INI나 JSON)에도 적용이 가능한다. XML이 광범위하게 사용 되기 때문에 예제로 적당하다고 보고 사용했을 뿐이다. 어떤 앱을 위한 로그 시스템을 만든다고 가정하고, 프로그램이 시작될 때 설정을 읽어오는 부분을 작성한다. 로그 시스템을 위한 설정 파일의 예는 이것 : debug.log Finance Admin HR 2 로그 파일 이름과 로그 기록이 적용되어야 할 모듈 이름, 디버그 레벨이 있다. 로그 설정 정보를 저장할 구조체, debug_settings 구조체는 아래와 같이 작성한다. struct debug_settings { std::string m_file; // log filename int m_level; // debug level std::set m_modules; // modules where logging is enabled void load(const std::string &filename); void save(const std::string &filename); }; 남은 작업은 load()와 save()함수를 구현하는 것. load() 함수부터 시작한다. 에러 처리를 포함해도 7줄의 코드면 충분하다. #include #include // debug_settings 구조체에 xml 파일을 읽어온다. void debug_settings::load(const std::string &filename) { // 프로퍼티트리 선언, 속이 빈 트리 생성 using boost::property_tree::ptree; ptree pt; // 프로퍼티트리에 xml 파일을 읽어온다. 읽는 작업이 실패하면 // (파일 열기 실패, 파싱 실패) 예외처리를 던진다. read_xml(filename, pt); // 주목할 부분은 도트(점,'.')로 구분되는 키key 문자열 스트링을 사용해서 값을 읽어온다는 점. // 다른 구분자는 사용되지 않는다. // 'debug.filename' 키가 없으면 예외 리턴(던진다.) m_file = pt.get("debug.filename"); // m_level 변수에 디버그 레벨을 저장한다. // 다른 방식의 get() 메소드를 사용하는데, 값이 없는 경우에는 두번째 파라미터에 있는 기본값(default value)을 리턴한다. // 리턴되는 값의 형식의 이 두번째 파라미터에 의해 자동 결정 된다. 따라서 get() 대신 get()만으로 사용할 수 있다. m_level = pt.get("debug.level", 0); // 'debug.modules' 섹션을 반복 검색해서 모든 모듈 값을 m_modules 변수에 저장한다. // get_child() 함수는 특정 경로에 있는 자식 노드(child)의 레퍼런스를 리턴한다. // 값이 없는 경우, 자식 노드가 없는 경우, 예외를 던진다. // 프로퍼티 트리의 이터레이터(iterator)는 BidirectionalIterator 을 사용한다. BOOST_FOREACH(ptree::value_type &v, pt.get_child("debug.modules")) m_modules.insert(v.second.data()); } save()함수로, 마찬가지로 7줄이다. // debug_settings 구조체 내용을 xml파일로 저장한다. void debug_settings::save(const std::string &filename) { // 프로퍼티 트리 선언 using boost::property_tree::ptree; ptree pt; // 프로퍼티트리에 파일 이름을 기록 pt.put("debug.filename", m_file); // 디버그 레벨 값을 기록 예약 pt.put("debug.level", m_level); // 모든 모듈 이름을 프로퍼티 트리에 저장. // 주목할 점은, put() 함수는 새로운 값은 리스트의 마지막에 추가한다. // 이건 보통 괜찮지만, 저장 위치를 처음이나 중간에 하려고 했을 때는 // put_own()함수와 같이 사용해서 처리할 수 있다. BOOST_FOREACH(const std::string &name, m_modules) pt.add("debug.modules.module", name); // 파일로 저장. write_xml(filename, pt); } 예제 소스는 [[http://www.boost.org/doc/libs/1_51_0/libs/property_tree/examples/debug_settings.cpp|debug_settings.cpp]]에 있다. ====== Property Tree as a Container 컨테이너(Container)로서 프로퍼티트리 ====== 프로퍼티 트리는 특정 노드에서, 그 노드의 자식 노드를 바로 쓸 수 있도록 고안되었다. 즉, ptree 로 반복수행(iterating)할 때는 트리 전체가 아니라 한 단계의 노드씩 순행 된다는 것이다. 프로퍼티가 키에 의해 정렬된 것이 아니라는 것이 중요하다. 추가될 때의 순서대로 노드들이 구성된다. std::list와 비슷하다. 이름으로 자식 노드에 빨리 이동할 수 있는 것은 전혀 다른 색인 구조로 구성된다. 'binary_search'와 같이 정렬된 상태에서 쓰는 알고리즘을 사용 하면 안된다. 대신 프로퍼티 트리는 컨테이너와 유사한 '뷰'라는 인터페이스를 제공한다. 뷰의 이터레이터는 assoc_iterator 내부의 이터레이터 형식이다.(?) 결국, 정렬된 뷰를 얻을 수 있다. 이때 사용되는 함수는 ordered_begin()과 ordered_end(). The associative view also provides find() and equal_range() members, which return assoc_iterators, but otherwise have the same semantics as the members of std::map of the same name. You can get a normal iterator from an assoc_iterator by using the to_iterator() member function. Converting the other way is not possible. ====== How to Populate a Property Tree 프로퍼티 트리에 어떻게 저장되는가 ====== ===== XML Parser ===== 나중에 읽기 ===== JSON Parser ===== * JSON 오브젝트는 노드로 맵핑된다. 각 프로퍼티는 자식 노드가 된다. * JSON 배열도 노드로 매핑된다. 각 배열 항목 이름이 없는 자식 노드가 된다. 노드에 이름 있는 노드 없는 노드가 같이 있는 경우 JSON의 의도대로 맵핑 되지 않는다. * JSON의 값은 노드에 있는 값으로 저장 된다. 이때 타입 정보는 모두 삭제된다. "null", "true", "false" 와 같은 문자형식의 값도 모두 문자열로 저장된다. * 자식노드와 데이터를 같이 갖고 있는 프로퍼티 트리는 맵핑되지 않는다. 예시 JSON: { "menu": { "foo": true, "bar": "true", "value": 102.3E+06, "popup": [ {"value": "New", "onclick": "CreateNewDoc()"}, {"value": "Open", "onclick": "OpenDoc()"}, ] } } 프로퍼티 트리에는 이렇게 저장된다. (JSON의 진짜 장점을 살리는 건 아니네) menu { foo true bar true value 102.3E+06 popup { "" { value New onclick CreateNewDoc() } "" { value Open onclick OpenDoc() } } } ===== INI Parser ===== 나중에 읽기 ===== INFO Parser ===== 나중에 읽기