Narzędzia użytkownika

Narzędzia witryny


serialization

Biblioteka boost:serialization

Autor : Marcin Kocoń, G1ISI — Marcin Kocoń 2008/04/14 11:36

Opis biblioteki

Poprzez serializację rozumiemy zapis instancji dowolnej klasy do pliku w sposób umożliwiający jej późniejszą rekonstrukcję.

Biblioteka boost:serialization umożliwia automatyczną (tzn. bez jakieś większej naszej ingerencji) serializację wybranych atrybutów. Dzięki wbudowanych funkcjom tej biblioteki jesteśmy w stanie w prosty (jednakże czasem mniej lub bardziej zróżnicowany) sposób dokonać serializacji następujących elementów :

  • składowych prywantych w sposób ingerenujący w składowe wewnętrzne klasy
  • składowych publicznych w sposób nieingerenujący w składowe wewnętrzne klasy
  • składowych prywatnych poprzez zestaw Set'ów/Get'ów i rozdzielenie funkcji serializacji na save/load
  • STL-owskich kolekcji w sposób zupełnie automatyczny
  • wskaźników (a raczej elementów na które te wskaźniki wskazują)
  • tablic elementów
  • i wiele, wiele innych…

Więcej informacji o dokładnym działaniu biblioteki na stronie Boost:serialization

Opis programu testowego

Program testuje najważniejsze funkcje wchodzące w skład pakietu boost:serialization. Mamy tutaj zawarte :

  • klasę bazową budynek i dziedziczącą po niej klasę akademik
  • klasę student, której instancje wchodzą w skład STL-owskiej kolekcji, będącej elementem klasy akademik
  • test działania metody serialize, będącej elementem klas akademik i budynek
  • rozdzielenie metody serialize na load/save (+split_free) dla zapisu elementów klasy student
  • zapis do prostego archiwum tekstowego całej instancji klasy akademik, a następnie odtworzenie z tego pliku zserializowanych atrybutów

Kod źródłowy programu testowego

// Praca domowa ZPR 2008L
// Marcin Kocoń
// temat : biblioteka boost:serialization
 
#include <fstream>
#include <iostream>
// bedziemy zapisywac proste archwium w postaci tekstowej
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
// potrzebne do obslugi serializacji klas pochodnych
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/deque.hpp>
// nagłówek zawierający metodę(makro) do rozdzielenia funkcji serializacji
#include <boost/serialization/split_free.hpp>
#include <exception>
 
//	Klasa opisująca studenta
class student
{
private:
	std::string imie;
	std::string nazwisko;
public:
	student() {}
	student(const std::string& _imie, const std::string& _nazwisko){
		imie = _imie;
		nazwisko = _nazwisko;
	}
 
	// Zestaw Set'ów i Get'ów między innymi do obsługi serializacji dla atrybutów prywatnych
	std::string& getImie() { return imie; }
	std::string& getNazwisko() { return nazwisko; }
	void setImie(const std::string& _imie) { imie = _imie; }
	void setNazwisko(const std::string& _nazwisko) {nazwisko = _nazwisko; }
 
};
 
// Metoda serializacji nie będąca elementem klasy. Umożliwia zapisz/odczyt atrybutów 
// prywatnych. Jednakże, aby tego dokonać potrzeba jest rozczepienie domyślnej
// metody serialize na dwie : save i load, a następnie połączenie ich w całość
namespace boost {
	namespace serialization {
 
		template<class Archive>
		void save(Archive & ar, const student& g, const unsigned int version)
		{
			student g2 = const_cast<student&>(g);	// usunięcie const, aby można było wywołać metodę getXXX
			ar << g2.getImie();
			ar << g2.getNazwisko();
		}
		template<class Archive>
		void load(Archive & ar, student& g, const unsigned int version)
		{
			std::string s1, s2;
			ar >> s1;
			ar >> s2;
			g.setImie(s1);
			g.setNazwisko(s2);
		}
 
		template<class Archive>
		inline void serialize(Archive & ar,	student& g, const unsigned int version){
			// złączenie load/save -> serialize
			split_free(ar, g, version); 
		}
 
	} // namespace serialization
} // namespace boost
 
 
// Klasa opisująca budynek - klasa bazowa
class budynek
{
private:
	// dzięki temu metoda serializacji ma dostęp do atrybutów prywatnych
	friend class boost::serialization::access;
 
