XML parser wrapper - c++

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.

Related

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

Convert variadic template arguments to JSON string

I need a function that receives variadic template arguments and returns a JSON array string.
I was able to reach this point:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
template<typename Arg>
void add_value(std::stringstream &result, Arg arg){
if (result.rdbuf()->in_avail())
result << ",";
if (std::is_same_v<Arg, string>) {
result << "\"" << arg << "\"";
} else if (std::is_same_v<Arg, const char*>) {
result << "\"" << arg << "\"";
} else if (std::is_same_v<Arg, char*>) {
result << "\"" << arg << "\"";
} else if (std::is_same_v<Arg, bool>) {
result << arg;
} else {
result << arg;
}
}
template<typename... Args>
std::string to_json_array(Args... args) {
std::stringstream result;
(add_value(result, args), ...);
return "[" + result.str() + "]";
}
int main() {
std::string result;
char aa[10] = "haha";
char *bb = aa;
result = to_json_array(123, "Hello", 1.5, string("World!"), true, aa, false, bb, NULL);
std::cout << result << "\n";
}
But it did not work for booleans and null.
As you can notice, I am not a C++ developer.
If you know another method, you can share it too.
I am sharing the solution I've got with the help of the comments:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
template<typename Arg>
void add_value(std::stringstream &result, Arg arg){
if (result.rdbuf()->in_avail())
result << ",";
if constexpr (std::is_same_v<Arg, string>) {
result << "\"" << arg << "\"";
} else if constexpr (std::is_same_v<Arg, const char*>) {
result << "\"" << arg << "\"";
} else if constexpr (std::is_same_v<Arg, char*>) {
result << "\"" << arg << "\"";
} else if constexpr (std::is_same_v<Arg, bool>) {
result << std::boolalpha;
result << arg;
result << std::noboolalpha;
} else if constexpr (std::is_same_v<Arg, nullptr_t>) {
result << "null";
} else {
result << arg;
}
}
template<typename... Args>
std::string to_json_array(Args... args) {
std::stringstream result;
(add_value(result, args), ...);
return "[" + result.str() + "]";
}
int main() {
std::string result;
char aa[10] = "haha";
char *bb = aa;
result = to_json_array(123, "Hello", 1.5, string("World!"), true, aa, false, bb, nullptr, 0);
std::cout << result << "\n";
}
Thank you all!

Catching overflows with boost::lexical_cast

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

How to store template variadic arguments into std::ofstream?

I'm implementing a small Log class in c++
class Log
{
public:
enum LogLevel
{
TRACE,
DEBUG,
INFO,
WARNING,
ERROR,
FATAL
};
Log(): m_log(DEBUG) {} ;
~Log() {};
int SetLogFilePath(const std::string& p_directory, const std::string& p_prefix);
void SetLogLevel(const LogLevel p_logLvl);
LogLevel& GetLogLevel();
template<typename... Args>
int Logging(const std::string& p_logLevel, const std::string& p_srcFile, const std::string& p_function, const int& p_line,
const Args&& ... p_args);
private:
LogLevel m_log;
std::string m_logFilePath;
};
This is my Log class, I store directory and prefix into std::string m_logFilePath
template <typename ... Args>
int Log::Logging(const std::string& p_logLevel, const std::string& p_srcFile, const std::string& p_function, const int& p_line,
const Args&&... p_args)
{
std::ofstream log;
log.open(m_logFilePath.c_str(), std::ios::out);
if(!log.good())
{
std::cout << "Couldn't create file : " << m_logFilePath.c_str() << std::endl;
return 0;
}
switch(p_logLevel)
{
case "TRACE":
log << "(TRACE) ";
break;
case "DEBUG":
log << "(DEBUG) ";
break;
case "INFO":
log << "(INFO) ";
break;
case "WARNING":
log << "(WARNING) ";
break;
case "ERROR":
log << "(ERROR) ";
break;
case "FATAL":
log << "(FATAL) ";
break;
default: break;
}
log << "Log Created at (Local Time): " << std::put_time(std::localtime(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())), "%c") << " ";
log << "File Name: " << p_srcFile.c_str() << " " << "Function Name: " << p_function.c_str() << " " << "Line: " << p_line << " " << std::forward<Args>(p_args) << std::endl;
log.close();
return 1;
}
Above is my implementation of int Logging(...) function.
Basically, I store the logging level, current time, source file path, function name , line from code, and some variadic arguments.
I've been searching for a way to store multiple variadic arguments into my std::ostream log but couldn't find any answer.
Please Help!
If I understand correctly, your problem is that you don't know how to manage (in this case: send to an output stream) a variadic list (variadic template based) of arguments.
I propose an example of use that use the power of comma operator in the context of an (unused) array initialization
using unused = int[];
int cnt { -1 };
(void)unused { 0, (oss << ", extra val "
<< ++cnt << " [" << args << ']', 0)... };
The following is a simplified, but full working, example
#include <sstream>
#include <iostream>
template <typename ... Args>
std::string logging (std::string const & valA, std::string const & valB,
std::string const & valC, Args const & ... args)
{
std::ostringstream oss;
using unused = int[];
oss << "valA [" << valA << "], "
<< "valB [" << valB << "], "
<< "valC [" << valC << ']';
int cnt { -1 };
(void)unused { 0, (oss << ", extra val "
<< ++cnt << " [" << args << ']', 0)... };
return oss.str();
}
int main()
{
auto l = logging("val1", "val2", "val3", 0, 1L, 2LL, 3U, 4UL, 5ULL,
6.0, 7.0f, "eight");
std::cout << l << std::endl;
}

