I have a problem where I'm having to instantiate instances of objects
earlier than I would like to do so because I need to connect signal
slots through some deep ownership, and I'd like to come up with a way of
storing and forwarding the slots so that I can construct objects closer
to their use site, instead of doing so as member variables.
My basic problem is that I have a process that will download an update
file on a separate thread and send a progress signal to anyone who is
interested. The signal is essentially:
typedef boost::signals2::signal<void (double)> DownloadProgress;
Assume that the implementation of the progress function mentioned
below conforms to this; the nature of the signal itself isn't very
important (although I am using functors for the most part).
The signal is set and the code is called something like this:
Updater updater;
updater.onDownloadProgress(&progress);
updater.runDownloadTask();
When you call updater.runDownloadTask(), it will start the
UpdaterDownloadTask, which starts an HTTPRequest and returns an
HTTPResponse. The HTTPResponse is the piece which interacts with the
network layer and receives the data and contains the DownloadProgress
signal. With this, my implementation looks a bit like (bottom-up from
HTTPResponse, heavily abbreviated to elide methods that aren't
particularly illustrative):
class HTTPResponse
{
public:
// this will be called for every "chunk" the underlying HTTP
// library receives
void processData(const char* data, size_t size)
{
// process the data and then send the progress signal
// assume that currentSize_ and totalSize_ are properly set
progressSignal_(currentSize_ * 100.0 / totalSize_);
}
void onDownloadProgress(const DownloadProgress::slot_type& slot)
{
progressSignal_.connect(slot);
}
private:
DownloadProgress progressSignal_;
};
class HTTPRequest
{
public:
HTTPRequest() : response_(new HTTPResponse) { }
void onDownloadProgress(const DownloadProgress::slot_type& slot)
{
response_->connect(slot);
}
boost::shared_ptr<HTTPResponse> perform()
{
// start the request, which operates on response_.
return response_;
}
private:
boost::shared_ptr<HTTPResponse> response_;
};
class UpdaterDownloadTask : public AsyncTask
{
public:
DownloadTask() : request_(new HTTPRequest) { }
void onDownloadProgress(const DownloadProgress::slot_type& slot)
{
request_->connect(slot);
}
void run()
{
// set up the request_ and:
request_>perform();
}
private:
boost::shared_ptr<HTTPRequest> request_;
};
class Updater
{
public:
Updater() : downloadTask_(new UpdaterDownloadTask) { }
void onDownloadProgress(const DownloadProgress::slot_type& slot)
{
downloadTask_->onDownloadProgress(slot);
}
void runDownloadTask() { downloadTask_.submit() }
private:
boost::shared_ptr<UpdaterDownloadTask> downloadTask_;
};
So, my Updater has to have an instance of UpdaterDownloadTask that's
always around, which has an instance of HTTPRequest, which has an
instance of HTTPResponse—just because I have to forward the slot
connection from Updater (the public API entry point) to HTTPResponse
(where the signal belongs).
I would rather implement UpdaterDownloadTask::run() like so:
void run()
{
HTTPRequest request;
request.onDownloadProgress(slots_);
#if 0
// The above is more or less equivalent to
BOOST_FOREACH(const DownloadProgress::slot_type& slot, slots_)
{
request.onDownloadProgress(slot);
}
#endif
request.perform();
}
This would have similar implications at the HTTPRequest level (so I
don't have to construct the HTTPResponse until I perform the request)
and overall make for a nicer data flow with strong RAII semantics. I've
previously tried defining the slots_ variable as a vector:
std::vector<DownloadProgress::slot_type> slots_;
Yet I can only get this to work if I force the callers to call
onDownloadProgress(boost::ref(slot));.
Has anyone done this successfully, or have a good suggestion on how to
store and forward other than what I'm doing?
I think storing the slots in a vector should work ok. If you want to get rid of the need for boost::ref(...) you can remove the & from the onDownloadProgress parameter (since slot_type is copyable).
Alternatively, you could have your signal inside HTTPResponse fire and in turn fire a signal in HTTPRequest, doing that, you could connect all the slots to the signal in HTTPRequest, then once the HTTPResponse is created, you connect to the response signal onDownloadProgress(request.signalname). Where signalname is the signal your client.
pseudocode:
Request request;
request.onProgress(myProgressBarCallback);
//calls: this.signal.connect(myProgressBarCallback);
request.go();
//calls: Response response;
// and: response.onProgress(this.signal);
I hope that helps.
Related
I'm using a DataRouter class to handle communication with a QSerialPort (and then communicate the results elsewhere). The connected device sends a status package every second or so, and I would like to read it without polling the device. I tried directly using QSerialPort's waitForReadyRead function, but no matter how long I set the wait time, it always timed out. Looking here and here I saw signals can be connected to Lambda functions. Now I'm trying to connect QSerialPort's readyRead signal to a Lambda which calls my on_dataRecieved function but I get the error C2665:"QObject::connect: none of the 3 overloads could convert all of the argument types. Below is an example of what I have:
DataRouter.h
template<class SerialPort>
class DataRouter
{
public:
DataRouter ();
private slots:
on_dataRecieved();
private:
shared_ptr<SerialPort> m_port;
};
DataRouter.cpp
template<class SerialPort>
DataRouter<SerialPort>::DataRouter()
{
m_port = std::make_shared<SerialPort>()
QObject::connect(m_port, &QSerialPort::readyRead, this, [=](){this->on_dataRecieved();})
}
template<class SerialPort>
void DataRouter<SerialPort>::on_dataRecieved()
{
//Do stuff
}
If your "target" is not QObject you need to use the following overload of connect. The problem is that, you are trying to use non-QObject as "context" to determine the lifetime of the connection and that's not possible. To mitigate it you will need to release the connection somehow on DataRouter's destruction; one way is to store what connect() will have returned and call disconnect on it later on.
As for the signal coming from a smart pointer, have you tried this:
connect(m_port->get(), &QSerialPort::readyRead, &DataRouter::on_dataRecieved);
Your m_port is not entity of QSerialPort class, that's why you don't have QSerialPort::readyRead that can be emitted from it. template<class SerialPort> doesn't do what you what, it is just name of templated parameter.
You probably wanted something like this:
DataRouter.h
class DataRouter : QObject
{
public:
DataRouter ();
private slots:
on_dataRecieved();
private:
QSerialPort* m_port;
};
DataRouter.cpp
DataRouter::DataRouter()
{
m_port = new QSerialPort(this);
connect(m_port, &QSerialPort::readyRead, this, &DataRouter::on_dataRecieved);
// or connect(m_port, &QSerialPort::readyRead, this, [this](){this->on_dataRecieved();});
}
void DataRouter::on_dataRecieved()
{
//Do stuff
}
You don't have to wrap Qt classes in smart pointers as long, as you provide parent class for them. Memory freed when parent is destructed.
Hi,
I've got a SIGSEGV when I want to use a QTcpSocket with the following code. (more explanation at the very bottom)
Function to create a QTTCPSocket (which will keep the QTcpSocket pointer):
std::shared_ptr<QTTCPSocket> createQtSocket(std::string host, unsigned int port)
{
QTcpSocket *sock = new QTcpSocket;
sock->connectToHost(QHostAddress(QString::fromStdString(host)), port);
if (!sock->waitForConnected())
throw std::runtime_error("Connection refused");
return std::make_shared<QTTCPSocket>(sock);
}
QTTCPSocket class:
class QTTCPSocket {
public:
QTTCPSocket(QTcpSocket *socket)
: _socket(socket)
{};
~QTTCPSocket() = default;
void send(const std::string &msg)
{
std::cout << msg << std::endl;
_socket->write(&msg[0], static_cast<qint64>(msg.length())); // produces a SIGSEGV if called from a qt event (a button for example)
_socket->waitForBytesWritten(0);
}
private:
QTcpSocket *_socket;
};
Main function:
int main()
{
std::shared_ptr<QTTCPSocket> socket = createQtSocket("127.0.0.1", 33333);
ViewStateMachine vsm(socket);
vsm.init();
vsm.start();
}
ViewStateMachine class (hpp):
class ViewStateMachine {
public:
ViewStateMachine(std::shared_ptr<QTTCPSocket> sock);
void init();
void start();
std::shared_ptr<QTTCPSocket> getSock();
private:
LoginView *_loginView;
std::shared_ptr<QTTCPSocket> _sock;
};
ViewStateMachine class (cpp):
#include "ViewStateMachine.hpp"
ViewStateMachine::ViewStateMachine(std::shared_ptr<QTTCPSocket> socket)
: _sock(std::move(socket))
{
}
void ViewStateMachine::init()
{
_loginView = new LoginView(this);
_loginView->init();
}
void ViewStateMachine::start()
{
_loginView->show();
}
std::shared_ptr<QTTCPSocket> ViewStateMachine::getSock()
{
_sock->send("test1"); // SIGSEGV inside (check above)
return _sock; // if I remove _sock->send("test1"), it SIGSEGV in the shared_ptr's constructor (only if called from a qt event (a button for example)
}
LoginView is a passive class, which register an event with QObject::connect(_connectBtn, SIGNAL(clicked()), this, SLOT(onClick_connectBtn())); with onClick_connectBtn() a member function of LoginView.
All these pieces of code might be confused so here a step-by-step explanation:
Start in main(): create a QTcpSocket * contained in a QTTCPSocket class contained in a std::shared_ptr<QTTCPSocket>.
Create a ViewStateMachine instance with the shared_ptr, which will be stored in the instance. We will call it vsm.
Call vsm.init() which will create a LoginView instance with this (vsm). LoginView will store the ViewStateMachine instance.
Call vsm.start() which will call _loginView.show() (from QT's QWidget), it will show the login view.
Everything works well and we can see the login view.
However, if I want to use my socket in the LoginView :
* when I want to use it from _loginView->init() or in the LoginView's constructor, it works well!
* when I want to use it from LoginView::onClick_connectBtn (called by QT, perhaps in a special environment like a constructor, not sure), it produces a SIGSEGV where I commented above in the code (shared_ptr's constructor or write function from the QT' socket).
To get the socket from LoginView, I use ViewStateMachine::getSock() (_viewStateMachine->getSock()).
Valgrind shows
pure virtual method called
terminate called without an active exception
The shared_ptr's constructor produces a SIGSEGV when it uses its mutex to increment the value.
The write SIGSEGV when I use _socket (QT' socket). The pointer address didn't change from beginning to end.
If there's any other question, please ask!
Thanks a lot for helping :).
EDIT: It works if I replace every std::shared_ptr with a C pointer.
We are using the model set up in the PoCo-project library documentation. A thread/Handler is spawned for every connection to the http server. We want to connect each thread to a shared SessionPoolContainer(SPC). We are working on the assumption that we should instantiate the SPC in the HandlerFactory and give the handler a reference to the SPC.
class Handler: public Poco::Net::HTTPRequestHandler{
public:
Handler(SessionPoolContainer &spc){
//Here is where it goes wrong. "spc is private."
SessionPool sp = spc.getPool("p1");
//Todo fetch a session once we have the sessionpool reference.
}
void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response){
//Do stuff.
}
};
class HandlerFactory : public Poco::Net::HTTPRequestHandlerFactory{
public:
SessionPoolContainer spc;
Poco::Net::HTTPRequestHandler* createRequestHandler(const Poco::Net::HTTPServerRequest &request){
Poco::Data::MySQL::Connector::registerConnector();
AutoPtr<SessionPool> p1 = new SessionPool("MySQL", "host=127.0.0.1;port=3306;db=testdb2;user=bachelor;password=bachelor;compress=true;auto-reconnect=true");
spc.add(p1);
if (request.getContentType().compare("Application/JSON")) {
return new Handler(spc);
}
}
};
class MyWebHTTPServerApplication : public Poco::Util::ServerApplication{
protected:
int main(const std::vector<std::string> &args){
// Instanciate HandlerFactory
Poco::Net::HTTPServer server(new HandlerFactory(), socket, pParams);
server.start();
//SIC
}
};
The error we get from this is (from the 3rd line):
/home/notandi/git/poco-1.7.2-all/cmake_install/debug/include/Poco/Data/SessionPool.h:187:9: error: 'Poco::Data::SessionPool::SessionPool(const Poco::Data::SessionPool&)' is private
SessionPool(const SessionPool&);
^ /home/notandi/QT/MySQLWithPool/main.cpp:68:41: error: within this context
SessionPool sp = spc.getPool("p");
From where I'm sitting this just needs to work and have the reference passed around.
I have tried to "friend class Handler;" in Handler with no change in status.
The relevant part of SessionPoolContainer looks like:
private:
typedef std::map<std::string, AutoPtr<SessionPool>, Poco::CILess> SessionPoolMap;
SessionPoolContainer(const SessionPoolContainer&);
SessionPoolContainer& operator = (const SessionPoolContainer&);
SessionPoolMap _sessionPools;
Poco::FastMutex _mutex;
Do I edit and recompile PoCo with SessionPoolContainer with "friend class Handler;"? How do I get around this or am I just thinking this all wrong?
From the posted code, it looks like the pool container is not needed at all because p1 pool is never added to the pool container; so, even if you could compile, spc would contain no session pools; using SessionPoolContainer makes sense only if you are connecting to multiple databases, in which case you have to add session pool to the pool container:
spc.add(p1);
However, if there is no need for SessionPoolContainer, then just pass the reference to SessionPool to the handler and get a session from it:
class Handler: public Poco::Net::HTTPRequestHandler{
public:
Handler(SessionPool &sp){
Session s = sp.get();
}
//...
};
Look at this code to get a better understanding how to use session pools and containers thereof.
I am developing GUI form in Qt and I wonder how to implement ObserverPattern. Form can subscribe to many data streams distinguished by tickerId, when data stream arrives (new quote is available) my PosixClient (socket wrapper) fires notifyObservers() method what results in update() method of observer being executed.
BUT:
this update() method is void update() and I need to take incoming data records and plot them, count something, just use them. So how can I achieve this, how pass data records to observer?
Data is available to Observable (MarketData object derived from Observable). When data arrives I push it into this Observable and notify observers.
void PosixClient::tickPrice( TickerId tickerId, TickType field, double price, int canAutoExecute) {
printf("tradingclient_1: tickPrice: \n");
for(std::vector<boost::shared_ptr<MarketData> >::iterator it=dataRepository.begin();
it!=dataRepository.end(); it++){
if((*it)->tickerId==tickerId){
(*it)->tickPriceData.push_back(tickSizeRecord(field,price,canAutoExecute));
(*it)->notifyObservers();
//TODO: start thread to store incoming data in repository
}
}
}
Their void update() methods are then called. in order to retrieve a data from this function I decided to pass a function pointer boost::function<> to it as a callback and Observer calls this function pointer that points to my GUI object with incoming data from observable as argument. Is this right approach?
struct MarketData : public QuantLib::Observable {
//public:
MarketData();
MarketData(IB::Contract c, int tickerId):c(c),tickerId(tickerId){}
MarketData(const MarketData& orig);
virtual ~MarketData();
std::vector<IB::Record> tickPriceData; //market data fed in tickPrice
//private:
IB::Contract c;
int tickerId;
};
typedef boost::shared_ptr<MarketData> pMyObservable;
typedef boost::function<void (int tickerId, IB::Record record)> f_action_ptr;
class MarketDataObserver : public QuantLib::Observer{
public:
MarketDataObserver(pMyObservable obs, f_action_ptr ptr)
: observable(obs), f_ptr(ptr){
this->registerWith(observable);
}
MarketDataObserver(const MarketDataObserver &observer)
: Observer(observer),
observable(observer.observable){ // faction_ptr is not copied!
}
void update(){
data=observable->tickPriceData.back();
//printf("new data: %l\n",data.price);
f_ptr(observable->tickerId, data);
}
private:
IB::Record data;
pMyObservable observable;
f_action_ptr f_ptr;
};
PLEASE NOTE:
I am aware of Qt signal/slot mechanism, but in my opinion Qt signal/slot is not at all solution here, when I need dynamically subscribe to data, plot them, show on Qt Form, then delete subscription when Form is canceled. But maybe I am wrong. Am I? I ask for real, working examples, from life, not theoretical dispute.
The usual Qt idiom for the observer pattern are indeed signals and slots. Have the source of the data emit signals and pass the data as an argument of the signal. That's how this is done within Qt -- signals are not used just for the GUI events.
Consider the following in Qt using QtSoap lib:
QtSoapHttpTransport http;
http.setHost("XXXX",3333);
connect(&http, SIGNAL(responseReady()), this, SLOT(getResponse()));
now there is a method i want to call which is:
QtSoapMessage request;
request.setMethod("test");
request.addMethodArgument("xxx","zzzz",xxx);
request.addMethodArgument("xx","xx",xx);
http.submitRequest(Request, "/api/soap");
now i want to have something like this :
QString GetTest(){
while(http.isBusy); // no such a thing as isbusy
return http.getResponse().returnValue().toString();}
or any technique i can use to get the return value or wait for it and get it..
Thanks in advance...
I don't see a problem. The QtSoapHttpTransport reference already has a nice simple example.
If you want to have a getter that blocks and returns only when the response is received, doing active wait (your while loop) is absolutely not a way to go.
You already connect the responseReady signal to your slot, so the only missing thing would be to have a synchronization point that blocks your thread calling getTest until this slot is executed.
class Messenger : public QObject {
Q_OBJECT
public:
Messenger() { /* ... your initialization code with connect ... */ }
void sendRequest() { /* ... your sending code ... */ }
QString getTest() // call this from a worker thread to wait
{ // for a response to arrive and retrieve it
QMutexLocker lock(&responseMutex);
responseReady.wait(&responseMutex);
return http.getResponse().returnValue().toString();
}
public slots:
void getResponse() { // slot called by Qt event loop when response arrives
responseReady.wakeAll();
}
private:
QtSoapHttpTransport http;
QWaitCondition responseReady;
QMutex responseMutex;
};
Note that this design only makes sense if you have a multithreaded application and the thread calling getTest is a working thread, not event-driven thread.
On the other hand, if your application just wants to do something with the received response, there is imho no reason why you need a blocking method in the first place. Just perform your actions in the slot directly - just like it is in the Qt documentation.