Using Boost to read and write XML files
You should Try pugixml Light-weight, simple and fast XML parser for C++
The nicest thing about pugixml is the XPath support, which TinyXML and RapidXML lack.
Quoting RapidXML's author "I would like to thank Arseny Kapoulkine for his work on pugixml, which was an inspiration for this project" and "5% - 30% faster than pugixml, the fastest XML parser I know of" He had tested against version 0.3 of pugixml, which has reached recently version 0.42.
Here is an excerpt from pugixml documentation:
The main features are:
- low memory consumption and fragmentation (the win over pugxml is ~1.3 times, TinyXML - ~2.5 times, Xerces (DOM) - ~4.3 times 1). Exact numbers can be seen in Comparison with existing parsers section.
- extremely high parsing speed (the win over pugxml is ~6 times, TinyXML - ~10 times, Xerces-DOM - ~17.6 times 1
- extremely high parsing speed (well, I'm repeating myself, but it's so fast, that it outperforms Expat by 2.8 times on test XML) 2
- more or less standard-conformant (it will parse any standard-compliant file correctly, with the exception of DTD related issues)
- pretty much error-ignorant (it will not choke on something like You & Me, like expat will; it will parse files with data in wrong encoding; and so on)
- clean interface (a heavily refactored pugxml's one)
- more or less Unicode-aware (actually, it assumes UTF-8 encoding of the input data, though it will readily work with ANSI - no UTF-16 for now (see Future work), with helper conversion functions (UTF-8 <-> UTF-16/32 (whatever is the default for std::wstring & wchar_t))
- fully standard compliant C++ code (approved by Comeau strict mode); the library is multiplatform (see reference for platforms list)
- high flexibility. You can control many aspects of file parsing and DOM tree building via parsing options.
Okay, you might ask - what's the catch? Everything is so cute - it's small, fast, robust, clean solution for parsing XML. What is missing? Ok, we are fair developers - so here is a misfeature list:
- memory consumption. It beats every DOM-based parser that I know of - but when SAX parser comes, there is no chance. You can't process a 2 Gb XML file with less than 4 Gb of memory - and do it fast. Though pugixml behaves better, than all other DOM-based parser, so if you're stuck with DOM, it's not a problem.
- memory consumption. Ok, I'm repeating myself. Again. When other parsers will allow you to provide XML file in a constant storage (or even as a memory mapped area), pugixml will not. So you'll have to copy the entire data into a non-constant storage. Moreover, it should persist during the parser's lifetime (the reasons for that and more about lifetimes is written below). Again, if you're ok with DOM - it should not be a problem, because the overall memory consumption is less (well, though you'll need a contiguous chunk of memory, which can be a problem).
- lack of validation, DTD processing, XML namespaces, proper handling of encoding. If you need those - go take MSXML or XercesC or anything like that.
Boost and xml parsing
get_child
(and all the other path-based access functions) isn't very good at dealing with multiple identical keys. It will choose the first child with the given key and return that, ignoring all others.
But you don't need get_child
, because you already hold the node you want in your hand.
pt.get_child("applications")
gives you a ptree
. Iterating over that gives you a ptree::value_type
, which is a std::pair<std::string, ptree>
.
The first weird thing, then, is this line:
std::string key = v.first.data();
The data()
function you're calling here is std::string::data
, not ptree::data
. You could just write
std::string key = v.first;
The next strange thing is the comparison:
if (key == std::string("application"))
You don't need to explicitly construct a std::string
here. In fact, doing so is a pessimization, because it has to allocate a string buffer and copy the string there, when std::string
has comparison operators for C-style strings.
Then you iterator over pt.get_child("applications.application")
, but you don't need to do this lookup - v.second
is already the tree you want.
Furthermore, you don't need to iterate over the child at all, you can use its lookup functions to get what you need.
std::string pkgId = v.second.get("id", "");
So to sum up, this is the code I would write:
boost::property_tree::ptree pt;
boost::property_tree::read_xml(sModel, pt);
BOOST_FOREACH(boost::property_tree::ptree::value_type &v, pt.get_child("applications"))
{
// You can even omit this check if you can rely on all children
// being application nodes.
if (v.first == "application")
{
std::string pkgId = v.second.get("id", "");
std::string platform = v.second.get("platform", "");
std::string version = v.second.get("version", "");
}
}
Write/read vector of item in xml using boost::write_graphml
In response to the older question I had this:
A few approaches
Opaque Member Object
One way would be to treat the
student
property just like theName
property:dp.property("Name", boost::get(&GraphData::Name, graph));
dp.property("Student", boost::get(&GraphData::student, graph));
All that's required is you tell the standard library how to stream
Student
objects:inline static std::ostream& operator<<(std::ostream& os, Student const& s) {
return os << s.roll_no << " " << std::quoted(s.division);
}
inline static std::istream& operator>>(std::istream& is, Student& s) {
return is >> s.roll_no >> std::ws >> std::quoted(s.division);
}
You'll get XML Like Live On
Wandbox.<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns
http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
Mary-Anne Hornam
80 "alchemy"
Mary-Anne Bufgloon
57 "drama"
Joyce Preet
8 "drama"
Philomena Joyce
3 "drama"
James Tarsinck
78 "science"
Separate Properties
If you really want the separate GraphML properties
roll_no
and
division
, then you go back to yourtransform_value_property_map
:void WriteGraph(std::ostream &os, Graph &graph) {
boost::dynamic_properties dp;
auto roll_no = [](Student const& s) { return s.roll_no; };
auto division = [](Student const& s) { return s.division; };
dp.property("Name", get(&GraphData::Name, graph));
dp.property("roll_no", boost::make_transform_value_property_map(roll_no,
get(&GraphData::student, graph)));
dp.property("division", boost::make_transform_value_property_map(division,
get(&GraphData::student, graph)));boost::write_graphml(os, graph, dp, true);
}
Note that for reading purposes, a LvaluePropertyMap is required, so it
needs to be written slightly more permissive:Graph ReadGraph(std::string const &fileName) {
Graph graph;
boost::dynamic_properties dp;
auto roll_no = [](Student& s) ->auto& { return s.roll_no; };
auto division = [](Student& s) ->auto& { return s.division; };
dp.property("Name", get(&GraphData::Name, graph));
dp.property("roll_no", boost::make_transform_value_property_map(roll_no,
get(&GraphData::student, graph)));
dp.property("division", boost::make_transform_value_property_map(division,
get(&GraphData::student, graph)));std::ifstream file(fileName);
boost::read_graphml(file, graph, dp);
return graph;
}
Now, you'll get XML Like Live On
Wandbox.<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns
http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
Philomena Glinka
science
3
Philomena Preet
alchemy
84
John Habakuk
alchemy
19
Ernest Habakuk
philosophy
31
John Bufgloon
science
44
Simplifying
You can do without the lambdas and the make the differences between
write/read go away, by usingstd::mem_fn
:static inline boost::dynamic_properties DynProps(Graph& g) {
boost::dynamic_properties dp;
dp.property("Name", get(&GraphData::Name, g));
auto student = get(&GraphData::student, g);
dp.property("roll_no", make_transform_value_property_map(std::mem_fn(&Student::roll_no),
student));
dp.property("division", make_transform_value_property_map(std::mem_fn(&Student::division),
student));return dp;
}
Which can be used for
ReadGraph
andWriteGraph
as follows:Graph ReadGraph(std::string const &fileName) {
Graph graph;
auto dp = DynProps(graph);
std::ifstream file(fileName);
boost::read_graphml(file, graph, dp);
return graph;
}
void WriteGraph(std::ostream &os, Graph &graph) {
boost::write_graphml(os, graph, DynProps(graph), true);
}
You still get the same XML.
FULL LISTING
Live On Wandbox
#include <boost/graph/adjacency_list.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
#include <boost/graph/graphml.hpp>
struct Student {
int roll_no;
std::string division;
};
struct GraphData {
std::string Name;
Student student;
};
using Graph = boost::adjacency_list<boost::setS, boost::vecS, boost::directedS, GraphData>;
static inline boost::dynamic_properties DynProps(Graph& g) {
boost::dynamic_properties dp;
dp.property("Name", get(&GraphData::Name, g));
auto student = get(&GraphData::student, g);
dp.property("roll_no", make_transform_value_property_map(std::mem_fn(&Student::roll_no),
student));
dp.property("division", make_transform_value_property_map(std::mem_fn(&Student::division),
student));return dp;
}
Graph ReadGraph(std::string const &fileName) {
Graph graph;
auto dp = DynProps(graph);
std::ifstream file(fileName);
boost::read_graphml(file, graph, dp);
return graph;
}
void WriteGraph(std::ostream &os, Graph &graph) {
boost::write_graphml(os, graph, DynProps(graph), true);
}
void WriteGraph(std::string const& fileName, Graph &graph) {
std::ofstream ofs(fileName);
WriteGraph(ofs, graph);
}
#include <boost/graph/graph_utility.hpp>
namespace Gen { Graph graph(); } // generate random data
int main() {
{
Graph g = Gen::graph();
WriteGraph("input.txt", g);
}
Graph g = ReadGraph("input.txt");
print_graph(g, get(&GraphData::Name, g));
// or as XML
WriteGraph(std::cout << "==== XML version: ====\n\n", g);
}
/// generate data
#include <boost/graph/random.hpp>
#include <boost/random.hpp>
#include <random>
namespace Gen {
namespace {
namespace R = boost::random;
R::mt19937 engine {42}; // { std::random_device{}() };
template <typename Range> auto sample(Range const &from) {
return *std::next(boost::begin(from), R::uniform_int_distribution<>(1, boost::size(from))(engine) - 1);
}
int roll() { return R::uniform_int_distribution<>(1, 100)(engine); }
std::string division() {
static std::string const d[] = { "science", "drama", "mathematics", "philosophy", "alchemy" };
return sample(d);
}
std::string name() {
static std::string const f[] = { "John", "Daisy", "Chuck", "Mary-Anne", "Ernest", "Philomena", "Joyce", "James" };
static std::string const l[] = { "Joyce", "Habakuk", "Hornam", "Bufgloon", "Glinka", "Tarsinck", "Preet" };
return sample(f) + " " + sample(l);
}
Student student() { return { roll(), division() }; }
}
Graph graph() {
Graph g;
boost::generate_random_graph(g, 5, 7, engine);
for (auto vd: boost::make_iterator_range(vertices(g)))
g[vd] = { name(), student() };
return g;
}
} // namespace Gen
When You Have A Vector Member
Your current question adds vector
to the mix. There's a related post here: read boost graph (boost::read_graphviz) where vertex contains vector
You likely ran into the ADL trap with operator<<
/operator>>
for std::vector<T>
.
In case the above doesn't already solve the issue you were having, I'll add a demo later tonight.
Demonstration
This was trickier than I'd have hoped, because overloading operator<<
/operator>>
inside namespace ::std
is just poor taste, so we need a wrapper type:
using StudentInfo = std::vector<std::pair<std::string, int>>;
struct Wrapper {
StudentInfo& _si;
friend std::ostream& operator<<(std::ostream& os, const Wrapper sis) {
for(auto& pair : sis._si)
os << std::quoted(pair.first) << pair.second << ';';
return os;
}
friend std::istream& operator>>(std::istream& is, const Wrapper sis) {
StudentInfo::value_type pair;
while (is >> std::quoted(pair.first)) {
char ch;
if (is >> pair.second >> ch && ch == ';')
sis._si.push_back(pair);
else
return is;
}
if (!is.bad()) // eof this point is ok
is.clear();
return is;
}
};
Note, below code adds
", "
between name and age.
Now, some extra obstacles appear, since e.g. TransformValuePropertyMap is not an LValuePropertyMap unless the returned value is a mutable reference.
I opted to do a simple CoercePropertyMap to "wrap" a property with a a wrapping type:
template <typename T, typename Map> struct CoercePropertyMap : Map {
CoercePropertyMap(Map map) : Map(map){}
using value_type = T;
using reference = T;
};
template <typename T, typename Map>
CoercePropertyMap<T, Map> coerce_map(Map map) { return map; }
Now, we can put it all together:
dp.property("studentInfo", coerce_map(get(&GraphItem::studentInfo, g)));
Full Listing
Live On Wandbox
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphml.hpp>
#include <iostream>
#include <iomanip>
using StudentInfo = std::vector<std::pair<std::string, int>>;
namespace /*static*/ {
struct Lit { char ch; };
static inline std::istream& operator>>(std::istream& is, Lit expected) {
char actual;
if (is >> actual && actual != expected.ch)
is.setstate(std::ios::failbit);
return is;
}
static StudentInfo null_info;
struct Wrapper {
StudentInfo& _si;
Wrapper(StudentInfo& si = null_info) : _si(si) {}
operator StudentInfo&() const { return _si; }
friend std::ostream& operator<<(std::ostream& os, const Wrapper sis) {
for(auto& pair : sis._si)
os << std::quoted(pair.first) << ", " << pair.second << ';';
return os;
}
friend std::istream& operator>>(std::istream& is, const Wrapper sis) {
StudentInfo::value_type pair;
while (is >> std::skipws >> std::quoted(pair.first)) {
if (is >> Lit{','} >> pair.second >> Lit{';'})
sis._si.push_back(pair);
else
return is; // error here is bad
}
if (!is.bad()) // just eof this point is ok
is.clear();
return is;
}
};
template <typename T, typename Map> struct CoercePropertyMap : Map {
CoercePropertyMap(Map map) : Map(map){}
using value_type = T;
using reference = T;
};
template <typename T, typename Map>
CoercePropertyMap<T, Map> coerce_map(Map map) { return map; }
}
struct GraphItem {
std::string Division;
StudentInfo studentInfo;
};
using Graph = boost::adjacency_list<boost::setS, boost::vecS, boost::directedS, GraphItem>;
static inline boost::dynamic_properties DynProps(Graph& g) {
boost::dynamic_properties dp;
dp.property("Division", get(&GraphItem::Division, g));
dp.property("studentInfo", coerce_map<Wrapper>(get(&GraphItem::studentInfo, g)));
return dp;
}
Graph ReadGraph(std::string const &fileName) {
Graph graph;
auto dp = DynProps(graph);
std::ifstream file(fileName);
boost::read_graphml(file, graph, dp);
return graph;
}
void WriteGraph(std::ostream &os, Graph &graph) {
boost::write_graphml(os, graph, DynProps(graph), true);
}
void WriteGraph(std::string const& fileName, Graph &graph) {
std::ofstream ofs(fileName);
WriteGraph(ofs, graph);
}
#include <boost/graph/graph_utility.hpp>
namespace Gen { Graph graph(); } // generate random data
int main() {
{
Graph g = Gen::graph();
WriteGraph("input.txt", g);
}
Graph g = ReadGraph("input.txt");
print_graph(g, get(&GraphItem::Division, g));
// or as XML
WriteGraph(std::cout << "==== XML version: ====\n\n", g);
}
/// generate data
#include <boost/graph/random.hpp>
#include <boost/random.hpp>
#include <random>
namespace Gen {
namespace {
namespace R = boost::random;
R::mt19937 engine {42}; // { std::random_device{}() };
template <typename Range> auto sample(Range const &from) {
return *std::next(boost::begin(from), R::uniform_int_distribution<>(1, boost::size(from))(engine) - 1);
}
int age() { return R::uniform_int_distribution<>(18, 27)(engine); }
std::string division() {
static std::string const d[] = { "science", "drama", "mathematics", "philosophy", "alchemy" };
return sample(d);
}
std::string name() {
static std::string const f[] = { "John", "Daisy", "Chuck", "Mary-Anne", "Ernest", "Philomena", "Joyce", "James" };
static std::string const l[] = { "Joyce", "Habakuk", "Hornam", "Bufgloon", "Glinka", "Tarsinck", "Preet" };
return sample(f) + " " + sample(l);
}
StudentInfo studentInfo() {
StudentInfo si;
auto const n = R::uniform_int_distribution<>(2,5)(engine);
for (int i = 0; i < n; ++i)
si.emplace_back(name(), age());
return si;
}
}
Graph graph() {
Graph g;
boost::generate_random_graph(g, 5, 7, engine);
for (auto vd: boost::make_iterator_range(vertices(g)))
g[vd] = { division(), studentInfo() };
return g;
}
} // namespace Gen
Prints
philosophy --> drama
drama --> mathematics
drama --> philosophy
mathematics --> philosophy drama
drama --> philosophy drama
==== XML version: ====
<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<key id="key0" for="node" attr.name="Division" attr.type="string" />
<key id="key1" for="node" attr.name="studentInfo" attr.type="string" />
<graph id="G" edgedefault="directed" parse.nodeids="canonical" parse.edgeids="canonical" parse.order="nodesfirst">
<node id="n0">
<data key="key0">philosophy</data>
<data key="key1">"James Joyce", 18;"James Tarsinck", 25;"Daisy Joyce", 20;"Ernest Habakuk", 27;</data>
</node>
<node id="n1">
<data key="key0">drama</data>
<data key="key1">"James Joyce", 18;"James Tarsinck", 25;"Daisy Joyce", 20;"Ernest Habakuk", 27;"Mary-Anne Joyce", 23;"Ernest Hornam", 18;"Daisy Hornam", 24;"James Hornam", 18;</data>
</node>
<node id="n2">
<data key="key0">drama</data>
<data key="key1">"James Joyce", 18;"James Tarsinck", 25;"Daisy Joyce", 20;"Ernest Habakuk", 27;"Mary-Anne Joyce", 23;"Ernest Hornam", 18;"Daisy Hornam", 24;"James Hornam", 18;"Joyce Joyce", 22;"Mary-Anne Habakuk", 24;</data>
</node>
<node id="n3">
<data key="key0">mathematics</data>
<data key="key1">"James Joyce", 18;"James Tarsinck", 25;"Daisy Joyce", 20;"Ernest Habakuk", 27;"Mary-Anne Joyce", 23;"Ernest Hornam", 18;"Daisy Hornam", 24;"James Hornam", 18;"Joyce Joyce", 22;"Mary-Anne Habakuk", 24;"John Bufgloon", 23;"Philomena Glinka", 26;"John Bufgloon", 19;"James Preet", 18;"Joyce Bufgloon", 27;</data>
</node>
<node id="n4">
<data key="key0">drama</data>
<data key="key1">"James Joyce", 18;"James Tarsinck", 25;"Daisy Joyce", 20;"Ernest Habakuk", 27;"Mary-Anne Joyce", 23;"Ernest Hornam", 18;"Daisy Hornam", 24;"James Hornam", 18;"Joyce Joyce", 22;"Mary-Anne Habakuk", 24;"John Bufgloon", 23;"Philomena Glinka", 26;"John Bufgloon", 19;"James Preet", 18;"Joyce Bufgloon", 27;"Daisy Joyce", 18;"Mary-Anne Habakuk", 24;"Ernest Joyce", 24;</data>
</node>
<edge id="e0" source="n0" target="n2">
</edge>
<edge id="e1" source="n1" target="n3">
</edge>
<edge id="e2" source="n2" target="n0">
</edge>
<edge id="e3" source="n3" target="n0">
</edge>
<edge id="e4" source="n3" target="n2">
</edge>
<edge id="e5" source="n4" target="n0">
</edge>
<edge id="e6" source="n4" target="n1">
</edge>
</graph>
</graphml>
Parsing XML File with Boost C++
Boost Documentation:
The attributes of an XML element are stored in the subkey . There is one child node per attribute in the attribute node. Existence of the node is not guaranteed or necessary when there are no attributes.
<module value = "abc"/>
//One way would be this:
boost::get<std::string>("module.<xmlattr>.value");
One more way (untested), which appears to be better:
BOOST_FOREACH(boost::property_tree::ptree::value_type &v, pt.get_child("a.modules"))
{
std::cout << v.second.get_child("<xmlattr>.type").data() << std::endl;
std::cout << v.second.get_child("<xmlattr>.Reference").data() << std::endl;
}
One more taken from here.
//Parse XML...
BOOST_FOREACH(boost::property_tree::ptree::value_type &v, pt.get_child("a.modules"))
{
const boost::property_tree::ptree &attributes = v.second.get_child("<xmlattr>", boost::property_tree::ptree());
BOOST_FOREACH(const boost::property_tree::ptree::value_type &v, attributes)
{
std::cout << v.first.data() << std::endl;
std::cout << v.second.data() << std::endl;
}
}
How to use Boost XML Parser
Boost does not have an XML library.
Boost Serialization doesn't read general XML (like) documents. It reads XML archives only. Meaning, you don't control the XML.
That all said, you can simply extend the class with another Data member, and it would accidentally result in a very similar XML archive. Perhaps this is good enough for you:
Live On Coliru
#include <boost/archive/xml_oarchive.hpp>
class Data
{
public:
int Degrees;
int Minutes;
float Seconds;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int /*version*/){
ar & BOOST_SERIALIZATION_NVP(Degrees);
ar & BOOST_SERIALIZATION_NVP(Minutes);
ar & BOOST_SERIALIZATION_NVP(Seconds);
}
};
class Position
{
public:
// every serializable class needs a constructor
Position() {
Degrees = 0;
Minutes = 0;
Seconds = 0;
};
Position(int degrees, int minutes, float seconds){
Degrees = degrees;
Minutes = minutes;
Seconds = seconds;
};
int Degrees;
int Minutes;
float Seconds;
Data data;
Data data2;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int /*version*/){
ar & BOOST_SERIALIZATION_NVP(Degrees);
ar & BOOST_SERIALIZATION_NVP(Minutes);
ar & BOOST_SERIALIZATION_NVP(Seconds);
ar & boost::serialization::make_nvp("data", data);
ar & boost::serialization::make_nvp("data", data2);
}
};
#include <fstream>
int main() {
Position position;
position.Degrees = 1;
position.Minutes = 2;
position.Seconds = 3;
position.data = {1,2,3};
position.data2 = {4,5,6};
{
std::ofstream ofs("output.xml");
boost::archive::xml_oarchive oa(ofs);
oa << BOOST_SERIALIZATION_NVP(position);
}
}
Writes
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="14">
<position class_id="0" tracking_level="0" version="0">
<Degrees>1</Degrees>
<Minutes>2</Minutes>
<Seconds>3.000000000e+00</Seconds>
<data class_id="1" tracking_level="0" version="0">
<Degrees>1</Degrees>
<Minutes>2</Minutes>
<Seconds>3.000000000e+00</Seconds>
</data>
<data>
<Degrees>4</Degrees>
<Minutes>5</Minutes>
<Seconds>6.000000000e+00</Seconds>
</data>
</position>
</boost_serialization>
UPDATE
If you DIDN'T want to control the XML, all the better. Just let the library do what you want it to do:
Live On Coliru
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/serialization/vector.hpp>
class Data
{
public:
int Degrees;
int Minutes;
float Seconds;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int /*version*/){
ar & BOOST_SERIALIZATION_NVP(Degrees);
ar & BOOST_SERIALIZATION_NVP(Minutes);
ar & BOOST_SERIALIZATION_NVP(Seconds);
}
};
class Position
{
public:
// every serializable class needs a constructor
Position() {
Degrees = 0;
Minutes = 0;
Seconds = 0;
};
Position(int degrees, int minutes, float seconds){
Degrees = degrees;
Minutes = minutes;
Seconds = seconds;
};
int Degrees;
int Minutes;
float Seconds;
std::vector<Data> data;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int /*version*/){
ar & BOOST_SERIALIZATION_NVP(Degrees);
ar & BOOST_SERIALIZATION_NVP(Minutes);
ar & BOOST_SERIALIZATION_NVP(Seconds);
ar & boost::serialization::make_nvp("data", data);
}
};
#include <fstream>
int main() {
{
Position position;
position.Degrees = 1;
position.Minutes = 2;
position.Seconds = 3;
Related Topics
How Boost::Function and Boost::Bind Work
How to Create a Function Dynamically, During Runtime in C++
Convert Rgb to Black & White in Opencv
C++11 Equivalent to Boost Shared_Mutex
Changing Function Access Mode in Derived Class
Why Lifetime of Temporary Doesn't Extend Till Lifetime of Enclosing Object
Why Is Modifying a String Through a Retrieved Pointer to Its Data Not Allowed
Rationale of Enforcing Some Operators to Be Members
How to Implement a Natural Sort Algorithm in C++
Mesh Class Called with Default Constructor Not Working Opengl C++
Std::String Length() and Size() Member Functions
How to Use SQLite in a Multi-Threaded Application
What Is the Underlying Data Structure of a Stl Set in C++
Cmake: Include Library Dependencies in a Static Library
Inheritance or Composition: Rely on "Is-A" and "Has-A"
Cuda How to Get Grid, Block, Thread Size and Parallalize Non Square Matrix Calculation
Inheritance and Templates in C++ - Why Are Inherited Members Invisible