I am trying to perform XMPP Handshake Flow mentioned at https://developers.google.com/cloud-print/docs/rawxmpp.
This allows the device to subscribe and receive notifications.
As of now, I have explored the following options:
1) libcurl
2) Gloox C/C++
3) TXMPP
4) Libjingle
Which option would be a good choice to start with? I would like to consider support for and maintenance of the library as a major factor.
Following is my solution using Gloox C/C++ library to perform XMPP Handshake Flow:
#include <cassert>
#include <iostream>
#include <boost/make_shared.hpp>
#include <iq.h>
#include <parser.h>
#include <base64.h>
#include <connectiontcpclient.h>
#include <connectiontls.h>
#include <connectiondatahandler.h>
#include <connectionhttpproxy.h>
#include <logsink.h>
#include <client.h>
#include <connectionsocks5proxy.h>
using namespace gloox;
using namespace std;
const string proxyHost = ""; //specify proxy server name
const int proxyPort = 0; //specify proxy port number
const string xmppHost = "talk.google.com";
const int xmppPort = 5222;
Client *c;
ConnectionBase *client_ ;
ConnectionTCPClient* conn0;
ConnectionHTTPProxy* conn2;
class Bot: public ConnectionDataHandler, TagHandler, TLSHandler{
public:
Bot(): parser_(this)
{
conn0 = new ConnectionTCPClient(this, log_, proxyHost, proxyPort);
ConnectionHTTPProxy* conn2 = new ConnectionHTTPProxy( this, conn0, log_, xmppHost, xmppPort);
client_ = conn0;
ConnectionError ce = ConnNoError;
ce = conn2->connect();
assert(ce == ConnNoError);
conn2->receive();
}
virtual void handleConnect(const ConnectionBase* con) {
send("<stream:stream to=\"gmail.com\" xml:lang=\"en\" version=\"1.0\" xmlns:stream=\"http://etherx.jabber.org/streams\" xmlns=\"jabber:client\">\r\n");
}
virtual void handleReceivedData(const ConnectionBase* con, const string& data) {
cerr << "[recv] " << data << endl;
string copied = data;
int pos = parser_.feed(copied);
assert(pos < 0);
}
virtual void handleTag(Tag* tag) {
if (tag->name() == "stream" && tag->xmlns() == "http://etherx.jabber.org/streams") {
sid_ = tag->findAttribute("id");
} else{
if (tag->name() == "features") {
if (tag->hasChild("starttls", "xmlns", "urn:ietf:params:xml:ns:xmpp-tls")) {
send(Tag("starttls", "xmlns", "urn:ietf:params:xml:ns:xmpp-tls").xml());
}
else if (tag->hasChild("mechanisms", "xmlns", "urn:ietf:params:xml:ns:xmpp-sasl") && tag->findChild("mechanisms")->hasChildWithCData(
"mechanism", "X-OAUTH2"))
{
Tag a("auth", "xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
a.addAttribute("mechanism", "X-OAUTH2");
a.addAttribute("service", "chromiumsync");
a.addAttribute("allow-generated-jid", "true");
a.addAttribute("client-uses-full-bind-result", "true");
a.addAttribute("auth", "http://www.google.com/talk/protocol/auth");
string credential;
credential.append("\0", 1);
credential.append(""); //Specify Bare JID
credential.append("\0", 1);
credential.append(""); //Specify Access Token
a.setCData(Base64::encode64(credential));
send(a.xml());
}
else if (tag->hasChild("bind", "xmlns", "urn:ietf:params:xml:ns:xmpp-bind")) {
Tag iq("iq", "xmlns", "jabber:client");
iq.addAttribute("type", "set");
iq.addAttribute("id", "0");
Tag *bind = new Tag("bind", "xmlns", "urn:ietf:params:xml:ns:xmpp-bind");
Tag *resource = new Tag("resource");
resource->setCData("GCPResource");
bind->addChild(resource);
iq.addChild(bind);
send(iq.xml());
}
}
else if (tag->name() == "proceed" && tag->xmlns() == "urn:ietf:params:xml:ns:xmpp-tls") {
ConnectionTLS* encryption_client = new ConnectionTLS(this, conn0, log_);
encryption_client->registerTLSHandler(this);
client_ = encryption_client;
ConnectionError ret = encryption_client->connect();
assert(ret == ConnNoError);
}
else if (tag->name() == "success" && tag->xmlns() == "urn:ietf:params:xml:ns:xmpp-sasl") {
send("<stream:stream to=\"gmail.com\" xml:lang=\"en\" version=\"1.0\" xmlns:stream=\"http://etherx.jabber.org/streams\" xmlns=\"jabber:client\">\r\n");
}
else if (tag->name() == "iq") {
if (tag->hasChild("bind", "xmlns", "urn:ietf:params:xml:ns:xmpp-bind")) {
resource_ = tag->findChild("bind")->findChild("jid")->cdata();
Tag iq("iq");
iq.addAttribute("type", "set");
iq.addAttribute("id", "1");
iq.addChild(new Tag("session", "xmlns", "urn:ietf:params:xml:ns:xmpp-session"));
send(iq.xml());
//Step 2: Subscribing for notifications
if (tag->hasAttribute("type", "result")) {
Tag iq("iq");
iq.addAttribute("type", "set");
iq.addAttribute("to", ""); //Specify Bare JID
iq.addAttribute("id", "3");
Tag *bind = new Tag("subscribe", "xmlns", "google:push");
Tag *resource = new Tag("item");
resource->addAttribute("channel", "cloudprint.google.com");
resource->addAttribute("from", "cloudprint.google.com");
bind->addChild(resource);
iq.addChild(bind);
send(iq.xml());
}
}
}
}
}
virtual void handleEncryptedData(const TLSBase* tls,
const string& data) {
cout << "handleEncryptedData" << endl;
}
virtual void handleDecryptedData(const TLSBase* tls,
const string& data) {
cout << "handleDecryptedData" << endl;
}
virtual void handleHandshakeResult(const TLSBase* tls, bool,
CertInfo& cert) {
cout << "handleHandshakeResult" << endl;
}
virtual void handleDisconnect(const ConnectionBase* con, ConnectionError) {
cout << "handleDisconnect" << endl;
}
private:
LogSink log_;
string sid_;
string resource_;
Parser parser_;
ConnectionBase *client_;
void send(const string &data) {
cerr << "[send] " << data << endl;
client_->send(data);
}
};
int main() {
Bot bot;
}
Related
I write a C++ dome of tcp server with the libuv. When I check the cpu performance, I found the dome is a single thread running, how can I implement it with multi-thread?
Currently, the dome can hanlde 100,000+ tcp request per second, it can only eat 1 CPU.
Code:
#include <iostream>
#include <atomic>
#include "uv.h"
#include <thread>
#include <mutex>
#include <map>
using namespace std;
auto loop = uv_default_loop();
struct sockaddr_in addr;
typedef struct {
uv_write_t req;
uv_buf_t buf;
} write_req_t;
typedef struct {
uv_stream_t* client;
uv_alloc_cb alloc_cb;
uv_read_cb read_cb;
} begin_read_req;
void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
buf->base = (char*)malloc(suggested_size);
buf->len = suggested_size;
}
void free_write_req(uv_write_t *req) {
write_req_t *wr = (write_req_t*)req;
free(wr->buf.base);
free(wr);
}
void echo_write(uv_write_t *req, int status) {
if (status) {
fprintf(stderr, "Write error %s\n", uv_strerror(status));
}
free_write_req(req);
}
void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) {
if (nread > 0) {
auto req = (write_req_t*)malloc(sizeof(write_req_t));
auto *aaa = (char*)malloc(5);
aaa[0] = '+';
aaa[1] = 'O';
aaa[2] = 'K';
aaa[3] = '\r';
aaa[4] = '\n';
req->buf = uv_buf_init(aaa, 5);
uv_write((uv_write_t*)req, client, &req->buf, 1, echo_write);
}
if (nread < 0) {
if (nread != UV_EOF)
fprintf(stderr, "Read error %s\n", uv_err_name(static_cast<unsigned int>(nread)));
uv_close((uv_handle_t*)client, nullptr);
}
free(buf->base);
}
void acceptClientRead(uv_work_t *req) {
begin_read_req *data = (begin_read_req *)req->data;
uv_read_start(data->client, data->alloc_cb, data->read_cb);
}
void on_new_connection(uv_stream_t *server, int status) {
if (status < 0) {
cout << "New connection error:" << uv_strerror(status);
return;
}
uv_tcp_t *client = (uv_tcp_t *)malloc(sizeof(uv_tcp_t));
uv_tcp_init(loop, client);
uv_work_t *req = (uv_work_t *)malloc(sizeof(uv_work_t));
begin_read_req *read_req = (begin_read_req *)malloc(sizeof(begin_read_req));
read_req->client = (uv_stream_t *)client;
read_req->read_cb = echo_read;
read_req->alloc_cb = alloc_buffer;
req->data = read_req;
if (uv_accept(server, (uv_stream_t *)client) == 0) {
uv_read_start((uv_stream_t *)client, alloc_buffer, echo_read);
// uv_queue_work(workloop[0], req, acceptClientRead, nullptr);
}
else {
uv_close((uv_handle_t *)client, nullptr);
}
}
void timer_callback(uv_timer_t* handle) {
cout << std::this_thread::get_id() << "---------" << "hello" << endl;
}
int main() {
uv_tcp_t server{};
uv_tcp_init(loop, &server);
uv_ip4_addr("0.0.0.0", 8790, &addr);
uv_tcp_bind(&server, (const struct sockaddr *) &addr, 0);
uv_listen((uv_stream_t *)&server, 511, on_new_connection);
uv_run(loop, UV_RUN_DEFAULT);
return 0;
}
Of course, I can make the write step asynchronous in the method "echo_read", but I didn't do anything before the write, can I make the demo multi-thread in another way to improve the throughput?
I am writing mosquitto code for consuming the message after subscribing to a particular topic. Now I want to set different configuration for mosquitto like
autosave_interval 100
persistence true
persistence_location /var/lib/mosquitto/
persistence_file mosquitto.db
But I have no idea how to set this configuration in c++. I tried to google it but could not found any result. Plz, help. Below is c++ code for mosquito
myMosq.h
/*
* myMosq.h
*
* Created on: Jul 28, 2016
* Author: nilav
*/
#include <iostream>
#ifndef MYMOSQ_H_
#define MYMOSQ_H_
#include <mosquittopp.h>
#include <mosquitto.h>
using namespace std;
class myMosq : public mosqpp::mosquittopp
{
private:
const char * host;
const char * id;
const char * topic;
int port;
int keepalive;
void on_connect(int rc);
void on_message(const struct mosquitto_message *message);
void on_disconnect(int rc);
void on_subscribe(int mid, int qos_count, const int *granted_qos);
void on_publish(int mid);
void on_unsubscribe(int mid);
public:
myMosq(const char *id, const char * _topic, const char *host, int port);
~myMosq();
bool send_message(string responseMessage);
bool receive_message();
void writeToDatabase(string query);
};
#endif
myMosq.cpp
#include <cstdio>
#include <cstring>
#include <iostream>
#include "myMosq.h"
#include <mosquittopp.h>
#include "Configuration.h"
#include "Databases.h"
using namespace std;
Configuration configuration;
myMosq::myMosq(const char * _id,const char * _topic, const char * _host, int _port) : mosquittopp(_id)
{
mosqpp::lib_init(); // Mandatory initialization for mosquitto library
this->keepalive = 60; // Basic configuration setup for myMosq class
this->id = _id;
this->port = _port;
this->host = _host;
this->topic = _topic;
connect_async(host, // non blocking connection to broker request
port,
keepalive);
loop_start(); // Start thread managing connection / publish / subscribe
};
myMosq::~myMosq() {
loop_stop(); // Kill the thread
mosqpp::lib_cleanup(); // Mosquitto library cleanup
}
bool myMosq::receive_message()
{
int set = subscribe(NULL, configuration.subscriptionTopic.c_str(),2);
return set;
}
bool myMosq::send_message(string responseMessage) {
int ret = publish(NULL,configuration.producerTopic.c_str(),strlen(responseMessage.c_str()),responseMessage.c_str(),1,false);
return (ret = MOSQ_ERR_SUCCESS);
}
void myMosq::on_disconnect(int rc) {
std::cout << ">> myMosq - disconnection(" << rc << ")" << std::endl;
}
void myMosq::on_connect(int rc)
{
if ( rc == 0 ) {
std::cout << ">> myMosq - connected with server" << std::endl;
} else {
std::cout << ">> myMosq - Impossible to connect with server(" << rc << ")" << std::endl;
}
}
void myMosq::on_message(const struct mosquitto_message *message) {
char * pchar = (char*)(message->payload);
string str(pchar);
writeToDatabase(str);
}
void myMosq::on_subscribe(int mid, int qos_count, const int *granted_qos)
{
std::cout << ">> subscription succeeded (" << mid << ") " << std::endl;
}
void myMosq::on_publish(int mid) {
std::cout << ">> myMosq - Message (" << mid << ") succeed to be published " << std::endl;
}
void myMosq::writeToDatabase(string query) {
Databases* database = new Databases(configuration.db,
configuration.dbPort, configuration.username, configuration.password,
configuration.schema);
database->writeDatabase(query);
if(database->responseMessage == "") {
database->responseMessage = "SUCCESS";
}
this->send_message(database->responseMessage);
}
void myMosq::on_unsubscribe(int mid) {
cout<<"unscubscribed";
};
The options you are seeing are for mosquitto broker which acts almost like a server.
Mosquitto C++ library is a client library and those options(e.g. autosave_interval) are not valid for a client. Regarding persistance, mosquitto C/C++ client library doesn't offer file persistance currently.
I'm studying ZeroMQ with myself.
I tested PUB as a server(bind), SUB as a client(connect) and worked fine. Opposite (PUB as a client(connect), SUB as a server(bind)) also works fine.
When I connect a another SUB socket as client something goes wrong without any exception or errors.
here's my example code.
#include <zmq.hpp>
#include <string>
#include <iostream>
#include <unistd.h>
#include <thread>
class ZMQSock
{
public:
ZMQSock(const char* addr)
{
if (addr != NULL)
{
mctx = new zmq::context_t(1);
mszAddr = new char[strlen(addr) + 1];
snprintf(mszAddr, strlen(addr) + 1, "%s", addr);
}
}
virtual ~ZMQSock()
{
if (msock != nullptr)
delete msock;
if (mctx != nullptr)
delete mctx;
if (mszAddr != nullptr)
delete [] mszAddr;
}
int zbind()
{
if (msock != nullptr)
msock->bind(mszAddr);
else return -1;
return 0;
}
int zconnect()
{
if (msock != nullptr)
msock->connect(mszAddr);
else return -1;
return 0;
}
void start()
{
if (mbthread != false)
return ;
mbthread = true;
mhthread = std::thread(std::bind(&ZMQSock::run, this));
}
virtual void stop()
{
if (mbthread == false)
return ;
mbthread = false;
if (mhthread.joinable())
mhthread.join();
}
virtual void run() = 0;
protected:
char* mszAddr{nullptr};
zmq::context_t* mctx{nullptr};
zmq::socket_t* msock{nullptr};
bool mbthread{false};
std::thread mhthread;
};
class ZPublisher : public ZMQSock
{
public:
ZPublisher(const char* addr) : ZMQSock(addr)
{
if (msock == nullptr)
{
msock = new zmq::socket_t(*mctx, ZMQ_PUB);
}
}
virtual ~ZPublisher()
{
}
bool zsend(const char* data, const unsigned int length, bool sendmore=false)
{
zmq::message_t msg(length);
memcpy(msg.data(), data, length);
if (sendmore)
return msock->send(msg, ZMQ_SNDMORE);
return msock->send(msg);
}
void run()
{
if (mszAddr == nullptr)
return ;
if (strlen(mszAddr) < 6)
return ;
const char* fdelim = "1";
const char* first = "it sends to first. two can not recv this sentence!\0";
const char* sdelim = "2";
const char* second = "it sends to second. one can not recv this sentence!\0";
while (mbthread)
{
zsend(fdelim, 1, true);
zsend(first, strlen(first));
zsend(sdelim, 1, true);
zsend(second, strlen(second));
usleep(1000 * 1000);
}
}
};
class ZSubscriber : public ZMQSock
{
public:
ZSubscriber(const char* addr) : ZMQSock(addr)
{
if (msock == nullptr)
{
msock = new zmq::socket_t(*mctx, ZMQ_SUB);
}
}
virtual ~ZSubscriber()
{
}
void setScriberDelim(const char* delim, const int length)
{
msock->setsockopt(ZMQ_SUBSCRIBE, delim, length);
mdelim = std::string(delim, length);
}
std::string zrecv()
{
zmq::message_t msg;
msock->recv(&msg);
return std::string(static_cast<char*>(msg.data()), msg.size());
}
void run()
{
if (mszAddr == nullptr)
return ;
if (strlen(mszAddr) < 6)
return ;
while (mbthread)
{
std::cout << "MY DELIM IS [" << mdelim << "] - MSG : ";
std::cout << zrecv() << std::endl;
usleep(1000 * 1000);
}
}
private:
std::string mdelim;
};
int main ()
{
ZPublisher pub("tcp://localhost:5252");
ZSubscriber sub1("tcp://localhost:5252");
ZSubscriber sub2("tcp://*:5252");
pub.zconnect();
sub1.zconnect();
sub2.zbind();
sub1.setScriberDelim("1", 1);
sub2.setScriberDelim("2", 1);
pub.start();
std::cout << "PUB Server has been started.." << std::endl;
usleep(1000 * 1000);
sub1.start();
std::cout << "SUB1 Start." << std::endl;
sub2.start();
std::cout << "SUB2 Start." << std::endl;
int i = 0;
std::cout << "< Press any key to exit program. >" << std::endl;
std::cin >> i;
std::cout << "SUB1 STOP START" << std::endl;
sub1.stop();
std::cout << "SUB2 STOP START" << std::endl;
sub2.stop();
std::cout << "PUB STOP START" << std::endl;
pub.stop();
std::cout << "ALL DONE" << std::endl;
return 0;
}
What causes this? or Am I using PUB/SUB illegally?
You are connecting a SUB socket to a SUB socket, that is an invalid connection. In your case the PUB should bind and the SUBs should connect.
i solved couple of my redefinition problems but still have one:
Error 2 error LNK2005: "class ConsoleCommandHandler commandHandler" (?commandHandler##3VConsoleCommandHandler##A) already defined in IRC.obj C:\Users\Łukasz\Desktop\IRCClient-master\Magic.obj
Here are the .h files
magic.h
#ifndef Magic_h
#define Magic_h
#include <iostream>
#include <signal.h>
#include <cstdlib>
#include <map>
#include <algorithm>
#include "src\Thread.h"
#include "src\IRCClient.h"
void signalHandler(int signal);
class ConsoleCommandHandler
{
public:
bool AddCommand(std::string name, int argCount, void(*handler)(std::string /*params*/, IRCClient* /*client*/));
void ParseCommand(std::string command, IRCClient* client);
private:
struct CommandEntry
{
int argCount;
void(*handler)(std::string /*arguments*/, IRCClient* /*client*/);
};
std::map<std::string, CommandEntry> _commands;
};
ConsoleCommandHandler commandHandler;
void msgCommand(std::string arguments, IRCClient* client);
void joinCommand(std::string channel, IRCClient* client);
void partCommand(std::string channel, IRCClient* client);
void ctcpCommand(std::string arguments, IRCClient* client);
ThreadReturn inputThread(void* client);
#endif
magic.cpp
#include "Magic.h"
void signalHandler(int signal)
{
volatile bool running;
running = false;
};
bool ConsoleCommandHandler::AddCommand(std::string name, int argCount, void(*handler)(std::string /*params*/, IRCClient* /*client*/))
{
CommandEntry entry;
entry.argCount = argCount;
entry.handler = handler;
std::transform(name.begin(), name.end(), name.begin(), towlower);
_commands.insert(std::pair<std::string, CommandEntry>(name, entry));
return true;
}
void ConsoleCommandHandler::ParseCommand(std::string command, IRCClient* client)
{
if (_commands.empty())
{
std::cout << "No commands available." << std::endl;
return;
}
if (command[0] == '/')
command = command.substr(1); // Remove the slash
std::string name = command.substr(0, command.find(" "));
std::string args = command.substr(command.find(" ") + 1);
int argCount = std::count(args.begin(), args.end(), ' ');
std::transform(name.begin(), name.end(), name.begin(), towlower);
std::map<std::string, CommandEntry>::const_iterator itr = _commands.find(name);
if (itr == _commands.end())
{
std::cout << "Command not found." << std::endl;
return;
}
if (++argCount < itr->second.argCount)
{
std::cout << "Insuficient arguments." << std::endl;
return;
}
(*(itr->second.handler))(args, client);
}
struct CommandEntry
{
int argCount;
void(*handler)(std::string /*arguments*/, IRCClient* /*client*/);
};
std::map<std::string, CommandEntry> _commands;
void msgCommand(std::string arguments, IRCClient* client)
{
std::string to = arguments.substr(0, arguments.find(" "));
std::string text = arguments.substr(arguments.find(" ") + 1);
std::cout << "To " + to + ": " + text << std::endl;
client->SendIRC("PRIVMSG " + to + " :" + text);
};
void joinCommand(std::string channel, IRCClient* client)
{
if (channel[0] != '#')
channel = "#" + channel;
client->SendIRC("JOIN " + channel);
}
void partCommand(std::string channel, IRCClient* client)
{
if (channel[0] != '#')
channel = "#" + channel;
client->SendIRC("PART " + channel);
}
void ctcpCommand(std::string arguments, IRCClient* client)
{
std::string to = arguments.substr(0, arguments.find(" "));
std::string text = arguments.substr(arguments.find(" ") + 1);
std::transform(text.begin(), text.end(), text.begin(), towupper);
client->SendIRC("PRIVMSG " + to + " :\001" + text + "\001");
}
ThreadReturn inputThread(void* client)
{
std::string command;
commandHandler.AddCommand("msg", 2, &msgCommand);
commandHandler.AddCommand("join", 1, &joinCommand);
commandHandler.AddCommand("part", 1, &partCommand);
commandHandler.AddCommand("ctcp", 2, &ctcpCommand);
while (true)
{
getline(std::cin, command);
if (command == "")
continue;
if (command[0] == '/')
commandHandler.ParseCommand(command, (IRCClient*)client);
else
((IRCClient*)client)->SendIRC(command);
if (command == "quit")
break;
}
#ifdef _WIN32
_endthread();
#else
pthread_exit(NULL);
#endif
}
irc.h
#pragma once
#include <iostream>
#include <signal.h>
#include <cstdlib>
#include <map>
#include <algorithm>
#include "Magic.h"
#include <msclr/marshal.h>
#include <msclr/marshal_cppstd.h>
#using <mscorlib.dll>
namespace IRCclient {
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
using namespace System::IO;
using namespace System::Runtime::InteropServices;
using namespace msclr::interop;
/// <summary>
/// Summary for MyForm
/// </summary>
private: System::Void connect_button_Click(System::Object^ sender, System::EventArgs^ e)
{
if ((server_box->Text == "") || (port_box->Text == "") || (username_box->Text == "") || (channel_box->Text == ""))
{
MessageBox::Show("Wypełnij wszystkie pola", "Puste pola", MessageBoxButtons::OK, MessageBoxIcon::Warning);
server_box->Focus();
}
else
{
String^ host_string = server_box->Text;
char* host = (char*)(void*)Marshal::StringToHGlobalAnsi(host_string);
String^ port_string = port_box->Text;
int port;
//String^ port_string = port.ToString();
String^ nick_string = username_box->Text;
std::string nick(marshal_as<std::string>(nick_string));
std::string user = "test";
IRCClient client;
volatile bool running;
Thread thread;
thread.Start(&inputThread, &client);
if (client.InitSocket())
{
content_box->Text = "Socket initialized. Connecting..." + "\r\n";
if (client.Connect(host, port))
{
content_box->Text = "Connected. Loggin in..." + "\r\n";
if (client.Login(nick, user))
{
content_box->Text = "Logged." + "\r\n";
running = true;
signal(SIGINT, signalHandler);
while (client.Connected() && running)
client.ReceiveData();
}
if (client.Connected())
client.Disconnect();
content_box->Text = "Disconnected." + "\r\n";
}
}
}
};
};
};
In the Magic.h file make this change:
extern ConsoleCommandHandler commandHandler;
Then in the Magic.cpp file add this code:
ConsoleCommandHandler commandHandler;
Otherwise both Magic.obj and IRC.obj will end up with ConsoleCommandHandler commandHandler because the header will reserve it twice, once in each obj file.
I'm writing a server and I wanted to use XML with my Java client. I'm using CygWin with XercesC 3.1.1 for my development test and this works fine (I looped 30000 with this function and had no crash). However, on my target machine it's running HP-UX with XercesC 2.7. To implement the differences in the XercesC implementation I wrote a separate class to handle each version.
When I try to run the code with XercesC 2.7. I always get a NULL pointer when I try to create the DOMWriter, and a SIGABORT when trying again.
Since I couldn't find anyything on google I hope that someone can shed some light on what I'm doing wrong here. I've been looking at the sample code provided with the XercesC souorce, and I also have some production code from fellow programmers, and I can't see any difference for the live of it.
I tried to create an SSCE which is a bit long, but it is the shortest sample that I could create.
xml_serialize.h
#ifndef XML_SERIALIZE_H_
#define XML_SERIALIZE_H_
#include <string>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/dom/DOM.hpp>
#if defined(XERCES_NEW_IOSTREAMS)
#include <iostream>
#else
#include <iostream.h>
#endif
#include <xercesc/util/OutOfMemoryException.hpp>
class XStr
{
public :
// -----------------------------------------------------------------------
// Constructors and Destructor
// -----------------------------------------------------------------------
XStr(const char* const toTranscode)
{
// Call the private transcoding method
fUnicodeForm = xercesc::XMLString::transcode(toTranscode);
}
~XStr()
{
xercesc::XMLString::release(&fUnicodeForm);
}
// -----------------------------------------------------------------------
// Getter methods
// -----------------------------------------------------------------------
const XMLCh* unicodeForm() const
{
return fUnicodeForm;
}
private :
// -----------------------------------------------------------------------
// Private data members
//
// fUnicodeForm
// This is the Unicode XMLCh format of the string.
// -----------------------------------------------------------------------
XMLCh* fUnicodeForm;
};
#define X(str) XStr(str).unicodeForm()
std::string fromXMLString(XMLCh *oXMLString);
class XMLSerialize
{
private:
xercesc::DOMImplementation *mImpl;
protected:
xercesc::DOMImplementation *getDOMImplementation(void);
public:
XMLSerialize(void);
virtual ~XMLSerialize(void);
public:
/**
* Creates an empty DOM
*/
xercesc::DOMDocument *createDocument(const std::string &oDocumentName);
/**
* Parses an XML from a string.
*/
xercesc::DOMDocument *parseDocument(const std::string &oDocumentName, std::string const &oReferenceId);
/**
* Serializes the document into a string
*/
int serialize(xercesc::DOMDocument *oDocument, std::string &oXMLOut, bool bDocumentRelease = true);
};
#endif /* XML_SERIALIZE_H_ */
xml_serialize.cpp
#include <xercesc/util/XMLString.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/util/TransService.hpp>
#include <xercesc/framework/MemBufFormatTarget.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>
#include <sstream>
#include <vector>
#include <iostream>
#include "xml_serialize.h"
int serializeEnvironment(void);
XMLSerialize *serializer = NULL;
XMLSerialize::XMLSerialize()
{
mImpl = xercesc::DOMImplementationRegistry::getDOMImplementation(X("Core"));
}
XMLSerialize::~XMLSerialize()
{
}
xercesc::DOMDocument *XMLSerialize::createDocument(const std::string &oDocumentName)
{
if(mImpl == NULL)
return NULL;
xercesc::DOMDocument *doc = mImpl->createDocument(
0, // root element namespace URI.
X(oDocumentName.c_str()), // root element name
0); // document type object (DTD).
if(doc == NULL)
return NULL;
return doc;
}
int XMLSerialize::serialize(xercesc::DOMDocument *oDocument, std::string &oXMLOut, bool bDocumentRelease)
{
int result = 0;
XMLCh *xmlUnicode = NULL;
char *strXML = NULL;
xercesc::DOMWriter *serializer = NULL;
if(mImpl == NULL)
{
oXMLOut = "ERROR: XercesC DOMImplementationRegistry not initialized";
result = 1;
goto Quit;
}
serializer = ((xercesc::DOMImplementationLS*)mImpl)->createDOMWriter();
if(serializer == NULL)
{
oXMLOut = "ERROR: XercesC unable to instantiate a DOMWriter!";
result = 2;
goto Quit;
}
xmlUnicode = serializer->writeToString(*oDocument);
strXML = xercesc::XMLString::transcode(xmlUnicode);
oXMLOut = strXML;
if(bDocumentRelease == true)
oDocument->release();
result = 0;
Quit:
if(strXML != NULL)
xercesc::XMLString::release(&strXML);
if(xmlUnicode != NULL)
xercesc::XMLString::release(&xmlUnicode);
if(serializer != NULL)
serializer->release();
return result;
}
int serializeEnvironment(void)
{
int errorCode = 0;
xercesc::DOMElement *rootElem = NULL;
xercesc::DOMElement *item = NULL;
xercesc::DOMElement *element = NULL;
xercesc::DOMText *nameNode = NULL;
xercesc::DOMCDATASection *dataNode = NULL;
std::string xml;
try
{
xercesc::DOMDocument *doc = serializer->createDocument("EnvironmentList");
if(doc == NULL)
return 1;
rootElem = doc->getDocumentElement();
std::vector<std::pair<std::string, std::string> > env;
for(int i = 0; i < 5; i++)
{
std::string key;
std::string value;
std::stringstream ss;
ss << "KEY";
ss << i;
ss >> key;
ss.clear();
ss << "VALUE";
ss << i;
ss >> value;
ss.clear();
env.push_back(std::make_pair(key, value));
}
for(std::vector<std::pair<std::string, std::string> >::const_iterator it = env.begin(); it != env.end(); ++it)
{
std::pair<std::string, std::string>entry = *it;
std::string name = entry.first;
std::string value = entry.second;
if(value.empty())
value = "";
item = doc->createElement(X("item"));
rootElem->appendChild(item);
element = doc->createElement(X("item"));
nameNode = doc->createTextNode(X(name.c_str()));
item->appendChild(element);
element->appendChild(nameNode);
element = doc->createElement(X("item"));
dataNode = doc->createCDATASection(X(value.c_str()));
item->appendChild(element);
element->appendChild(dataNode);
}
errorCode = serializer->serialize(doc, xml);
std::cout << xml << std::endl;
doc->release();
errorCode = 0;
}
catch (const xercesc::OutOfMemoryException&)
{
XERCES_STD_QUALIFIER cerr << "OutOfMemoryException" << XERCES_STD_QUALIFIER endl;
errorCode = 2;
}
catch (const xercesc::DOMException& e)
{
XERCES_STD_QUALIFIER cerr << "DOMException code is: " << e.code << XERCES_STD_QUALIFIER endl;
errorCode = 3;
}
catch (...)
{
XERCES_STD_QUALIFIER cerr << "An error occurred creating the document" << XERCES_STD_QUALIFIER endl;
errorCode = 4;
}
return errorCode;
}
int main()
{
xercesc::XMLPlatformUtils::Initialize();
serializer = new XMLSerialize();
int error = 0;
for(int i = 0; i < 2; i++)
{
std::cout << "Serializing:" << i << " ... " << std::endl;
if((error = serializeEnvironment()) != 0)
std::cout << "ERROR" << error << std::endl;
std::cout << "Done" << std::endl;
}
xercesc::XMLPlatformUtils::Terminate();
return 0;
}
output
Serializing:0 ...
ERROR: XercesC unable to instantiate a DOMWriter!
Done
Serializing:1 ...
aCC runtime: pure virtual function called for class "xercesc_2_7::DOMImplementationLS".
Abort(coredump)
update
I finally managed to compile 2.7 for cygwin and tested the above code there. This works fine, so there must be some problem with the HP-UX environment.
I was compiling the code with gcc and aparently the xerces library was compiled with aCC. So nopw I switched to aCC in my makefile and now it works.
One should expect that the produced libraries are compatible, but apparently this is not the case. So the above code is actually correct.