ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Boost를 이용한 객체 직렬화(Object Serialization)
    C++ with Boost 2020. 7. 11. 16:12

    객체 직렬화

    객체 직렬화는 객체를 전송가능한 형태의 연속적인 데이터로 변형하는 것으로 파일로 저장하거나 전송할 때 사용된다.  객체를 연속적인 데이터로 변환하는 과정을 직렬화 연속적인 데이터를 객체로 변환하는 과정을 역직렬화라고 한다. 자바나 C# 은 객체 직렬화를 언어 자체에서 지원하지만 C++은 이를 지원하지 않는다.여기서는 boost의 serialization 컴포넌트를 사용해 직렬화를 구현하는 방법을 살펴본다.

     

    Boost 의 객체 직렬화 

    boost::text_oarchive는 객체를 받아 직렬화를 수행한 후 출력 스트림 객체에 전달한다. 반대로 text_iarchive 는 입력 스트림으로 직렬화된 데이터를 받아 객체로 변환한다.  

    Boost를 이용한 객체 직렬화 

    부스트의 텍스트 입출력 아카이브(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

    댓글

Designed by Tistory.