How to send custom data through the QWebChannel - c++

I've got issue sending a custom class as an argument of a signal through Qt's web channel.
There isn't any error in the console, not even a warning, for both compilation and run time. Even though, I've got a null as parameters in my JavaScript signal handler. It works perfectly well with QString, int and others but not with my objects.
I saw this post: How to register a class for use it in a QWebChannel signal in Qt. The problem seems to be similar at first, but their solution doesn't work me. I have a public default constructor, a public copy constructor, and a public destructor. I used both qRegisterMetaType() and Q_DECLARE_METATYPE and still nothing.
By printing messages received by qwebchannel.js, I realized that the C++ send the null value. Which means that it doesn't know how to send my class.
Does anyone have an idea of what could be happening or how to solve it? (Propose even if you don't know)
Thanks in advance!
EDIT:
After Research, I realized that issue come from this line in QWebChannel (singalhandler_p.h):
template<class Receiver>
void SignalHandler<Receiver>::dispatch(const QObject *object, const int signalIdx, void **argumentData)
{
Q_ASSERT(m_signalArgumentTypes.contains(object->metaObject()));
const QHash<int, QVector<int> > &objectSignalArgumentTypes = m_signalArgumentTypes.value(object->metaObject());
QHash<int, QVector<int> >::const_iterator signalIt = objectSignalArgumentTypes.constFind(signalIdx);
if (signalIt == objectSignalArgumentTypes.constEnd()) {
// not connected to this signal, skip
return;
}
const QVector<int> &argumentTypes = *signalIt;
QVariantList arguments;
arguments.reserve(argumentTypes.count());
// TODO: basic overload resolution based on number of arguments?
for (int i = 0; i < argumentTypes.count(); ++i) {
const QMetaType::Type type = static_cast<QMetaType::Type>(argumentTypes.at(i));
QVariant arg;
if (type == QMetaType::QVariant) {
arg = *reinterpret_cast<QVariant *>(argumentData[i + 1]);
} else {
arg = QVariant(type, argumentData[i + 1]);
}
arguments.append(arg);
}
m_receiver->signalEmitted(object, signalIdx, arguments);
}
As you can see, when a signal is sent from C++ through the channel, the arguments are converted in QVariant.
After a few tests, I realized that the QVariant::typeName() return the name of my type, but doesn't contain anything from it.
I tried a single code to understand what I'm doing wrong.
MyClass myClass;
QVariant variant = QVariant::fromValue(myClass);
std::cout << (int)doc.isArray() << std::endl;
std::cout << (int)doc.isEmpty() << std::endl;
std::cout << (int)doc.isNull() << std::endl;
std::cout << (int)doc.isObject() << std::endl;
std::cout << variant.typeName() << ": [" << variant.toJsonDocument().toJson().toStdString() << "]" << std::endl;
The header for MyClass is:
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(int a MEMBER a)
public:
MyClass(QObject *parent = NULL);
MyClass(const MyClass &);
int a;
};
Q_DECLARE_METATYPE(MyClass)
And this give the output:
0
1
1
0
MaClass: []
I don't understand why it's always empty. I tried to create getters and setters but the result is the same.

Related

Class serving data via callbacks

