-
Boost를 이용한 객체 직렬화(Object Serialization)C++ with Boost 2020. 7. 11. 16:12
객체 직렬화
객체 직렬화는 객체를 전송가능한 형태의 연속적인 데이터로 변형하는 것으로 파일로 저장하거나 전송할 때 사용된다. 객체를 연속적인 데이터로 변환하는 과정을 직렬화 연속적인 데이터를 객체로 변환하는 과정을 역직렬화라고 한다. 자바나 C# 은 객체 직렬화를 언어 자체에서 지원하지만 C++은 이를 지원하지 않는다.여기서는 boost의 serialization 컴포넌트를 사용해 직렬화를 구현하는 방법을 살펴본다.
Boost 의 객체 직렬화
boost::text_oarchive는 객체를 받아 직렬화를 수행한 후 출력 스트림 객체에 전달한다. 반대로 text_iarchive 는 입력 스트림으로 직렬화된 데이터를 받아 객체로 변환한다.
부스트의 텍스트 입출력 아카이브(text_oarchive, text_iarchive)가 모든 객체를 직렬화하지는 않는다. 해당 객체가 직렬화 가능하려면 아래와 같이 클래스에 템플릿 함수 serialize()를 구현하고 이를 사용하는 boost::serialization::access 클래스가 이 함수에 접근할 수 있도록 해당 클래스를 friend로 선언해 주어야 한다.
class Serializable { private: friend class boost::serialization::access; template<typename Archive> void serialize(Archive& ar, const unsigned int version); }; template <typename Archive> void Serializable::serialize(Archive& ar, const unsigned int version) { //모든 멤버 변수를 Archive ar의 & 연산자의 인자로 전달 ar & 멤버 변수 ; }
위와 같이 정의된 객체를 직렬화하기 위해서는
1. 출력 스트림 객체를 boost::arcivie::text_oarchive에 참조로 전달하고
2. text_oarchive 객체의 << 연산의 인자로 직렬화할 객체를 넘겨 준다.
//직렬화를 위한 출력 스트링스트림 객체 std::ostringstream oss; //직렬화 수행을 위한 text_oarchive 직렬화결과는 oss에 데이터 스트림 boost::archive::text_oarchive oa(oss); //직렬화를 위해 객체를 text_oarchive 의 입력으로 전달 oa << c;
직렬화된 객체를 역직렬화하기 위해서는
1. 역직렬화를 위한 입력 데이터 스트림 객체를 text_iarchive 에 넘겨주고
2. text_iarchive 객체의 >> 연산을 통해 역직렬화 한다.
//역직렬화를 위한 입력 데이터 스트림 생성 (생성 시 직렬화된 데이터 스트림 전달) std::istringstream iss(oss.str()); //text_iarchive 생성 시 역직렬화를 위한 입력 데이터 스트림 지정 boost::archive::text_iarchive ia(iss); Serializable result; //text_iarchive >> 연산자를 이용해 역직렬화를 통한 객체 생성 ia >> result;
위와같이 Boost를 사용해 직렬화를 구현시 역직렬화 가능한 객체를 생성하는 방법과 text_oarchive, text_iarchive를 이용해 직렬화와 역직렬화를 수행할 수 있어야 한다.
아래는 Contact 객체를 파일에 직렬화하고 직렬화된 파일을 읽어와 Contact 객체를 역직렬화한 전체 예제 파일이다.
Address.h
#pragma once #include <iostream> #include <boost/serialization/access.hpp> class Address { public: Address() = default; Address(std::string street, std::string city, std::string post_code, int suite); friend std::ostream& operator << (std::ostream& os, const Address& rhs); private: std::string street, city, post_code; int suite; private: friend boost::serialization::access; template<typename Archive> void serialize(Archive& ar, const unsigned int version); }; //해당 멤버 변수에 대해 아카이브에 & 연산 수행 template<typename Archive> void Address::serialize(Archive& ar, const unsigned int version) { ar & street; ar & city; ar & post_code; ar & suite; } Address::Address(std::string street, std::string city, std::string post_code, int suite) :street{std::move(street)},city{std::move(city)}, post_code{std::move(post_code)}, suite{suite} {} std::ostream& operator << (std::ostream& os, const Address& rhs) { return os << rhs.street << ", " << rhs.city << "," << rhs.post_code << ", " << rhs.suite; }
Contact.h
#pragma once #include <iostream> #include <boost/serialization/serialization.hpp> #include "Address.h" class Contact { public: Contact() = default; Contact(std::string name, std::unique_ptr<Address> address); private: std::string name; //std::unique_ptr 직렬화 지원 std::unique_ptr<Address> address; friend std::ostream& operator << (std::ostream& os, const Contact& rhs); private: friend class boost::serialization::access; template <typename Archive> void serialize(Archive& ar, const unsigned int version); friend class PersistenceManager; }; Contact::Contact(std::string name, std::unique_ptr<Address> address) :name{std::move(name)}, address{std::move(address)} {} std::ostream& operator << (std::ostream& os, const Contact& rhs) { return os << rhs.name << '\n' << *rhs.address; } template <typename Archive> void Contact::serialize(Archive& ar, const unsigned int version) { ar & name; ar & address; }
PersistenceManager.h
#include <iostream> #include <fstream> #include <boost/archive/text_oarchive.hpp> #include <boost/archive/text_iarchive.hpp> #include <boost/serialization/unique_ptr.hpp> #include "Contact.h" //SRP(Single Responsibility Principle)을 지키기 위해 Contact 객체의 로드와 저장은 //PersistenceManger로 분리 struct PersistenceManager { static void save(const Contact& c) { //직렬화를 위한 파일스트림 생성 std::ofstream ofs(c.name + ".bin"); //text_oaarchive에 출력 파일 스트림 지정 boost::archive::text_oarchive oa(ofs); // 직렬화 수행 oa << c; } static Contact load(const std::string& name) { //역직렬화를 위한 파일 스트림 생성 std::ifstream ifs(name + ".bin"); //text_iarchive에 역직렬화를 위한 입력 스트림 지정 boost::archive::text_iarchive ia(ifs); Contact result; //result로 역직렬화 수행 ia >> result; return result; } };
main.cpp
#include <iostream> #include "Contact.h" #include "PersistenceManager.h" using namespace std; int main() { Contact nicki("nicki", std::make_unique<Address>("Jong-ro1", "Seoul", "03154", 1)); //직렬화 PersistenceManager::save(nicki); //역직렬화 Contact person = PersistenceManager::load("nicki"); std::cout << person << std::endl; }
CMakefile.txt
cmake_minimum_required(VERSION 3.16) project(contact) set(CMAKE_CXX_STANDARD 14) # boost 라이브러리 중 사용할 serialization 컴포넌트 지정 find_package(Boost 1.72 COMPONENTS serialization) if(Boost_FOUND) # 부스트 헤더 파일 include include_directories(${Boost_INCLUDE_DIRS}) endif() if(Boost_FOUND) add_executable(contact main.cpp Address.h Contact.h PersistenceManager.h) #boost serialization 컴포넌트 라이브러리 추가 target_link_libraries(contact ${Boost_LIBRARIES}) endif()
빌드 및 실행 결과
'C++ with Boost' 카테고리의 다른 글
macOS에서 부스트(Boost)라이브러리 사용하기 (0) 2020.07.10