Catching overflows with boost::lexical_cast - c++

I want to catch boost::lexicat_cast overflows the same way I can catch boost::numeric_cast overflows. Is it possible?
The first try block below throws a boost::numeric::negative_overflow.
The second block does not throw an exception (isn't this a lexical_cast bug?)
Though unsigned int is used in the example below, I am looking for a method that would work for any integer type.
#include <boost/numeric/conversion/cast.hpp>
#include <boost/lexical_cast.hpp>
int main()
{
unsigned int i;
try
{
int d =-23;
i = boost::numeric_cast<unsigned int>(d);
}
catch (const boost::numeric::bad_numeric_cast& e)
{
std::cout << e.what() << std::endl;
}
std::cout << i << std::endl; // 4294967273
try
{
char c[] = "-23";
i = boost::lexical_cast<unsigned int>(c);
}
catch (const boost::bad_lexical_cast& e)
{
std::cout << e.what() << std::endl;
}
std::cout << i << std::endl; // 4294967273
return 0;
}

You could write what you want using a modicum of Spirit:
Live On Coliru
#include <boost/spirit/include/qi.hpp>
#include <iostream>
template <typename Out, typename In> Out numeric_lexical_cast(In const& range) {
Out value;
{
using namespace boost::spirit::qi;
using std::begin;
using std::end;
if (!parse(begin(range), end(range), auto_ >> eoi, value)) {
struct bad_numeric_lexical_cast : std::domain_error {
bad_numeric_lexical_cast() : std::domain_error("bad_numeric_lexical_cast") {}
};
throw bad_numeric_lexical_cast();
}
}
return value;
}
int main()
{
for (std::string const& input : { "23", "-23" }) try {
std::cout << " == input: " << input << " -> ";
auto i = numeric_lexical_cast<unsigned int>(input);
std::cout << i << std::endl;
} catch (std::exception const& e) {
std::cout << e.what() << std::endl;
}
}
Prints
== input: 23 -> 23
== input: -23 -> bad_numeric_lexical_cast

Related

c++ exception: override what function does not return anything

In the following programm std::cout << ex.what() << std::endl; does not print anything.
What's the problem with this code?
#include <iostream>
#include <stdexcept>
#include <string>
#include <sstream>
#include <cstring>
class Interval_OutOfRange : public std::out_of_range {
public:
std::stringstream ss;
Interval_OutOfRange(int min, int max, int value)
: std::out_of_range{"interval value out of range"}
{
ss << value << " is not inside intervall [" << min << ", " << max << "]";
}
char const * what() const noexcept override {
return ss.str().c_str();
}
};
template<int Min, int Max>
class Interval {
public:
Interval(int value) : value_{value} {
throw Interval_OutOfRange{Min, Max, value};
}
auto get() const { return value_; }
auto set(int value) { value_ = value; }
private:
int value_;
};
int main() {
try {
Interval<7, 17> obj(19);
}
catch (Interval_OutOfRange& ex) {
std::cout << ex.what() << std::endl;
}
}

getting "input stream error" when trying to desirealize the object using boost::serialization and arcive

I'm trying to send a class over boost::message queue using boost::serialization, boost::Arcive, and boost::split members (load and save)
the problem is when I'm trying to deserialize I'm getting the input stream error exception
#include <iostream>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/version.hpp>
#include <random>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/access.hpp>
#include <boost/archive/impl/basic_text_oarchive.ipp>
#include <boost/archive/impl/text_oarchive_impl.ipp>
#include <boost/archive/impl/text_iarchive_impl.ipp>
#include <boost/serialization/split_member.hpp>
class Data{
public:
int a_ ;
double b_ ;
std::string s ;
template<class Archive>
void serialize(Archive & ar, const unsigned int version) {
boost::serialization::split_member(ar, *this, version);
}
template<class Archive> void save(Archive & ar, unsigned int version) const {
// ar << order_request_type_;
ar << a_;
ar << b_;
ar << s; }
template<class Archive> void load(Archive & ar, unsigned int version) {
// ar >> order_request_type_;
ar >> a_;
ar >> b_;
ar >> s;
}
private:
// friend class boost::archive::access;
friend class boost::archive::save_access;
};
[[nodiscard]]bool RunChiled() {
using namespace boost::interprocess; try {
message_queue mq(open_only //open or create
, "message_queue" //name
);
unsigned int priority = 0;
message_queue::size_type recvd_size;
Data d;
std::stringstream iss;
std::string serialized_string;
serialized_string.resize(150);
long long number = 0;
while(true)
{
mq.receive(&serialized_string[0], 150, recvd_size , priority);
std::cout << serialized_string << "\n";
iss << serialized_string;
try{
boost::archive::text_iarchive ia(iss); // <-- getting the exception
ia >> d;
} catch (const std::exception& ex) {
std::cout << ex.what() << "\n";
}
number++;
std::cout << d.a_ << " " << d.b_ << " " << d.s << "\n";
} }catch(const interprocess_exception &ex) {
message_queue::remove("message_queue");
std::cout << "interprocess_exception " << ex.what() << std::endl;
return 1; } catch (const std::exception& e) {
std::cout << "exception " << e.what() << std::endl;
message_queue::remove("message_queue");
return 1; }
message_queue::remove("message_queue"); return true; }
int main() { std::cout << "1\n"; std::default_random_engine generator; std::uniform_real_distribution<double> distribution(0,15);
using namespace std;
cout << "Boost version: " << BOOST_LIB_VERSION << endl; using namespace boost::interprocess; message_queue::remove("message_queue"); auto pid = fork();
if(pid > 0) {
std::cout << "2\n";
sleep(2);
try {
auto res = RunChiled();
std::cout << res;
} catch (...) {
std::cout << "error\n";
}
} else if(pid == 0) {
try{
boost::interprocess::message_queue mq(create_only,"message_queue", 100, 150);
std::stringstream oss;
Data request;
request.b_ = 17.5;
request.a_ = I;
request.s = to_string(17.5) + " " + to_string(i);
try {
boost::archive::text_oarchive oa(oss);
oa << request;
} catch (const std::exception& e) {
std::cout << "serialzation:" << e.what() ;
}
try{
// std::cout << "oss " << oss.str() << "\n";
std::string serialized_string(oss.str());
std::cout << "serialized_string " << oss.str().size() << "\n";
mq.send(&serialized_string, serialized_string.size(), 0);
}catch(const std::exception& e){
std::cout << "\n send exeption " << e.what() << "\n";
}
}
}catch (const std::exception& e){
message_queue::remove("message_queue");
std::cout << e.what() ;
}
}
return 0;
}
A number of big issues.
Firstly
mq.send(&serialized_string, serialized_string.size(), 0);
That's Undefined
Behaviour because
serialzed_string isn't POD and the size doesn't match either. You
probbably meant something like on the receive side:
mq.send(serialized_string.data(), serialized_string.size(), 0);
You're resizing your serialized_string message to 150, and never
back to the actual size. This means it will not work correctly as
there will be trailing data.
Fixing it:
std::string buffer(buffer_size, '\0');
unsigned int priority = 0;
message_queue::size_type recvd_size;
mq.receive(buffer.data(), buffer.size(), recvd_size, priority);
buffer.resize(recvd_size);
Other Notes
The serialization can be simpler without splitting:
class Data {
public:
int a_;
double b_;
std::string s;
template <class Archive> void serialize(Archive& ar, unsigned) {
ar & a_ & b_ & s;
}
private:
friend class boost::serialization::access; // not required
};
Other notes
return 1 and return true from bool runChiled look iffy - one of them is probably a bug
[[nodiscard]] seems a little bit tricky on a function that normally will not return ([[noreturn]] might be more apt, optionally just passing exceptions out?)
you will want to seed your random generator with something actually random:
std::default_random_engine generator { std::random_device{}() };
Live Demo With Fixes
Live On Coliru
Live On Wandbox
#include <iostream>
#include <iomanip>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/date_time.hpp>
#include <boost/core/demangle.hpp>
#include <boost/version.hpp>
#include <random>
#include <thread> // this_thread
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/split_member.hpp>
namespace {
namespace bip = boost::interprocess;
using bip::message_queue;
auto constexpr queuename = "message_queue";
auto constexpr max_queued = 5;
auto constexpr buffer_size = 150;
auto sleep_for = [](auto d) { std::this_thread::sleep_for(d); };
using namespace std::chrono_literals;
}
class Data {
public:
int a_{};
double b_{};
std::string s;
template <class Ar> void serialize(Ar& ar, unsigned /*unused*/) {
ar& a_& b_& s;
}
};
[[noreturn]] void RunChild()
{
std::cout << "2" << std::endl;
sleep_for(2s);
try {
message_queue mq(bip::open_only,queuename);
size_t number = 0;
for (std::string buffer(buffer_size, '\0');; buffer.resize(buffer_size)) {
{
unsigned int priority = 0;
message_queue::size_type recvd_size = 0;
#ifdef COLIRU
// make the process terminate for online compiler
{
using namespace boost::posix_time;
auto deadline = second_clock::universal_time() + seconds(3);
if (not mq.timed_receive(buffer.data(), buffer.size(),
recvd_size, priority, deadline))
{
throw std::runtime_error("no more messages");
}
}
#else
mq.receive(buffer.data(), buffer.size(), recvd_size, priority);
#endif
buffer.resize(recvd_size);
}
//std::cout << buffer << std::endl;
Data d;
try {
std::stringstream iss(buffer);
boost::archive::text_iarchive ia(iss);
ia >> d;
} catch (const std::exception& ex) {
std::cout << ex.what() << std::endl;
}
++number;
std::cout << "Received: " << d.a_ << " " << d.b_ << " "
<< std::quoted(d.s) << std::endl;
}
} catch (const std::exception& e) {
std::cout << boost::core::demangle(typeid(e).name()) << " " << e.what()
<< std::endl;
message_queue::remove(queuename);
throw; // re-raise
}
message_queue::remove(queuename);
}
static void RunParent()
{
std::cout << "1" << std::endl;
std::default_random_engine generator { std::random_device{}() };
std::uniform_real_distribution<double> distribution(0, 15);
message_queue mq(bip::create_only, queuename, max_queued, buffer_size);
for (auto i = 0; i < 10; ++i) {
auto value = distribution(generator);
Data const request {
i,
value,
std::to_string(value) + " " + std::to_string(i)
};
std::stringstream oss;
try {
boost::archive::text_oarchive oa(oss);
oa << request;
std::string buffer = std::move(oss).str();
std::cout << "Sending " << buffer.size() << " bytes" << std::endl;
mq.send(buffer.data(), buffer.size(), 0);
} catch (const std::exception& e) {
std::cout << "\nsend exeption " << e.what() << std::endl;
}
}
}
int main() {
std::cout << "Boost version: " << BOOST_LIB_VERSION << std::endl;
message_queue::remove(queuename);
try {
if (auto pid = ::fork(); pid > 0) {
RunChild();
} else if (pid == 0) {
RunParent();
}
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
message_queue::remove(queuename);
}
}
Prints
Boost version: 1_75
2
1
Sending 72 bytes
Sending 73 bytes
Sending 72 bytes
Sending 72 bytes
Sending 73 bytes
Sending 72 bytes
Sending 72 bytes
Received: 0 1.12642 "1.126420 0"
Received: 1 14.2474 "14.247412 1"
Received: 2 3.22163 "3.221631 2"
Sending 73 bytes
Sending 72 bytes
Received: 3 3.20471 "3.204709 3"
Sending 72 bytes
Received: 4 10.7838 "10.783761 4"
Received: 5 5.74063 "5.740629 5"
Received: 6 6.98008 "6.980078 6"
Received: 7 11.6643 "11.664257 7"
Received: 8 3.80561 "3.805614 8"
Received: 9 7.79641 "7.796408 9"
std::runtime_error no more messages
no more messages