I have recently returned to Visual C++ after a while programming in C where callbacks are much easier.
I have a singleton class which controls 0..* connected devices.
My idea is to create a function in this class which will iterate over the set of
connected devices and publish it via a callback to whatever might require it.
e.g.
Singleton class
typedef void (CALLBACK * PortListCallback_t)(ptrConstCComPortInfo_t);
.
.
.
void CCommsMgr::listPorts(PortListCallback_t cb)
{
PortInfoSetConstIter_t i;
for (i = m_setPorts.begin(); i != m_setPorts.end(); i++)
{
cb(*i);
}
}
In the first instance the consumer is an MFC dialog class which works fine if it's callback is static. However in order to access member data/functions of the dialog class I would need to pass 'this' to the singleton class and have it reflected back.
e.g.
Singleton class
typedef void (CALLBACK * PortListCallback_t)(void *, ptrConstCComPortInfo_t);
.
.
.
void CCommsMgr::listPorts(void *pObj, PortListCallback_t cb)
{
PortInfoSetConstIter_t i;
for (i = m_setPorts.begin(); i != m_setPorts.end(); i++)
{
cb(pObj, *i);
}
}
Dialog Class
static void CALLBACK getPorts(void *obj, ptrConstCComPortInfo_t port);
.
.
.
void CALLBACK CMFC_iTFTPDlg::getPorts(void *obj, ptrConstCComPortInfo_t port)
{
CMFC_iTFTPDlg *pThis = (CMFC_iTFTPDlg*)obj;
// do something with it
}
My question - Is there a better way of doing this? Static functions feel like a kludge and I do not want the Singleton class to be constrained by how it might be used.
If I remove the static on getPorts it will not compile. To repeat myself the Singleton class should have no knowledge of it's consumer.
With help from the excellent hints from WhozCraig, this is what I came up with:
#include <functional> // std::function, std::bind, std::placeholders
#include <iostream>
#include <vector>
class ConstCComPortInfo {};
using ptrConstCComPortInfo_t = ConstCComPortInfo*;
using callback_t = void(void*, ptrConstCComPortInfo_t);
using function_t = std::function<callback_t>;
// an example class with a member function to call
class foo {
public:
foo(const std::string& name) : instance_name(name) {}
void bar(void* something, ptrConstCComPortInfo_t c) {
std::cout << "foo::bar(" << instance_name << ") called\n"
"void* = " << something << "\n"
"ptrConstCComPortInfo_t = " << c << "\n";
}
private:
std::string instance_name;
};
// and a free function to call
void free_func(void* something, ptrConstCComPortInfo_t c) {
std::cout << "free_func_called\n"
"void* = " << something << "\n"
"ptrConstCComPortInfo_t = " << c << "\n";
}
int main() {
// some instances of the class
foo via_bind("called_via_bind");
foo via_lambda("called_via_lambda");
ptrConstCComPortInfo_t bork = nullptr; // dummy value
// a vector of callback subscribers
std::vector<function_t> subscribers{
&free_func,
std::bind(&foo::bar, &via_bind, std::placeholders::_1, std::placeholders::_2),
[&via_lambda](void* p, ptrConstCComPortInfo_t c) { via_lambda.bar(p, c); }
};
// perform callbacks
for(auto& cb : subscribers) {
cb(nullptr, bork);
}
}
Output:
free_func_called
void* = 0
ptrConstCComPortInfo_t = 0
foo::bar(called_via_bind) called
void* = 0
ptrConstCComPortInfo_t = 0
foo::bar(called_via_lambda) called
void* = 0
ptrConstCComPortInfo_t = 0

setProperty() returning false