Boost ASIO async_read is not reading data from the server

I have a server/client application which works for a write from client to a read at server.
After the sever is done receiving the data in the function read_async_1, it writes a simple string "Response" at the end.
Now, this is not received at the client. In the client code, StartHandlingServer is what does an async read.. Now, the handler inside this is not getting called..
Can someone please have a look at this ? Appreciate your feedback.
Server Code
#include <boost/asio.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/bind.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/tuple/tuple.hpp>
#include <thread>
#include <atomic>
#include <memory>
#include <iostream>
#include "../stocks.hpp"
using namespace boost;
class Service {
public:
Service(){}
void StartHandligClient(boost::shared_ptr<asio::ip::tcp::socket> sock)
{
std::cout << "StartHandligClient : sock.use_count : " << sock.use_count() << "\n";
read_async_1(sock);
return;
}
private:
void read_async_1(boost::shared_ptr<asio::ip::tcp::socket> sock)
{
if(!(*sock.get()).is_open())
{
std::cout << getpid() << " : Socket closed in sync_read \n" << std::flush;
return ;
}
std::cout << "haha_1\n" << std::flush;
boost::asio::async_read( (*sock.get()), boost::asio::buffer(inbound_header_),
[this, sock](boost::system::error_code ec,
size_t bytesRead)
{
int headerBytesReceived = bytesRead;
std::cout << "\n\n headerBytesReceived : " << headerBytesReceived << "\n" << std::flush ;
if (!ec)
{
// Determine the length of the serialized data.
std::istringstream is(std::string(inbound_header_, header_length));
std::cout << "is : +" << is.str() << "+, inbound_header_ : +" << inbound_header_ << "+\n";
std::size_t inbound_data_size = 0;
if (!(is >> std::hex >> inbound_data_size))
{
// Header doesn't seem to be valid. Inform the caller.
// boost::system::error_code error(boost::asio::error::invalid_argument);
// boost::get<0>(handler)(error);
std::cout << "RET-1 \n";
return;
}
std::cout << "inbound_data_size : " << inbound_data_size << "\n" << std::flush;
// Start an asynchronous call to receive the data.
inbound_data_.resize(inbound_data_size);
std::cout << "inbound_data_.size() : " << inbound_data_.size() << "\n" << std::flush;
int bytesReceived = asio::read( *sock.get(), boost::asio::buffer(inbound_data_) );
std::string archive_data(&inbound_data_[0], inbound_data_.size());
std::istringstream archive_stream(archive_data);
boost::archive::text_iarchive archive(archive_stream);
archive >> stocks_;
std::cout << "bytesReceived : " << bytesReceived << " , stocks_.size() : " << stocks_.size() << "\n";
// Print out the data that was received.
for (std::size_t i = 0; i < stocks_.size(); ++i)
{
std::cout << "Stock number " << i << "\n";
std::cout << " code: " << stocks_[i].code << "\n";
std::cout << " name: " << stocks_[i].name << "\n";
std::cout << " open_price: " << stocks_[i].open_price << "\n";
std::cout << " high_price: " << stocks_[i].high_price << "\n";
std::cout << " low_price: " << stocks_[i].low_price << "\n";
std::cout << " last_price: " << stocks_[i].last_price << "\n";
std::cout << " buy_price: " << stocks_[i].buy_price << "\n";
std::cout << " buy_quantity: " << stocks_[i].buy_quantity << "\n";
std::cout << " sell_price: " << stocks_[i].sell_price << "\n";
std::cout << " sell_quantity: " << stocks_[i].sell_quantity << "\n";
}
sleep(1);
// Sending response.
std::string response = "Response\n";
asio::write(*sock.get(), asio::buffer(response));
this->read_async_1(sock);
}
else
{
// Terminate connection ?
if(ec == boost::asio::error::eof)
{
std::cout << getpid() << " : ** sync_read : Connection lost : boost::asio::error::eof ** \n";
}
std::cout << "Error occured in async_read! Error code = " << ec.value() << ". Message: " << ec.message() << "\n" << std::flush;
return ;
}
return ;
}
);
std::cout << getpid() << " : final return from async_read \n" << std::flush;
return ;
}
/// The size of a fixed length header.
enum { header_length = 8 };
/// Holds an outbound header.
std::string outbound_header_;
/// Holds the outbound data.
std::string outbound_data_;
/// Holds an inbound header.
char inbound_header_[header_length];
/// Holds the inbound data.
std::vector<char> inbound_data_;
std::vector<stock> stocks_;
};
class Acceptor {
public:
Acceptor(asio::io_service& ios, unsigned short port_num) :
m_ios(ios),
m_acceptor(m_ios,
asio::ip::tcp::endpoint(
asio::ip::address_v4::any(),
port_num))
{
m_acceptor.listen();
}
void Accept() {
std::cout << "Server Accept() \n" << std::flush;
boost::shared_ptr<asio::ip::tcp::socket> sock(new asio::ip::tcp::socket(m_ios));
m_acceptor.accept(*sock.get());
(new Service)->StartHandligClient(sock);
std::cout << "Accept : sock.use_count : " << sock.use_count() << "\n";
}
private:
asio::io_service& m_ios;
asio::ip::tcp::acceptor m_acceptor;
};
class Server {
public:
Server() : m_stop(false) {}
void Start(unsigned short port_num) {
m_thread.reset(new std::thread([this, port_num]() {
Run(port_num);
}));
}
void Stop() {
std::cout << "STOPPING \n";
m_stop.store(true);
m_thread->join();
}
private:
void Run(unsigned short port_num) {
Acceptor acc(m_ios, port_num);
while (!m_stop.load())
{
std::cout << "Server accept\n" << std::flush;
acc.Accept();
m_ios.run();
}
}
std::unique_ptr<std::thread> m_thread;
std::atomic<bool> m_stop;
asio::io_service m_ios;
};
int main()
{
unsigned short port_num = 3333;
try {
Server srv;
srv.Start(port_num);
std::this_thread::sleep_for(std::chrono::seconds(100));
std::cout << "Stopping server \n";
srv.Stop();
}
catch (system::system_error &e) {
std::cout << "Error occured! Error code = "
<< e.code() << ". Message: "
<< e.what();
}
return 0;
}
Client Code
#include <boost/asio.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/bind.hpp>
#include <boost/serialization/vector.hpp>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include "../stocks.hpp"
using namespace boost;
class mysock : public boost::asio::ip::tcp::socket
{
public:
mysock(asio::io_service& serv) : boost::asio::ip::tcp::socket(serv)
{
}
~mysock()
{
std::cout << "Inside destructor for mysock \n";
}
};
class SyncTCPClient {
public:
SyncTCPClient(const std::string& raw_ip_address,
unsigned short port_num) :
socket_((new mysock(m_ios))),
m_ep(asio::ip::address::from_string(raw_ip_address), port_num)
{
(*socket_.get()).open(m_ep.protocol());
connect();
StartHandlingServer(socket_);
}
mysock& socket()
{
return *socket_.get();
}
void connect() {
(*socket_.get()).connect(m_ep);
m_ios.run();
}
void StartHandlingServer(boost::shared_ptr<mysock> sock)
{
if(!(*sock.get()).is_open())
{
std::cout << getpid() << " : Socket closed in sync_read \n" << std::flush;
return ;
}
std::cout << "Start StartHandlingServer\n" << std::flush;
char inbound_header_[4];;
try
{
boost::asio::async_read( (*sock.get()), boost::asio::buffer(inbound_header_),
[this, sock](boost::system::error_code ec,
size_t bytesRead)
{
int headerBytesReceived = bytesRead;
std::cout << "\n\n headerBytesReceived : " << headerBytesReceived << "\n" << std::flush ;
if (!ec)
{
}
else
{
if(ec == boost::asio::error::eof)
{
std::cout << getpid() << " : ** sync_read : Connection lost : boost::asio::error::eof ** \n";
}
std::cout << "Error occured in async_read! Error code = " << ec.value() << ". Message: " << ec.message() << "\n" << std::flush;
return ;
}
}
);
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
std::cout << "Done StartHandlingServer\n" << std::flush;
}
void close() {
(*socket_.get()).shutdown(
boost::asio::ip::tcp::socket::shutdown_both);
(*socket_.get()).close();
}
std::string emulateLongComputationOp(
unsigned int duration_sec) {
std::string request = "EMULATE_LONG_COMP_OP "
+ std::to_string(duration_sec)
+ "\n";
sendRequest(request);
/*
sleep(2);
sendRequest(request);
sleep(2);
sendRequest(request);
*/
return receiveResponse();
};
private:
void sendRequest(const std::string& request) {
std::vector<stock> stocks_;
// Create the data to be sent to each client.
stock s;
s.code = "ABC";
s.name = "A Big Company";
s.open_price = 4.56;
s.high_price = 5.12;
s.low_price = 4.33;
s.last_price = 4.98;
s.buy_price = 4.96;
s.buy_quantity = 1000;
s.sell_price = 4.99;
s.sell_quantity = 2000;
stocks_.push_back(s);
// Serialize the data first so we know how large it is.
std::ostringstream archive_stream;
boost::archive::text_oarchive archive(archive_stream);
archive << stocks_;
outbound_data_ = archive_stream.str();
std::cout << "outbound_data_ : " << outbound_data_ << "\n" << std::flush;
std::cout << "outbound_data_.size() : " << outbound_data_.size() << "\n" << std::flush;
// Format the header.
std::ostringstream header_stream;
header_stream << std::setw(header_length) << std::hex << outbound_data_.size();
std::cout << "header_stream.str() : " << header_stream.str() << "\n" << std::flush;
std::cout << "header_stream.str().size() : " << header_stream.str().size() << "\n" << std::flush;
if (!header_stream || header_stream.str().size() != header_length)
{
// Something went wrong, inform the caller.
// boost::system::error_code error(boost::asio::error::invalid_argument);
// socket_.get_io_service().post(boost::bind(handler, error));
return;
}
outbound_header_ = header_stream.str();
std::cout << "outbound_header_ : +" << outbound_header_ << "+\n" << std::flush;
// Write the serialized data to the socket. We use "gather-write" to send
// both the header and the data in a single write operation.
/*
std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(boost::asio::buffer(outbound_header_));
buffers.push_back(boost::asio::buffer(outbound_data_));
*/
std::size_t headerSize = asio::write(*socket_.get(), boost::asio::buffer(outbound_header_));
std::size_t dataSize = asio::write(*socket_.get(), boost::asio::buffer(outbound_data_));
std::cout << "headerSize : " << headerSize << " , dataSize : " << dataSize;
}
std::string receiveResponse() {
std::string response;
/*
asio::streambuf buf;
asio::read_until(*socket_.get(), buf, '\n');
std::istream input(&buf);
std::getline(input, response);
*/
return response;
}
private:
asio::io_service m_ios;
boost::shared_ptr<mysock> socket_;
asio::ip::tcp::endpoint m_ep;
enum { header_length = 8 };
std::string outbound_data_;
std::string outbound_header_;
};
int main()
{
const std::string raw_ip_address = "127.0.0.1";
const unsigned short port_num = 3333;
try {
SyncTCPClient client(raw_ip_address, port_num);
std::cout << "Sending request to the server... \n"<< std::endl;
std::string response = client.emulateLongComputationOp(10);
std::cout << "\nResponse received: " << response << std::endl;
sleep(10);
std::cout << "\n\n Closing client connection \n\n";
// Close the connection and free resources.
client.close();
}
catch (system::system_error &e) {
std::cout << "Client Error occured! Error code = " << e.code()
<< ". Message: " << e.what();
return e.code().value();
}
return 0;
}
Included file stocks.hpp
#ifndef _STOCKS_HPP_
#define _STOCKS_HPP_
struct stock
{
std::string code;
std::string name;
double open_price;
double high_price;
double low_price;
double last_price;
double buy_price;
int buy_quantity;
double sell_price;
int sell_quantity;
template <typename Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar & code;
ar & name;
ar & open_price;
ar & high_price;
ar & low_price;
ar & last_price;
ar & buy_price;
ar & buy_quantity;
ar & sell_price;
ar & sell_quantity;
}
};
#endif
The io_service::run function runs until there are no more events.
In the client you run it once, when there are no active events to be handled, which means it will return immediately.
Because the io_server isn't "running" it will not handle any events.
You need to call run (or poll) in a loop, like you do in the server.