values on the output are duplicated
CODE:
enum class status_t
{
_success,
};
const char* set_status(status_t status)
{
switch(status)
{
case status_t::_success: return xorstr_("[SUCCESS]");
}
}
enum class action_t
{
_launch,
};
const char* set_action(action_t action)
{
switch (action)
{
case action_t::_launch: return xorstr_("[LAUNCH]");
}
}
void record(const char* status, const char* action, char* message)
{
const char* directory = "C:\\log.txt";
std::ofstream out(directory, std::ios_base::out | std::ios_base::app);
out << status << char(0x20) << action << char(0x20) << message << '\n';
out.close();
}
int main()
{
record(set_status(status_t::_success), set_action(action_t::_launch), "success");
}
output: [SUCCESS] [SUCCESS] success
what can cause such an unusual problem?
I do not know what stackoverflow wants me to write here, enough to understand the problem
Related
I am unable to receive data over serial port in boost::asio while using asynchronous. When I use synchronous routines I am able to receive data.
Code :
SerialPort.cpp
bool SerialPort::read_async(std::uint32_t read_timeout)
{
try
{
if (read_timeout not_eq SerialPort::ignore_timeout)
this->read_timeout = read_timeout;//If read_timeout is not set to ignore_timeout, update the read_timeout else use old read_timeout
this->port.async_read_some(boost::asio::buffer(this->read_buffer.data(), this->read_buffer.size()),
boost::bind(&SerialPort::read_handler, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
return true;
}
catch (const std::exception& ex)
{
PLOG_ERROR << ex.what();
return false;
}
}
void SerialPort::read_handler(const boost::system::error_code& error, std::size_t bytes_transferred)
{
std::string received_data_buffer;
std::transform(this->read_buffer.begin(), this->read_buffer.begin() + bytes_transferred,
std::back_inserter(received_data_buffer), [](std::byte character) {
return static_cast<char>(character);
});
PLOG_INFO << "In Read Buffer : " << received_data_buffer;
}
bool SerialPort::open_port(void)
{
try
{
this->port.open(this->port_name);
return true;
}
catch (const std::exception& ex)
{
PLOG_FATAL << ex.what();
}
return false;
}
SerialPort.hpp
class SerialPort
{
private:
boost::asio::io_context io;
boost::asio::serial_port port;
boost::asio::serial_port::native_handle_type native_port;
std::string port_name;
const static std::uint32_t READ_BUFFER_MAX_LENGTH{ 8096 };
std::array<std::byte, SerialPort::READ_BUFFER_MAX_LENGTH> read_buffer;//Used in synchronous read
void read_handler(
const boost::system::error_code& error, // Result of operation.
std::size_t bytes_transferred // Number of bytes read.
);
//boost::asio::deadline_timer timer;
public:
SerialPort() : io(), port(io), thread_sync_read()
{
}
~SerialPort();
bool open_port(void);
bool read_async(std::uint32_t read_timeout = SerialPort::ignore_timeout);
};
main.cpp
SerialPort sp;
int main()
{
sp.open_port("COM11");
sp.write_sync("Testing123");
sp.read_async();
while (true)
{
}
return 0;
}
You're supposedly trying to do some operations asynchronously.
Firstly, mixing synchronous and asynchronous operations is not always advisable. Some services/IO objects might hold inner state that assumes one or the other.
Secondly, the asynchronous operation requires the io_service to be run. That doesn't happen. You could make it explicit instead of the current while() loop.
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <iostream>
namespace asio = boost::asio;
using boost::system::error_code;
std::ostream PLOG_INFO(std::clog.rdbuf());
std::ostream PLOG_ERROR(std::clog.rdbuf());
std::ostream PLOG_FATAL(std::clog.rdbuf());
class SerialPort
{
public:
SerialPort()
: io_()
, port_(io_) /*, thread_sync_read()*/
{}
~SerialPort() = default;
bool open_port(std::string name);
static constexpr uint32_t ignore_timeout = -1;
bool read_async(std::uint32_t read_timeout = SerialPort::ignore_timeout);
void run() { io_.run(); }
private:
static constexpr uint32_t READ_BUFFER_MAX_LENGTH{8096};
asio::io_context io_;
asio::serial_port port_;
std::string port_name_;
uint32_t read_timeout_ = ignore_timeout;
// asio::deadline_timer timer;
std::array<std::byte, READ_BUFFER_MAX_LENGTH> read_buffer_;
void read_handler(error_code error, size_t bytes_transferred);
};
bool SerialPort::read_async(std::uint32_t read_timeout) {
try {
if (read_timeout != SerialPort::ignore_timeout)
read_timeout_ =
read_timeout; // If read_timeout is not set to ignore_timeout,
// update the read_timeout else use old
// read_timeout
port_.async_read_some(
asio::buffer(read_buffer_.data(), read_buffer_.size()),
boost::bind(&SerialPort::read_handler, this,
asio::placeholders::error,
asio::placeholders::bytes_transferred));
return true;
} catch (const std::exception& ex) {
PLOG_ERROR << ex.what() << std::endl;
return false;
}
}
void SerialPort::read_handler(error_code error, size_t bytes_transferred) {
std::string s;
std::transform(
read_buffer_.begin(), read_buffer_.begin() + bytes_transferred,
std::back_inserter(s),
[](std::byte character) { return static_cast<char>(character); });
PLOG_INFO << "In Read Buffer : " << s << " (" << error.message() << ")" << std::endl;
}
bool SerialPort::open_port(std::string name) {
try {
port_name_ = std::move(name);
port_.open(port_name_);
return true;
} catch (std::exception const& ex) {
PLOG_FATAL << ex.what() << std::endl;
return false;
}
}
SerialPort sp;
int main(int argc, char** argv) {
sp.open_port(argc > 1 ? argv[1] : "COM11");
// sp.write_sync("Testing123");
sp.read_async();
sp.run();
}
I am using qDebug(), qInfo() and so on in combination of qInstallMessageHandler to write my logfiles. I also get an output on my batch screen, when executing my application.
I only found QT_NO_DEBUG_OUTPUT, but I want to toggle this configuration while run-time. Is there a way to prevent Qt from writing to std output?
sadly no, you only get access to the messages, but cannot prevent from beeing written to std output.
That's false in Qt 5 at the very least. The message printing code looks as follows: you can clearly see that only the message handler is being used:
if (grabMessageHandler()) {
// prefer new message handler over the old one
if (msgHandler.load() == qDefaultMsgHandler
|| messageHandler.load() != qDefaultMessageHandler) {
(*messageHandler.load())(msgType, context, message);
} else {
(*msgHandler.load())(msgType, message.toLocal8Bit().constData());
}
ungrabMessageHandler();
} else {
fprintf(stderr, "%s\n", message.toLocal8Bit().constData());
}
As you can see, the fprintf(stderr, ...) is a fallback only if recursion is detected from within the messageHandler itself.
Thus, all you need to prevent any debug output is to implement and set your own messageHandler.
To completely turn off all the debug output in Qt, the following works:
#include <QtCore>
int main() {
qDebug() << "I'm not quiet at all yet";
qInstallMessageHandler(+[](QtMsgType, const QMessageLogContext &, const QString &){});
qDebug() << "I'm very, very quiet";
}
In any case, a sensible implementation of an application-wide file logger might look as follows - it doesn't recreate the QTextStream unnecessarily; it uses QString::toUtf8() instead, and explicitly writes line endings.
#include <QtCore>
class Logger {
static struct Data {
Logger *instance;
QtMessageHandler chainedHandler;
} d;
bool m_isOpen;
QFile m_logFile;
QtMessageHandler m_oldHandler = {};
static void handler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
if (d.instance)
d.instance->log(msg);
if (d.chainedHandler)
d.chainedHandler(type, context, msg);
}
public:
enum ChainMode { DontChain, Chain };
Logger() {
Q_ASSERT(!instance());
m_logFile.setFileName("myLog.txt");
m_isOpen = m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
d.instance = this;
}
~Logger() { uninstallHandler(); }
bool isOpen() const { return m_isOpen; }
void installHandler(ChainMode mode) {
Q_ASSERT(!m_oldHandler);
m_oldHandler = qInstallMessageHandler(handler);
if (mode == Chain)
d.chainedHandler = m_oldHandler;
}
void uninstallHandler() {
if (m_oldHandler) {
m_oldHandler = nullptr;
d.chainedHandler = nullptr;
qInstallMessageHandler(m_oldHandler);
}
}
/// This method is *not* thread-safe. Use with a thread-safe wrapper such as `qDebug`.
void log(const QString & msg) {
if (isOpen()) {
m_logFile.write(msg.toUtf8());
m_logFile.write("\n", 1);
}
}
/// Closes the log file early - this is mostly used for testing.
void endLog() {
uninstallHandler();
m_logFile.close();
}
static Logger *instance() { return d.instance; }
};
Logger::Data Logger::d;
template <typename T> QByteArray streamOutputFor(const T &data) {
QBuffer buf;
buf.open(QIODevice::ReadWrite | QIODevice::Text);
QTextStream s(&buf);
s << data << endl;
buf.close();
return buf.data();
}
QByteArray readEnd(const QString &fileName, int count) {
QFile file(fileName);
if (file.open(QIODevice::ReadOnly | QIODevice::Text) && file.size() >= count) {
file.seek(file.size() - count);
return file.readAll();
}
return {};
}
void test() {
auto const entry = QDateTime::currentDateTime().toString().toUtf8();
Q_ASSERT(Logger::instance()->isOpen());
qDebug() << entry.data();
Logger::instance()->endLog();
auto reference = streamOutputFor(entry.data());
auto readback = readEnd("myLog.txt", reference.size());
Q_ASSERT(!reference.isEmpty());
Q_ASSERT(readback == reference);
}
int main() {
Logger logger;
logger.installHandler(Logger::DontChain);
qDebug() << "Something or else";
test();
}
To create a boost::process with output redirection, you should do:
bp::ipstream out;
bp::child c("c++filt", std_out > out);
Now, what would be the syntax to conditionally control the redirection:
bool redirect = true; // or false
bp::ipstream out;
bp::child c("c++filt", (redirect) ? std_out > out : "what should I put here??" );
The fancy boost::process API revolves around supplying handler objects implementing on_setup, on_error, on_success (and potentially some more, depending on current OS) methods that will be executed at process construction call in context of some internal process launcher and will be able to alter process launcher behavior. std_out > out is an overloaded operator that will return such handler. More information can be found at boost process Extensions documentation.
So the generic way to conditionally control the redirection and other parameters would be to write generic handler proxy accepting optional real handler and calling appropriate methods of real handler:
#include <boost/process.hpp>
#include <boost/process/extend.hpp>
#include <boost/optional.hpp>
#include <memory>
#include <utility>
template<typename TRealHandler> class
t_OptionalHandler
: public ::boost::process::extend::handler
{
private: using t_OptionalRealHandler = ::boost::optional<TRealHandler>;
private: t_OptionalRealHandler m_real_handler;
public:
t_OptionalHandler(void)
: m_real_handler{}
{}
public: explicit
t_OptionalHandler(TRealHandler && real_handler)
: m_real_handler{::std::move(real_handler)}
{}
template<typename TExecutor>
void on_setup(TExecutor & e) const
{
if(m_real_handler)
{
m_real_handler.get().on_setup(e);
}
}
template<typename TExecutor>
void on_error(TExecutor & e, const std::error_code & code) const
{
if(m_real_handler)
{
m_real_handler.get().on_error(e, code);
}
}
template<typename TExecutor>
void on_success(TExecutor & e) const
{
if(m_real_handler)
{
m_real_handler.get().on_success(e);
}
}
};
int main()
{
bool const need_to_redirect{false};
::std::unique_ptr<::boost::process::ipstream> const p_stream
{
need_to_redirect?
new ::boost::process::ipstream{}
:
nullptr
};
using t_OptionalStdOutRedirectionHandler = t_OptionalHandler
<
decltype(::boost::process::std_out > *p_stream)
>;
::boost::process::child ch
(
"cmd"
, need_to_redirect?
t_OptionalStdOutRedirectionHandler{::boost::process::std_out > *p_stream}
:
t_OptionalStdOutRedirectionHandler{}
);
ch.wait();
return(0);
}
I've been there.
Indeed, launcher functions (not exactly factories, but composable procedural wrappers) were what I used.
Intro
We had a CommandRunner with a legacy implementation that I rewrote to use Boost. I'm skipping the public interface:
class CommandRunner {
public: struct IRunnerImpl;
};
The implementation was Pimpl-ed and worked with the base implementation storing mostly simple implementation-independent parameters:
struct CommandRunner::IRunnerImpl {
virtual ~IRunnerImpl() = default;
virtual void run() = 0;
virtual void ensure_completed() = 0;
virtual std::string to_string() const = 0;
friend class CommandRunner;
protected:
std::string _working_directory;
mylibrary::optional<mylibrary::io::time::duration> _time_constraint;
std::string _stdin_data;
int _redirected_output_fd = -1;
std::string _redirected_output_fname;
bool _discard_output = false;
int _exit_code;
std::string _stdout_str;
std::string _stderr_str;
bool _command_timed_out = false;
bool _sensitive_args = false;
string_map_t _env;
};
The Boost RunnerImpl
The core of ensure_completed was composed using helper lambdas like this:
try {
mylibrary::threads::safe_io_service safe_ios;
boost::asio::io_service& ios = safe_ios;
mylibrary::io::time::timer deadline(ios);
bp::group process_group;
bp::async_pipe input(ios);
std::future<std::string> output, error;
if (_working_directory.empty())
_working_directory = ".";
auto on_exit = [this, &deadline](int exit, std::error_code ec) {
if (!_command_timed_out) {
_exit_code = exit;
}
deadline.cancel();
if (ec) s_logger.log(LOG_WARNING) << "Child process returned " << ec.message();
else s_logger.log(LOG_DEBUG) << "Child process returned";
};
auto launcher = [](auto&&... args) { return bp::child(std::forward<decltype(args)>(args).../*, bp::posix::fd.restrict_inherit()*/); };
auto redirect_out = [&](auto f) {
return [&,f](auto&&... args) {
if (_discard_output) {
if (_redirected_output_fd != -1 && !_redirected_output_fname.empty()) {
s_logger.log(LOG_ERR) << "Ignoring output redirection with set_discard_output. This is a bug.";
}
return f(std::forward<decltype(args)>(args)..., bp::std_out > bp::null, bp::std_err > bp::null);
}
if (_redirected_output_fd != -1 && !_redirected_output_fname.empty()) {
s_logger.log(LOG_WARNING) << "Conflicting output redirection, ignoring filename with descriptor";
}
if (_redirected_output_fd != -1) {
return f(std::forward<decltype(args)>(args)..., bp::posix::fd.bind(1, _redirected_output_fd), bp::std_err > error);
}
return _redirected_output_fname.empty()
? f(std::forward<decltype(args)>(args)..., bp::std_out > output, bp::std_err > error)
: f(std::forward<decltype(args)>(args)..., bp::std_out > _redirected_output_fname, bp::std_err > error);
};
};
bp::environment bp_env = boost::this_process::environment();
for (auto& p : _env)
bp_env[p.first] = p.second;
auto c = redirect_out(launcher)(_command_path, _args,
process_group,
bp::std_in < input,
bp::start_dir(_working_directory),
bp_env,
ios, bp::on_exit(on_exit)
);
if (_time_constraint) {
deadline.expires_from_now(*_time_constraint);
deadline.async_wait([&](boost::system::error_code ec) {
if (ec != boost::asio::error::operation_aborted) {
if (ec) {
s_logger.log(LOG_WARNING) << "Unexpected condition in CommandRunner deadline: " << ec.message();
}
_command_timed_out = true;
_exit_code = 1;
::killpg(process_group.native_handle(), SIGTERM);
deadline.expires_from_now(3s); // grace time
deadline.async_wait([&](boost::system::error_code ec) { if (!ec) process_group.terminate(); }); // timed out
}
});
}
boost::asio::async_write(input, bp::buffer(_stdin_data), [&input](auto ec, auto bytes_written){
if (ec) {
s_logger.log(LOG_WARNING) << "Standard input rejected: " << ec.message() << " after " << bytes_written << " bytes written";
}
may_fail([&] { input.close(); });
});
ios.run();
if (output.valid()) _stdout_str = output.get();
if (error.valid()) _stderr_str = error.get();
// make sure no grand children survive
if (process_group && process_group.joinable() && !process_group.wait_for(1s))
process_group.terminate();
// Note: even kills get the child reaped; 'on_exit' handler is
// actually the 'on wait_pid() complete'). No need for c.wait()
// in this scenario
//// may_fail([&] { if (c.running()) c.wait(); }); // to avoid zombies
} catch(bp::process_error const& e) {
if (e.code().value() != static_cast<int>(std::errc::no_child_process)) throw;
}
Full Self-Contained Listing
This compiles, but doesn't have any public interface. Merely for expositional purposes.
Note:
this didn't go into production as there were cases in which the Boost implementation would get stuck in async IO operations. The reason for this has remained unclear, but I suspect it is a hairy case of epoll is fundamentally broken related to our use cases being "wildly" multi-threaded and forking.
this does not include our attempted workarounds for safe_io_service (that guarantees fork synchronization and notifications on all active io_services)
this does not include our patch that adds restricted FD inheritance:
auto launcher = [](auto&&... args) { return
bp::child(std::forward<decltype(args)>(args)...,
bp::posix::fd.restrict_inherit()); };
other crucial things like on-fork handlers for global (library) state have not been shown here (they used pthread_atfork and similar)
Compiling On Coliru
#include <boost/process.hpp>
#include <boost/process/extend.hpp>
#include <boost/process/async.hpp>
#include <boost/process/posix.hpp>
#include <boost/optional.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <signal.h> // ::killpg
#include <iomanip>
#include <map>
///////
// mockups for standalone exposition
//#include "log.h"
enum LOG_LEVELS{LOG_DEBUG, LOG_WARNING, LOG_ERR};
struct DummyLogger {
struct Tx {
template <typename...Ts> friend Tx operator<<(Tx const&, Ts&&...) { return {}; }
};
Tx log(LOG_LEVELS) { return {}; }
} s_logger;
//#include "safe_io_service.h"
namespace mylibrary {
namespace threads {
//this did manage fork notifications
using safe_io_service = boost::asio::io_service;
}
using boost::optional;
namespace io { namespace time {
using clock = std::chrono::high_resolution_clock;
using duration = clock::duration;
using timer = boost::asio::high_resolution_timer;
}
}
}
using namespace std::chrono_literals;
using string_vector_t = std::vector<std::string>;
using string_map_t = std::map<std::string, std::string>;
class CommandRunner {
public: struct IRunnerImpl;
};
struct CommandRunner::IRunnerImpl {
virtual ~IRunnerImpl() = default;
virtual void run() = 0;
virtual void ensure_completed() = 0;
virtual std::string to_string() const = 0;
friend class CommandRunner;
protected:
std::string _working_directory;
mylibrary::optional<mylibrary::io::time::duration> _time_constraint;
std::string _stdin_data;
int _redirected_output_fd = -1;
std::string _redirected_output_fname;
bool _discard_output = false;
int _exit_code;
std::string _stdout_str;
std::string _stderr_str;
bool _command_timed_out = false;
bool _sensitive_args = false;
string_map_t _env;
};
//
//////////////////////////////////////
namespace {
namespace bp = boost::process;
template <typename F> static auto may_fail(F&& f) {
try {
return std::forward<F>(f)();
} catch(std::exception const& e) {
s_logger.log(LOG_DEBUG) << "Ignoring non-fatal error (" << e.what() << ")";
} catch(...) {
s_logger.log(LOG_DEBUG) << "Ignoring non-fatal, unspecified error";
}
}
}
namespace mylibrary { namespace process { namespace with_boost {
struct BoostRunnerImpl : CommandRunner::IRunnerImpl {
BoostRunnerImpl(std::string cmd_path, string_vector_t args) :
_command_path(std::move(cmd_path)),
_args(std::move(args))
{
}
std::string _command_path;
string_vector_t _args;
virtual void run() override {
if (_completed) {
s_logger.log(LOG_DEBUG) << "NOT running already completed command: " << *this;
return;
}
ensure_completed();
}
////////////////////////////
// implementation
virtual void ensure_completed() override {
if (_completed) return;
// Log command and args
s_logger.log(LOG_DEBUG) << "Running command: " << *this;
try {
try {
mylibrary::threads::safe_io_service safe_ios;
boost::asio::io_service& ios = safe_ios;
mylibrary::io::time::timer deadline(ios);
bp::group process_group;
bp::async_pipe input(ios);
std::future<std::string> output, error;
if (_working_directory.empty())
_working_directory = ".";
auto on_exit = [this, &deadline](int exit, std::error_code ec) {
if (!_command_timed_out) {
_exit_code = exit;
}
deadline.cancel();
if (ec) s_logger.log(LOG_WARNING) << "Child process returned " << ec.message();
else s_logger.log(LOG_DEBUG) << "Child process returned";
};
auto launcher = [](auto&&... args) { return bp::child(std::forward<decltype(args)>(args).../*, bp::posix::fd.restrict_inherit()*/); };
auto redirect_out = [&](auto f) {
return [&,f](auto&&... args) {
if (_discard_output) {
if (_redirected_output_fd != -1 && !_redirected_output_fname.empty()) {
s_logger.log(LOG_ERR) << "Ignoring output redirection with set_discard_output. This is a bug.";
}
return f(std::forward<decltype(args)>(args)..., bp::std_out > bp::null, bp::std_err > bp::null);
}
if (_redirected_output_fd != -1 && !_redirected_output_fname.empty()) {
s_logger.log(LOG_WARNING) << "Conflicting output redirection, ignoring filename with descriptor";
}
if (_redirected_output_fd != -1) {
return f(std::forward<decltype(args)>(args)..., bp::posix::fd.bind(1, _redirected_output_fd), bp::std_err > error);
}
return _redirected_output_fname.empty()
? f(std::forward<decltype(args)>(args)..., bp::std_out > output, bp::std_err > error)
: f(std::forward<decltype(args)>(args)..., bp::std_out > _redirected_output_fname, bp::std_err > error);
};
};
bp::environment bp_env = boost::this_process::environment();
for (auto& p : _env)
bp_env[p.first] = p.second;
auto c = redirect_out(launcher)(_command_path, _args,
process_group,
bp::std_in < input,
bp::start_dir(_working_directory),
bp_env,
ios, bp::on_exit(on_exit)
);
if (_time_constraint) {
deadline.expires_from_now(*_time_constraint);
deadline.async_wait([&](boost::system::error_code ec) {
if (ec != boost::asio::error::operation_aborted) {
if (ec) {
s_logger.log(LOG_WARNING) << "Unexpected condition in CommandRunner deadline: " << ec.message();
}
_command_timed_out = true;
_exit_code = 1;
::killpg(process_group.native_handle(), SIGTERM);
deadline.expires_from_now(3s); // grace time
deadline.async_wait([&](boost::system::error_code ec) { if (!ec) process_group.terminate(); }); // timed out
}
});
}
boost::asio::async_write(input, bp::buffer(_stdin_data), [&input](auto ec, auto bytes_written){
if (ec) {
s_logger.log(LOG_WARNING) << "Standard input rejected: " << ec.message() << " after " << bytes_written << " bytes written";
}
may_fail([&] { input.close(); });
});
ios.run();
if (output.valid()) _stdout_str = output.get();
if (error.valid()) _stderr_str = error.get();
// make sure no grand children survive
if (process_group && process_group.joinable() && !process_group.wait_for(1s))
process_group.terminate();
// Note: even kills get the child reaped; 'on_exit' handler is
// actually the 'on wait_pid() complete'). No need for c.wait()
// in this scenario
//// may_fail([&] { if (c.running()) c.wait(); }); // to avoid zombies
} catch(bp::process_error const& e) {
if (e.code().value() != static_cast<int>(std::errc::no_child_process)) throw;
}
} catch(std::exception const& e) {
s_logger.log(LOG_ERR) << "CommandRunner: " << e.what();
_completed = true;
_exit_code = 1;
throw;
} catch(...) {
s_logger.log(LOG_ERR) << "CommandRunner: unexpected error";
_completed = true;
_exit_code = 1;
throw;
}
_completed = true;
}
private:
bool _completed = false;
friend std::ostream& operator<<(std::ostream& os, BoostRunnerImpl const& i) {
os << i._command_path;
if (i._sensitive_args)
os << " (" << i._args.size() << " args)";
else for (auto& arg : i._args)
os << " " << std::quoted(arg);
return os;
}
virtual std::string to_string() const override {
std::ostringstream oss;
oss << *this;
return oss.str();
}
};
} } }
int main(){}
When i do the initialisation of the TCPServer inside of main it works, when i try to start it with the startServer() function it is not working, i mean i can not establish a connection with putty.
What am i doing wrong here?
Thanks for any help.
class EchoConnection : public TCPServerConnection {
public:
EchoConnection(const StreamSocket& s)
: TCPServerConnection(s) {}
void reply(char buffer[])
{
bzero(buffer, 256);
std::string myWord = "myWord\n\r";
strcpy(buffer, myWord.c_str());
}
void run() {
StreamSocket& ss = socket();
try {
char buffer[256];
int n = ss.receiveBytes(buffer, sizeof(buffer));
while (n > 0) {
reply(buffer);
ss.sendBytes(buffer, sizeof(buffer));
n = ss.receiveBytes(buffer, sizeof(buffer));
}
}
catch (Poco::Exception& exc)
{ std::cerr << "EchoConnection: " << exc.displayText() << std::endl; }
}
};
void startServer()
{
Poco::Net::TCPServer srv(new Poco::Net::TCPServerConnectionFactoryImpl<EchoConnection>, 8089);
srv.start();
SocketAddress sa("localhost", srv.socket().address().port());
StreamSocket ss(sa);
std::string data("hello, world");
ss.sendBytes(data.data(), (int)data.size());
char buffer[256] = { 0 };
int n = ss.receiveBytes(buffer, sizeof(buffer));
std::cout << std::string(buffer, n) << std::endl;
}
int main(int argc, char** argv) {
Poco::Net::TCPServer srv(new Poco::Net::TCPServerConnectionFactoryImpl<EchoConnection>, 8089);
srv.start();
SocketAddress sa("localhost", srv.socket().address().port());
StreamSocket ss(sa);
std::string data("hello, world");
ss.sendBytes(data.data(), (int)data.size());
char buffer[256] = { 0 };
int n = ss.receiveBytes(buffer, sizeof(buffer));
std::cout << std::string(buffer, n) << std::endl;
// startServer(8089);
ModuleData modData;
modData.ModuleNumber = 1;
modData.ModuleTypeId = 1;
string test = modData.serialize();
ifstream dataFile;
dataFile.open("ModuleData.dat");
if (dataFile.is_open())
{
string line;
while (getline(dataFile, line))
{
cout << line << std::endl;
}
dataFile.close();
}
while (1)
{
}
srv.stop();
}
At the end of startServer function srv object is deleted. TCPServer uses own thread but it finishes working in destructor of TCPServer.
Look at implementation of ~TCPServer
TCPServer::~TCPServer() {
try {
stop();
_pDispatcher->release();
...
}
and see stop method implementation
void TCPServer::stop() {
if (!_stopped)
{
_stopped = true; // !!
_thread.join(); // waiting for thread
_pDispatcher->stop();
}
}
the body of thread function is in run method
void TCPServer::run()
{
while (!_stopped) // this flag was set by stop method
{
... // body of thread function
}
}
stop method sets _stopped on true, and for this reason thread finishes working.
#include <xercesc/framework/Wrapper4InputSource.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/sax2/SAX2XMLReader.hpp>
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/dom/DOMLSParser.hpp>
#include <xercesc/dom/DOMImplementation.hpp>
#include <xercesc/sax2/XMLReaderFactory.hpp>
#include <xercesc/framework/MemBufInputSource.hpp>
#include <xercesc/framework/LocalFileInputSource.hpp>
#include <xercesc/sax/ErrorHandler.hpp>
#include <xercesc/sax/SAXParseException.hpp>
#include <xercesc/sax/Parser.hpp>
#include <xercesc/validators/common/Grammar.hpp>
class CErrorHandler : public xercesc::DefaultHandler
{
public:
CErrorHandler();
virtual ~CErrorHandler();
void startElement(const XMLCh* const uri,
const XMLCh* const localname,
const XMLCh* const qname,
const XERCES_CPP_NAMESPACE::Attributes& attrs
);
void endElement(const XMLCh* const uri,
const XMLCh* const localname,
const XMLCh* const qname
);
void characters(const XMLCh* const chars,
const XMLSize_t length
);
void fatalError(const xercesc::SAXParseException&);
};
CErrorHandler::CErrorHandler()
{
}
CErrorHandler::~CErrorHandler()
{
}
void CErrorHandler::startElement(const XMLCh* const uri,
const XMLCh* const localname,
const XMLCh* const qname,
const xercesc::Attributes& attrs)
{
char* name = xercesc::XMLString::transcode(localname);
std::cout << name;
xercesc::XMLString::release(&name);
}
void CErrorHandler::endElement(const XMLCh* const uri,
const XMLCh* const localname,
const XMLCh* const qname)
{
char* name = xercesc::XMLString::transcode(localname);
xercesc::XMLString::release(&name);
}
void CErrorHandler::fatalError(const xercesc::SAXParseException& exception)
{
char* message = xercesc::XMLString::transcode(exception.getMessage());
std::cout << "Error: " << message << " at line: " << exception.getLineNumber() << std::endl;
xercesc::XMLString::release(&message);
}
void CErrorHandler::characters(const XMLCh* const chars,
const XMLSize_t length
)
{
}
bool validateSchema()
{
std::string XSD_SHEMA ="<?xml version=\"1.0\" encoding=\"UTF-8\" ?>...";
try
{
xercesc::XMLPlatformUtils::Initialize();
}
catch (const XERCES_CPP_NAMESPACE::XMLException& toCatch)
{
char* message = xercesc::XMLString::transcode(toCatch.getMessage());
std::cout << "Error during initialization!" << std::endl;
std::cout << "Exception message is: " << message;
XERCES_CPP_NAMESPACE::XMLString::release(&message);
return false;
}
xercesc::SAX2XMLReader* parser = xercesc::XMLReaderFactory::createXMLReader();
parser->setFeature( xercesc::XMLUni::fgSAX2CoreValidation, true);
parser->setFeature( xercesc::XMLUni::fgSAX2CoreNameSpaces, true);
xercesc::DefaultHandler* defaultHandler = new CErrorHandler();
xercesc::ContentHandler* h = new CErrorHandler();
xercesc::MemBufInputSource mis(reinterpret_cast< const XMLByte* >(XSD_SHEMA.c_str() ), XSD_SHEMA.size (), "/schema.xsd");
xercesc::Wrapper4InputSource wmis (&mis, false);
parser->loadGrammar (&wmis, xercesc::Grammar::SchemaGrammarType, true);
parser->setFeature (xercesc::XMLUni::fgXercesUseCachedGrammarInParse, true);
void* id = (void*)("file:///schema.xsd");
parser->setProperty (xercesc::XMLUni::fgXercesSchemaExternalNoNameSpaceSchemaLocation, id);
parser->setContentHandler(h);
parser->setErrorHandler(defaultHandler);
try
{
parser->parse(mXMLFilePath.c_str());
}
catch (const xercesc::XMLException& toCatch)
{
char* message = xercesc::XMLString::transcode(toCatch.getMessage());
std::cout << "Exception message is: "<< message << std::endl;;
xercesc::XMLString::release(&message);
return false;
}
catch (const xercesc::SAXParseException& toCatch)
{
char* message = xercesc::XMLString::transcode(toCatch.getMessage());
std::cout << "Exception message is: " << message << std::endl;;
xercesc::XMLString::release(&message);
return false;
}
catch (...)
{
std::cout << "Unexpected Exception" ;
return false;
}
delete parser;
delete defaultHandler;
return true;
}
I am trying to validate xml file with path mXMLFilePath and xsd schema in string XSD_SHEMA in c++ with Xerces lib.
I created CErrorHandler class and initialized it, set schema nolocation parameter for not setting in xml path to schema.
It build`s, but not work. Have somebody any ideas?
The Xerces library (for both parsing and loading a grammar) can handle input sources (aka classes implementing the InputSource interface). MemBufInputSource would be the class for cases when something exists only in-memory.
XMLPlatformUtils::Initialize();
XercesDOMParser* domParser;
domParser = new XercesDOMParser();
char *str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" elementFormDefault=\"qualified\" attributeFormDefault=\"unqualified\"> \r\n </xs:schema>";
std::string strContent = str;
xercesc::MemBufInputSource pMemBufIS((XMLByte*)strContent.c_str(),
strContent.size(), "xsd");
if (domParser->loadGrammar(pMemBufIS, Grammar::SchemaGrammarType) == NULL)
{
fprintf(stderr, "couldn't load schema\n");
return false;
}
domParser->setValidationScheme(XercesDOMParser::Val_Auto);
domParser->setDoNamespaces(true);
domParser->setDoSchema(true);
domParser->setValidationConstraintFatal(true);
domParser->setExternalNoNamespaceSchemaLocation("C:\\User\\b.xsd");
domParser->setValidationConstraintFatal(true);
domParser->parse("file.xml");
if (domParser->getErrorCount() == 0)
printf("XML file validated against the schema successfully\n");
else
printf("XML file doesn't conform to the schema\n");
#include <xercesc/sax2/SAX2XMLReader.hpp>
#include <xercesc/sax2/XMLReaderFactory.hpp>
#include <xercesc/sax/ErrorHandler.hpp>
#include <xercesc/sax/SAXParseException.hpp>
#include <xercesc/validators/common/Grammar.hpp>
#include <xercesc/parsers/SAXParser.hpp>
#include <xercesc/framework/MemBufInputSource.hpp>
#include <xercesc/util/XMLString.hpp>
#include <string>
#include <iostream>
class CErrorHandler : public xercesc::ErrorHandler
{
public:
/** Warning message method */
void warning(const xercesc::SAXParseException& ex);
/** Error message method */
void error(const xercesc::SAXParseException& ex);
/** Fatal error message method */
void fatalError(const xercesc::SAXParseException& ex);
/** Errors resetter method */
void resetErrors();
private:
/** Based message reporter method */
void reportParseException(const xercesc::SAXParseException& ex);
};
void CErrorHandler::reportParseException(const xercesc::SAXParseException& ex)
{
char* message = xercesc::XMLString::transcode(ex.getMessage());
std::cout << message << " at line " << ex.getLineNumber() << " column " << ex.getColumnNumber() << std::endl;
xercesc::XMLString::release(&message);
}
void CErrorHandler::warning(const xercesc::SAXParseException& ex)
{
reportParseException(ex);
}
void CErrorHandler::error(const xercesc::SAXParseException& ex)
{
reportParseException(ex);
}
void CErrorHandler::fatalError(const xercesc::SAXParseException& ex)
{
reportParseException(ex);
}
void CErrorHandler::resetErrors()
{
}
class CXmlValidator
{
public:
/** Constructor method */
CXmlValidator();
/** Xml file setter method */
void setFilePath(const std::string &filePath);
/** Destructor method */
~CXmlValidator();
/** Xml file with schema validation method */
bool validateSchema();
private:
/** Xml file */
std::string mXMLFilePath;
};
CXmlValidator::CXmlValidator():
mXMLFilePath("")
{
}
CXmlValidator::~CXmlValidator()
{
}
void CXmlValidator::setFilePath(const std::string &filePath)
{
mXMLFilePath = filePath;
}
bool CXmlValidator::validateSchema()
{
std::cout << std::endl;
xercesc::XMLPlatformUtils::Initialize();
std::string xsdFile = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>...";
xercesc::SAX2XMLReader *parser = xercesc::XMLReaderFactory::createXMLReader();
xercesc::ErrorHandler *handler = new CErrorHandler();
xercesc::MemBufInputSource inMemorySchemaSource(reinterpret_cast<const XMLByte*>(xsdFile.c_str()), xsdFile.size (), "/schema.xsd");
parser->loadGrammar(inMemorySchemaSource, xercesc::Grammar::SchemaGrammarType, true);
parser->setFeature(xercesc::XMLUni::fgXercesUseCachedGrammarInParse, true);
parser->setFeature(xercesc::XMLUni::fgSAX2CoreValidation, true);
parser->setFeature(xercesc::XMLUni::fgSAX2CoreNameSpaces, true);
parser->setProperty(xercesc::XMLUni::fgXercesSchemaExternalNoNameSpaceSchemaLocation, const_cast<void*>(static_cast<const void*>("")));
parser->setErrorHandler(handler);
parser->parse("file.xml");
if (parser->getErrorCount() != 0)
{
std::cout << "ERROR: XML file '" << mXMLFilePath << "' not confirm to the schema" << std::endl;
return false;
}
else
{
return true;
}
}
Here is correct realizations of error handler and validator classes, if somebody will need them