I'm trying to convert a QVariantMap to a custom class derived from QObject but I'm getting the return value of false from setProperty() when it comes to set the property of my enum type. Code goes below:
The MessageHeader.h file:
// deserialization class header
class MessageHeader : public QObject
{
Q_OBJECT
public:
MessageHeader(QObject *parent = 0);
~MessageHeader();
enum class MessageType
{
none = 0,
foo = 1,
baa = 2
};
Q_ENUM(MessageType)
Q_PROPERTY(MessageType type READ getType WRITE setType)
Q_PROPERTY(int ContentLength READ getContentLength WRITE setContentLength)
void setType(MessageType type);
void setContentLength(int ContentLength);
MessageType getType();
int getContentLength();
QString toString();
MessageType type = MessageType::none;
int ContentLength = 0;
};
The MessageHeader.cpp file:
MessageHeader::MessageHeader(QObject *parent)
: QObject(parent)
{
}
MessageHeader::~MessageHeader()
{
}
MessageType MessageHeader::getType()
{
return type;
}
int MessageHeader::getContentLength()
{
return ContentLength;
}
void MessageHeader::setType(MessageType type)
{
this->type = type;
}
void MessageHeader::setContentLength(int ContentLength)
{
this->ContentLength = ContentLength;
}
QString MessageHeader::toString()
{
return QString("NOT IMPLEMENTED YET");
}
And the deserialize function template helper:
template<typename T>
T* Deserialize(const QString &json)
{
bool status = false;
QJson::Parser parser;
QVariantMap map = parser.parse(json.toUtf8(), &status).toMap();
if(!status)
return NULL;
T *obj = new T(); //don't worry about this, I'll rather take this from paramters once this is working
QObject *p = (QObject *) obj; // cast done so that I see setProperty() method
for(QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter)
{
const char *name = iter.key().toLatin1();
const QVariant value = iter.value();
qDebug() << "setting " << name << "=" << value;
// the issue goes below. Here setProperty() return false.
// At this point, name = 'type' and value = 2
assert(p->setProperty(name, value));
}
//QJson::QObjectHelper::qvariant2qobject(map, obj);
return obj;
}
The JSON input string to above function is like this:
"{\"ContentLength\": 100, \"type\": 2}"
The enum type is registered in the main funcction before anything else:
qRegisterMetaType<MessageType>("MessageType");
And here's the QJson library used in this example. I build it on Windows with this .pro file
EDIT:
I just found that the type property can't be find by indexOfProperty()
qDebug() << "id = " << meta->indexOfProperty(name); // print -1, name = 'type'
The enum property can only be set if the variant type is either a QString, QInt or QUInt as could be seen here. So to successfully set the enum property, the variant needs to be one of these types and nothing else. QJson parses any unsigned integers as QULongLong as can be seen here, line 84. So one way is to fork QJson and modify the code so the integer values are converted to QInt and QUInt or read/write the enum values as strings.
Also, putting statements within an assert is not a good idea, but I assume you just wrote that code trying to figure out the problem.
Just as a side note, according to Qt documentation,
[qRegisterMetaType] is useful only for registering an alias (typedef) for every other use case Q_DECLARE_METATYPE and qMetaTypeId() should be used instead.
so replacing qRegisterMetaType<MessageHeader::MessageType>("MessageType") with Q_DECLARE_METATYPE(MessageHeader::MessageType) in your header would be a reasonable move.
Building up on Rostislav's answer, if you have no choice but to receive a QULongLong as input, here is a code snippet to convert it if the property to set is an enum:
#include <QMetaProperty>
const QMetaObject* meta = object->metaObject();
const int index = meta->indexOfProperty(propName);
if (index == -1) {/* report error*/}
if (meta->property(index).isEnumType())
// special case for enums properties: they can be set from QInt or QUInt variants,
// but unsigned integers parsed from json are QULongLong
object->setProperty(propName, propVariant.value<unsigned int>());
else
object->setProperty(propName, propVariant);

QVariant cast to base type

I have some class like this:
class QObjectDerived : public QObject
{
Q_OBJECT
// ...
};
Q_DECLARE_METATYPE(QObjectDerived*)
When this class was stored to QVariant such behaviour occures
QObjectDerived *object = new QObjectDerived(this);
QVariant variant = QVariant::fromValue(object);
qDebug() << variant; // prints QVariant(QObjectDerived*, )
qDebug() << variant.value<QObject*>(); // prints QObject(0x0)
qDebug() << variant.value<QObjectDerived*>(); // QObjectDerived(0x8c491c8)
variant = QVariant::fromValue(static_cast<QObject*>(object));
qDebug() << variant; // prints QVariant(QObject*, QObjectDerived(0x8c491c8) )
qDebug() << variant.value<QObject*>(); // prints QObjectDerived(0x8c491c8)
qDebug() << variant.value<QObjectDerived*>(); // QObject(0x0)
Is there any way to store it in QVariant and be able to get it as QObject* and QObjectDerived*?
Only by writing
QObject *value = variant.value<QObjectDerived*>();
It may be possible to partially specialize qvariant_cast for your type, but that's not a documented supported use case, and I'd be reluctant to rely on it.
qvariant.h (Qt 4.8.6):
template<typename T>
inline T value() const
{ return qvariant_cast<T>(*this); }
...
template<typename T> inline T qvariant_cast(const QVariant &v)
{
const int vid = qMetaTypeId<T>(static_cast<T *>(0));
if (vid == v.userType())
return *reinterpret_cast<const T *>(v.constData());
if (vid < int(QMetaType::User)) {
T t;
if (qvariant_cast_helper(v, QVariant::Type(vid), &t))
return t;
}
return T();
}
QObject * is stored as a built-in QMetaType::QObjectStar type, and QObjectDerived is a user-defined type with id, defined by Meta-type system. Which means, you'll have to cast it manually.

Overwriting virtual functions in body of code

