Using Boost to Read and Write Xml Files

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 the Name
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 your transform_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 using std::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 and WriteGraph 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



Leave a reply



Submit