Spirit X3 not throwing expectation failure

From the documentation, it said says that when I use the expect operator, I should get an expectation_failure when the operator fails to match. I want to catch the exception to instruct the user where the erroneous input is. But it seems I get some kind of wrapped exception instead:
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::spirit::x3::expectation_failure<__gnu_cxx::__normal_iterator<char*, std::string> > > >'
what(): boost::spirit::x3::expectation_failure
Aborted
My catch statements are:
try {
r = parse(iter, end, wctl_parser::entry, root);
} catch (x3::expectation_failure<char const*> const& x) {
std::cout << "Never runs,";
} catch (x3::expectation_failure<std::string::const_iterator> const& e) {
std::cout << "me neither" << std::endl;
}
Update: Here is a small program exhibiting the behaviour:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/spirit/include/qi_char_class.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>
#include <string>
#include <vector>
namespace wccs_parser {
namespace x3 = boost::spirit::x3;
namespace ascii = x3::ascii;
namespace qi = boost::spirit::qi;
struct AstNullProcess;
struct AstChoiceProcess;
using AstAnyProcess = x3::variant<
x3::forward_ast<AstNullProcess>,
x3::forward_ast<AstChoiceProcess>
>;
struct AstNullProcess {};
struct AstChoiceProcess {
AstAnyProcess left;
AstAnyProcess right;
};
} // End namespace
BOOST_FUSION_ADAPT_STRUCT(wccs_parser::AstChoiceProcess, left, right)
namespace wccs_parser {
template <typename T> auto rule = [](const char* name = typeid(T).name()) {
struct _{};
return x3::rule<_, T> {name};
};
template <typename T> auto as = [](auto p) { return rule<T>() = p; };
auto nullProcess = as<AstNullProcess>(x3::omit['0']);
auto const choiceActual = as<AstChoiceProcess> (nullProcess > '+' > nullProcess);
auto const choice = rule<AstAnyProcess> ("choice")
= nullProcess >> !x3::lit('+')
| choiceActual;
auto const entry = x3::skip(ascii::space) [choice];
} //End namespace
namespace x3 = boost::spirit::x3;
int main() {
std::string str("0 + ");
wccs_parser::AstAnyProcess root;
auto iter = str.begin();
auto end = str.end();
bool r = false;
try {
r = parse(iter, end, wccs_parser::entry, root);
} catch (x3::expectation_failure<char const*> const& x) {
std::cout << "Never runs," << std::endl;
} catch (x3::expectation_failure<std::string::const_iterator> const& e) {
std::cout << "me neither" << std::endl;
}
if (r) {
std::cout << str << std::endl << std::endl << " Parses OK: " << std::endl;
std::cout << "\n-------------------------\n";
} else {
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
if (iter != end) std::cout << "Partial match" << std::endl;
return 0;
}
If you catch by const_iterator, you will want to make sure const_iterator is what you pass to parse:
std::string const str("0 + ");
See it Live On Coliru
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/spirit/include/qi_char_class.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>
#include <string>
#include <vector>
namespace wccs_parser {
using boost::spirit::x3::variant;
using boost::spirit::x3::forward_ast;
struct AstNullProcess;
struct AstChoiceProcess;
using AstAnyProcess = variant<
forward_ast<AstNullProcess>,
forward_ast<AstChoiceProcess>
>;
struct AstNullProcess {};
struct AstChoiceProcess {
AstAnyProcess left;
AstAnyProcess right;
};
} // End namespace
BOOST_FUSION_ADAPT_STRUCT(wccs_parser::AstChoiceProcess, left, right)
namespace wccs_parser {
namespace x3 = boost::spirit::x3;
using x3::expectation_failure;
template <typename T> auto rule = [](const char* name = typeid(T).name()) {
struct _{};
return x3::rule<_, T> {name};
};
template <typename T> auto as = [](auto p, const char* name = typeid(T).name()) { return rule<T>(name) = p; };
auto nullProcess = as<AstNullProcess>(x3::omit['0'], "nullProcess");
auto const choiceActual = as<AstChoiceProcess> (nullProcess > '+' > nullProcess, "choiceActual");
auto const choice = rule<AstAnyProcess> ("choice")
= nullProcess >> !x3::lit('+')
| choiceActual;
auto const entry = x3::skip(x3::ascii::space) [choice];
}
int main() {
std::string const str("0 + ");
try {
auto iter = str.cbegin();
auto end = str.cend();
wccs_parser::AstAnyProcess root;
bool const r = parse(iter, end, wccs_parser::entry, root);
if (r) {
std::cout << str << "\n\n Parses OK: \n";
std::cout << "\n-------------------------\n";
} else {
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
if (iter != end) {
std::cout << "Partial match, leaving '" << std::string(iter,end) << "'\n";
}
} catch (wccs_parser::expectation_failure<std::string::const_iterator> const& e) {
std::cout << "Expected: " << e.which() << " at '" << std::string(e.where(), str.end()) << "'\n";
return 1;
}
}
Prints
Expected: nullProcess at ' '

boost::variant gives wrong result when bool appears as possible type

The code that works is the following:
#include <boost/variant.hpp>
#include <string>
#include <map>
#include <iostream>
int main(int argc, char** argv) {
std::map<std::string, boost::variant<int, std::string> > values;
values["a"] = 10;
values["b"] = "bstring";
values["c"] = "cstring";
for (const auto &p : values) {
std::cout << p.first << " = ";
if (p.second.type() == typeid(std::string)) {
std::cout << boost::get<std::string>( p.second ) << " (found string)" << std::endl;
} else if ( p.second.type() == typeid(int)) {
std::cout << boost::get<int>( p.second ) << " (found int)" << std::endl;
} else if ( p.second.type() == typeid(bool)) {
std::cout << boost::get<bool>( p.second ) << " (found bool)" << std::endl;
} else {
std::cout << " not supported type " << std::endl;
}
}
}
The output (g++ test.cpp -std=c++11):
a = 10
b = bstring
c = cstring
The code that does not work is exactly the same, except the line that defines the std::map
modifying the line of the map definition to:
std::map<std::string, boost::variant<int, std::string, bool> > values;
the output is different:
a = 10
b = c =
The if statement that refers to std::string comparison does not succeed. What is the problem?
In your code, values["b"] = "bstring"; creates a bool value, when both std::string and bool are in the variant type.
A fix is values["b"] = std::string("bstring");.
Or, in C++14:
using namespace std::string_literals;
values["b"] = "bstring"s;
It is a well-known annoyance that string literals better convert to bool than to std::string:
#include <iostream>
#include <string>
void f(std::string) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
void f(std::string const&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
void f(bool) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
int main() {
f("hello");
}
Outputs:
void f(bool)

XML parser wrapper

Is there any kind of XML parser wrapper library that would allow switching the actual XML parser engine at configuration or run time instead of forcing me to choose between libxml2, expat or Xalan-C++?
I wrote something similar a while back:
struct xerces;
struct msxml;
struct rapid;
struct tiny;
struct pugixml;
template <typename T> struct platform_manager;
template <typename T> double parse_file(std::string const& f, QueryPerfCounter& qpc);
template<class T>
void demo(std::string const& f, size_t N = 10) {
platform_manager<T> pm;
QueryPerfCounter qpc;
std::vector<double> timing_data;
timing_data.reserve(N);
std::generate_n(std::back_inserter(timing_data), N, std::tr1::bind(&parse_file<typename T>, f, qpc));
adobe::Statistics<double> s(timing_data.begin(), timing_data.end());
std::cout << "Iteration count: " << s.count() << " Mean time: " << s.mean() << "s. Variance: " << s.variance() << "s.\n";
}
/***************************************************************/
template <>
struct platform_manager<msxml> {
platform_manager() {
if (FAILED(CoInitialize(NULL)))
throw std::runtime_error("CoCreateInstance failed");
}
~platform_manager() {
CoUninitialize();
}
};
template<>
double parse_file<msxml>(std::string const& f, QueryPerfCounter& qpc) {
CComPtr<IXMLDOMDocument> pXMLDom;
HRESULT hr = CoCreateInstance(__uuidof(MSXML2::DOMDocument60), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pXMLDom));
CComPtr<IXMLDOMParseError> pXMLErr;
VARIANT_BOOL varStatus;
qpc.Start();
if (FAILED(pXMLDom->load(CComVariant(f.c_str()), &varStatus)))
std::cout << "Parsing failed" << std::endl;
qpc.Stop();
return qpc.Duration(QueryPerfCounter::seconds);
}
/***************************************************************/
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/sax/HandlerBase.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#ifdef XERCES_CPP_NAMESPACE_USE
XERCES_CPP_NAMESPACE_USE
#endif
template <>
struct platform_manager<xerces> {
platform_manager() try {
XMLPlatformUtils::Initialize();
} catch (const XMLException& toCatch) {
char* message = XMLString::transcode(toCatch.getMessage());
std::cout << "Failed to init: " << XMLString::transcode(message) << std::endl;
XMLString::release(&message);
}
~platform_manager() {
XMLPlatformUtils::Terminate();
}
};
template<>
double parse_file<xerces>(std::string const& f, QueryPerfCounter& qpc) {
double duration = 0;
std::tr1::shared_ptr<XercesDOMParser> parser(new XercesDOMParser());
parser->setValidationScheme(XercesDOMParser::Val_Always);
parser->setDoNamespaces(true); // optional
std::tr1::shared_ptr<ErrorHandler> errHandler(new HandlerBase());
parser->setErrorHandler(errHandler.get());
try {
qpc.Start();
parser->parse(f.c_str());
qpc.Stop();
duration = qpc.Duration(QueryPerfCounter::seconds);
}
catch (const XMLException& toCatch) {
char* message = XMLString::transcode(toCatch.getMessage());
std::cout << "Exception message is: \n"
<< message << "\n";
XMLString::release(&message);
}
catch (const DOMException& toCatch) {
char* message = XMLString::transcode(toCatch.msg);
std::cout << "Exception message is: \n"
<< message << "\n";
XMLString::release(&message);
}
catch (...) {
std::cout << "Unexpected Exception \n" ;
}
return duration;
}
/***************************************************************/
#include "rapidxml.hpp"
#include <vector>
#include <fstream>
#include <iterator>
template <>
struct platform_manager<rapid> {};
enum size_hint { B = 1, KB = 1024, MB = 1024 * 1024 };
double file_size(std::ifstream& f, size_hint factor = MB) {
f.seekg (0, std::ios::end);
size_t length = f.tellg();
f.seekg (0, std::ios::beg);
return double(length) / factor;
}
template<>
double parse_file<rapid>(std::string const& f, QueryPerfCounter& qpc) {
double duration = 0;
rapidxml::xml_document<> doc;
try {
qpc.Start();
std::ifstream myfile(f.c_str());
myfile.seekg (0, std::ios::end);
size_t length = myfile.tellg();
myfile.seekg (0, std::ios::beg);
std::vector<char> buffer(length);
myfile.read(& buffer[0], length);
//buffer.reserve(length);
//buffer.insert(std::istreambuf_iterator<char>(myfile)), std::istreambuf_iterator<char>( ));
//std::copy(std::istreambuf_iterator<char>(myfile), std::istreambuf_iterator<char>( ), std::back_insert_iterator(buffer));
buffer.push_back('\0');
qpc.Stop();
duration += qpc.Duration(QueryPerfCounter::seconds);
//std::cout << "Buffer load time: " << duration << "s" << std::endl;
//QueryPerfCounter qpc;
qpc.Start();
doc.parse<rapidxml::parse_non_destructive>(&buffer[0]);
qpc.Stop();
duration += qpc.Duration(QueryPerfCounter::seconds);
} catch (rapidxml::parse_error const& e) {
std::cout << e.what() << std::endl;
} catch (std::exception const& e) {
std::cout << e.what() << std::endl;
}
return duration;
}
/***************************************************************/
template <>
struct platform_manager<tiny> {};
template<>
double parse_file<tiny>(std::string const& f, QueryPerfCounter& qpc) {
tinyxml2::XMLDocument doc;
qpc.Start();
doc.LoadFile(f.c_str());
doc.PrintError(); // emits nothing on success
qpc.Stop();
return qpc.Duration(QueryPerfCounter::seconds);
}
/***************************************************************/
struct banner_printer {
banner_printer(std::string const& libname, std::string const& input) : lib(libname), in(input) {
std::cout << "/*+------------------- BEGIN test for " << lib << " with file: " << in << " -------------------+*/" << std::endl;
}
~banner_printer() {
std::cout << "/*+------------------- END test for " << lib << " with file: " << in << " -------------------+*/" << std::endl;
}
private:
std::string lib, in;
};
/***************************************************************/
#include "pugixml.hpp"
template <>
struct platform_manager<pugixml> {};
template<>
double parse_file<pugixml>(std::string const& f, QueryPerfCounter& qpc) {
pugi::xml_document doc;
qpc.Start();
pugi::xml_parse_result result = doc.load_file(f.c_str());
qpc.Stop();
if (!result) {
std::cout << "XML [" << f << "] parsed with errors, attr value: [" << doc.child("node").attribute("attr").value() << "]\n";
std::cout << "Error description: " << result.description() << "\n";
std::cout << "Error offset: " << result.offset << " (error at offset [..." << (result.offset) << "]\n\n";
}
return qpc.Duration(QueryPerfCounter::seconds);
}
/***************************************************************/
int main() {
std::vector<std::string> v = parse_catalog("D:/Work/xml_parsers/perfcompare/benchmark/catalog.txt");
std::for_each(v.begin(), v.end(), [](std::string const& s) {
{
std::ifstream f(s);
std::cout << "Input file name: " << s << " size: " << file_size(f) << "MB\n\n";
}
{
banner_printer b("xerces", s);
demo<xerces>(s);
}
{
banner_printer b("rapid", s);
demo<rapid>(s);
}
{
banner_printer b("tiny", s);
demo<tiny>(s);
}
{
banner_printer b("pugi", s);
demo<pugixml>(s);
}
{
banner_printer b("MSXML6", s);
demo<msxml>(s);
}
}
);
//expat_demo(argc, argv);
return 0;
}
It may or may not help you get started. I've skipped header includes and some other trivia. I tried to keep the interface simple and identical. This meant that some libraries required additional helper functions.