I have a question about virtual functions in C++. I have spent the last hour searching but I'm getting nowhere quickly and I was hoping that you could help.
I have a class that handles transmitting and receiving data. I would like the class to be as modular as possible and as such I would like to make an abstract/virtual method to handle the received messages.
Although I know I could create a new class and overwrite the virtual method, I don't really want to have to create a large array of new classes all implementing the method different ways. In Java you can use listeners and/or override abstract methods in the body of the code when declaring the object as can be seen in the example.
JTextField comp = new JTextField();
comp.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//Handler Code
}
});
Is this possible in C++ or is there a better approach to this sort of problem?
Cheers and Many thanks in advance,
Chris.
Have look at this other SO post Does C++0x Support Anonymous Inner Classes as the question sounds similar.
Functors (function objects) or lambdas could be suitable alternatives.
In C++, you need to declare a new class:
class MyActionListener: public ActionListener
{
public:
void actionPerformed(ActionEvent evt) { ... code goes here ... }
};
The question has already been answered but I thought I'd throw this in for good measure. The SO discussion linked in the example is good but concentrates mostly on replicating the Java experience. Here's a more idiomatic C++ way of doing it:
struct EventArgs
{
int param1;
int param2;
};
class network_io
{
typedef std::function<void (EventArgs)> Event;
typedef std::vector<Event> EventList;
EventList Events;
public:
void AddEventHandler(Event evt)
{
Events.push_back(evt);
}
void Process()
{
int i,j;
i = j = 1;
std::for_each(std::begin(Events), std::end(Events), [&](Event& e)
{
EventArgs args;
args.param1 = ++i;
args.param2 = j++;
e(args);
});
}
};
int main()
{
network_io ni;
ni.AddEventHandler([](EventArgs& e)
{
std::cout << "Param1: " << e.param1 << " Param2: " << e.param2 << "\n";
});
ni.AddEventHandler([](EventArgs& e)
{
std::cout << "The Param1: " << e.param1 << " The Param2: " << e.param2 << "\n";
});
ni.Process();
}

Avoid dynamic_cast with derived classes (Cast Derived class)