	template <class Archive>									// te dwie linijki są wzorcem, z którego
	void serialize(Archive& ar, const unsigned int version)		// korzystamy zawsze przy serializacji
    {
		ar & identyfikator;		// poszczególne atrybuty są dodawane do archiwum
		ar & nazwa;				// poprzez użycie operatora '&' na archiwum
        ar & miasto; 
    }
	std::string miasto;
	std::string nazwa;
	int identyfikator;
protected:
	budynek(const int _id, const std::string& _nazwa, const std::string& _miasto) 
		: identyfikator(_id), nazwa(_nazwa), miasto(_miasto) {}
 
public:
	std::string& getMiasto() { return miasto; }
	std::string& getNazwa() { return nazwa; }
	int getId() { return identyfikator; }
	budynek() {}
	virtual ~budynek() {}
};
 
 
// Klasa pochodna akademik, dziedzicząca po budynku
class akademik : public budynek
{
private:
	// Standardowo dostęp do metod prywatnych dla serializacji
	friend class boost::serialization::access;
 
	template<class Archive>										// metoda serializacji
    void serialize(Archive & ar, const unsigned int version)
    {
        // Zserializuj atrybuty z klasy bazowej
        ar & boost::serialization::base_object<budynek>(*this);
		// Kolekcje STL-owskie są automatycznie serializowane.
		// W podobny sposób działa także serializacja wskaźników
		// (= obiektów, na które wskazują)
		ar & studenci;
    }
	std::deque<student *> studenci;	// Standardowa kolejka STL-owska
 
public:
	akademik() {}
	akademik(const int _id, const std::string& _nazwa, const std::string& _miasto)
		: budynek(_id, _nazwa, _miasto) {}
 
	// Umożliwia dodanie studenta do kolekcji studentów
	void dodajStudenta(student* const _stud)
	{
		studenci.push_back(_stud);
	}
	// Zwraca ilość studentów w akademiku
	const int getIlStud() { return studenci.size(); }
	// Podaje dane studenta
	student* const podajDaneStudentaNr(const int _nr)
	{
		return studenci[_nr];
	}
 
	virtual ~akademik() {}
 
};
 
void zapisz_akademik(const akademik &s, const char* filename)
{
	try{
		// stworz archiwum
		std::ofstream ofs(filename);
		// zapisz w postaci archiwum tekstowego
		boost::archive::text_oarchive oa(ofs);
		oa << s;
	}
	catch(std::exception e) { std::cout << e.what() << std::endl; exit(1); }
}
 
void wczytaj_akademik(akademik &s, const char* filename)
{
	try{
		// otwórz archiwum
		std::ifstream ifs(filename);
		boost::archive::text_iarchive ia(ifs);
		// przywroc dane z uprzednio zapisanego archiwum
		ia >> s;
	}
	catch(std::exception e) { 
		std::cout << e.what() << std::endl; exit(1); }
}
 
 
int main()
{
	const char* filename = "ala_i_kot.dat";
	// Nowy akademik + dwoch studentow
	akademik* ak1 = new akademik(1, "Riviera", "Warszawa");
	ak1->dodajStudenta(new student("Jacek", "Jankowski"));
	ak1->dodajStudenta(new student("Adam", "Jakubowski"));
 
	//----- Zapis do archiwum -----
	{
		zapisz_akademik(*ak1, filename);
	} // archiwum i output stream zamykane podczas wywolywania destruktora
 
	delete ak1;
	// Jakis czas pozniej....
 
	akademik nowy;
 
	// ----- Odczyt z archiwum -----
	{
		wczytaj_akademik(nowy, filename);
	}
 
	std::cout << "Nowy akademik : " << nowy.getId() << ", " << nowy.getNazwa() 
		<< ", " << nowy.getMiasto() << ", " << nowy.getIlStud() << "\n";
	std::cout << "Mieszkancy : \n";
	for (int i=0; i< nowy.getIlStud(); ++i)
	{
		std::cout << "[" << i << "] Imie : " << nowy.podajDaneStudentaNr(i)->getImie();
		std::cout << " Nazwisko : " << nowy.podajDaneStudentaNr(i)->getNazwisko() << "\n";
	}
	std::cout << "\n\n";
 
	return 0;
}
serialization.txt · ostatnio zmienione: 2008/04/14 14:53 przez mkocon