I am new to C++ and came to a point, where I generate an overhead with classes. I have a QTcpSocket and read messages from it and create objects, for example MessageJoin, MessagePart, MessageUserData etc. I send these objects to my client and display them (+ do some UI updating).
Now here comes my problem. I tested a few design techniques but all of them are not that nice:
Pass each parameter of a message object in a signal/slot connection to the client - small overhead but not that good-looking
Create a method for each Message-Type (messageJoinReceived, messageNoticeReceived etc.)
Create one method and use dynamic_cast to cast für each class and test it
For a better understanding, I added my dynamic_cast version. As a said, the code looks ugly and unusable. My questions are:
Is there a better way to do it with (a) dynamic_cast
Is there another way (For example a design pattern) to solve such a problem ? maybe add a method in the classes and return the type or something like this
I read about the visitor pattern. This pattern is just for dynamic object types in Getter/Setter methods ?
A few side notes
I can use RTTI
Speed isn't a big deal. Clean and understandable code is more important
I use Qt and have the possiblity to use qobject_cast and signal/slots
Here is my code (Pastebin-Link):
// Default class - contains the complete message (untouched)
class Message
{
public:
QString virtual getRawMessage() { return dataRawMessage; }
protected:
QString dataRawMessage;
};
// Join class - cointains the name of the joined user and the channel
class MessageJoin : public Message
{
public:
MessageJoin(const QString &rawmessage, const QString &channel, const QString &user)
{
dataRawMessage = rawmessage;
dataChannel = channel;
dataUser = user;
}
QString getChannel() { return dataChannel; }
QString getUser(){ return dataUser; }
private:
QString dataChannel;
QString dataUser;
};
// Notice class - contains a notification message
class MessageNotice : public Message
{
public:
MessageNotice(const QString &rawmessage, const QString &text)
{
dataRawMessage = rawmessage;
dataText = text;
}
QString getText() { return dataText;}
private:
QString dataText;
};
// Client code - print message and update UI
void Client::messageReceived(Message *message)
{
if(message)
{
MessageJoin *messagejoin;
MessagePart *messagepart;
MessageNotice *messagenotice;
if((messagejoin = dynamic_cast<MessageJoin *>(message)) != 0)
{
qDebug() << messagejoin->getUser() << " joined " << messagejoin->getChannel();
// Update UI: Add user
}
else if((messagenotice = dynamic_cast<MessageNotice *>(message)) != 0)
{
qDebug() << messagenotice->getText();
// Update UI: Display message
}
else
{
qDebug() << "Cannot cast message object";
}
delete message; // Message was allocated in the library and is not used anymore
}
}
This looks quite similar to the expression problem and AFAIK there is no way to avoid casts if you are going to add new messages and new ways to handle them. However it's not that hard to make more eye pleasing wrap for necessary run-time stuff. Just create a map from message type to corresponding handler using typeid.
#include <functional>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
typedef std::function<void(Message *)> handler_t;
typedef std::unordered_map<
std::type_index,
handler_t> handlers_map_t;
template <class T, class HandlerType>
handler_t make_handler(HandlerType handler)
{
return [=] (Message *message) { handler(static_cast<T *>(message)); };
}
template <class T, class HandlerType>
void register_handler(
handlers_map_t &handlers_map,
HandlerType handler)
{
handlers_map[typeid(T)] = make_handler<T>(handler);
}
void handle(handlers_map_t const &handlers_map, Base *message)
{
handlers_map_t::const_iterator i = handlers_map.find(typeid(*message));
if (i != handlers_map.end())
{
(i->second)(message);
}
else
{
qDebug() << "Cannot handle message object";
}
}
Then register handlers for specific message types:
handlers_map_t handlers_map;
register_handler<MessageJoin>(
handlers_map,
[] (MessageJoin *message)
{
qDebug() << message->getUser() << " joined " << message->getChannel();
// Update UI: Add user
});
register_handler<MessageNotice>(
handlers_map,
[] (MessageNotice *message)
{
qDebug() << message->getText();
// Update UI: Display message
});
And now you can handle messages:
// simple test
Message* messages[] =
{
new MessageJoin(...),
new MessageNotice(...),
new MessageNotice(...),
new MessagePart(...),
};
for (auto m: messages)
{
handle(handlers_map, m);
delete m;
}
Surely you might want to make some improvements like wrapping handlers stuff into reusable class, using QT or boost signals/slots so you can have multiple handlers for a single message, but the core idea is the same.
The visitor pattern could be a good fit i.e.
class Message
{
public:
QString virtual getRawMessage() { return dataRawMessage; }
virtual void accept(Client& visitor) = 0;
protected:
QString dataRawMessage;
};
// Join class - cointains the name of the joined user and the channel
class MessageJoin : public Message
{
public:
MessageJoin(const QString &rawmessage, const QString &channel, const QString &user)
{
dataRawMessage = rawmessage;
dataChannel = channel;
dataUser = user;
}
QString getChannel() { return dataChannel; }
QString getUser(){ return dataUser; }
void accept(Client& visitor) override
{
visitor.visit(*this);
}
private:
QString dataChannel;
QString dataUser;
};
// Notice class - contains a notification message
class MessageNotice : public Message
{
public:
MessageNotice(const QString &rawmessage, const QString &text)
{
dataRawMessage = rawmessage;
dataText = text;
}
QString getText() { return dataText;}
void accept(Client& visitor) override
{
visitor.visit(*this);
}
private:
QString dataText;
};
void Client::visit(MessageJoin& msg)
{
qDebug() << msg.getUser() << " joined " << msg.getChannel();
// Update UI: Add user
}
void Client::visit(MessageNotice& msg)
{
qDebug() << msg.getText();
// Update UI: Display message
}
// Client code - print message and update UI
void Client::messageReceived(Message *message)
{
if(message)
{
message->visit(this);
delete message; // Message was allocated in the library and is not used anymore
}
}
A better design might be to have an abstract virtual function in the Message class, called process or onReceive or similar, the sub-classes implements this function. Then in Client::messageReceived just call this function:
message->onReceive(...);
No need to for the dynamic_cast.
I would also recommend you to look into smart pointers, such as std::unique_ptr.
If you have private data in the Client class that is needed for the message processing functions, then there are many methods of solving that:
The simplest is to use a plain "getter" function in the client:
class Client
{
public:
const QList<QString>& getList() const { return listContainingUiRelatedStuff; }
// Add non-const version if you need to modify the list
};
If you just want add items to the list in your example, then add a function for that:
void addStringToList(const QString& str)
{ listContainingUiRelatedStuff.push_back(str); }
Or the non-recommended variant, make Client a friend in all message classes.
The second variant is what I recommend. For example, if you have a list of all connected clients and want to send a message to all of them, then create a function sendAll that does it.
The big idea here is to try and minimize the coupling and dependencies between your classes. The less coupling there is, the easier it will be to modify one or the other, or add new message classes, or even completely rewrite one or the other of the involved classes without it affecting the other classes. This is why we split code into interface and implementation and data hiding.