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);
Related
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.
How would I return a(n existing) pointer to a known type from a string of its name? Say I created some map<string, double> called map1 and some vector<string> called vec1. I'd like to write a function that will return map1 from "map1" and vec1 from "vec1" (and an additional argument specifying the respective type of each).
Is this possible?
My ultimate goal is to get a QWidget by its name in QString form, since I'm working with a large number of them, though an answer should be framework-independent, right?
You need to perform some kind of dynamic dispatch. To do so you can simply start with exactly what you proposed:
enum class types { A = 0, B = 1 };
void (*handlers[])(void*) = { &HandleA, &HandleB };
::std::unordered_map<::std::string, ::std::tuple<types, void*>> registry;
Now all that remains is to perform the lookup:
void lookup(::std::string const& name)
{
auto& t = registry.at(name);
handlers[static_cast<size_t>(::std::get<0>(t))](::std::get<1>(t));
}
Automagic argument casting for handlers
The handlers all take an argument of type void* - this can be dealt with by adding a little template magic:
template<typename T, void(*f)(T*)>
void handle(void* arg)
{
f(static_cast<T*>(arg));
}
void (*handlers[])(void*) = { &handle<A, &HandleA>, &handle<B, &HandleB> };
Now, the prototype is e.g. void HandleA(A*).
Simple adding of objects to registry
With the current code, you can add objects to your registry like so:
A a;
registry.emplace("A #1", ::std::make_tuple(types::A, &a));
While this works perfectly, we would like to do something a bit more elegant. Let us start by changing the enum class types to something which also knows about the type we whish to represent it:
template<typename T> struct types;
template<> struct types<A> { static const size_t id = 0; };
template<> struct types<B> { static const size_t id = 1; };
Of course, now we need to fix the registry type:
::std::unordered_map<::std::string, ::std::tuple<size_t, void*>> registry;
And finally we can provide a simple insert function:
template<typename T>
void insert(::std::string const& name, T* object)
{
registry.emplace(name, ::std::make_tuple(types<T>::id, static_cast<void*>(object)));
}
Final usage example
A a;
insert("A #1", &a);
lookup("A #1");
The meta-object system already handles this, so the answer will be framework-specific because you generally need a code generator to get metadata about the C++ types that's not otherwise available.
QLineEdit * ed = ...;
ed->setObjectName("myObject");
... elsewhere in the code
foreach(QWidget * w, QCoreApplication::allWidgets()) {
// Lookup by name
if (w->objectName() == "myObject") {
...
}
// Lookup by type
if (qobject_cast<QLineEdit*>(w)) {
...
}
}
If you want to speed up the lookup, and the objects have unique names:
class Widgets {
typedef QMap<QString, QPointer<QWidget>> Data;
mutable Data m_map;
public:
Widgets() {
foreach(QWidget * w, QCoreApplication::allWidgets()) {
if (w->objectName().isEmpty()) continue;
m_map.insert(w->objectName(), w);
}
}
QWidget * lookupWidget(const QString & name) const {
Data::iterator it = m_map.find(name);
if (it == m_map.end()) return nullptr;
QWidget * w = it->data();
if (!w) m_map.erase(it); // The widget doesn't exist anymore
return w;
}
template <typename T> T * lookup(const QString & name) const {
return qobject_cast<T*>(lookupWidget(name));
}
void setName(QWidget * w, const QString & name) {
Q_ASSERT(! name.isEmpty());
w->setObjectName(name);
m_map.insert(name, w);
}
};
In your code, use widgets->setName() instead of setObjectName.
If you want to look-up by both name and type, where duplicate names are OK as long as they are all of different types:
class Widgets2 {
typedef QPair<QString, QString> Key;
typedef QMap<Key, QPointer<QWidget>> Data;
mutable Data m_map;
static Key keyFor(QWidget * w) {
return qMakePair(w->objectName(),
QString::fromLatin1(w->metaObject()->className()));
public:
Widgets2() {
foreach(QWidget * w, QCoreApplication::allWidgets()) {
if (w->objectName().isEmpty()) continue;
m_map.insert(keyFor(w), w);
}
}
QWidget * lookupWidget(const QString & name, const QString & type) const {
Data::iterator it = m_map.find(qMakePair(name, type));
if (it == m_map.end()) return nullptr;
QWidget * w = it->data();
if (!w) m_map.erase(it); // The widget doesn't exist anymore
return w;
}
template <typename T> T * lookup(const QString & name) const
{
return qobject_cast<T*>(lookupWidget(name,
QString::fromLatin1(T::staticMetaObject.className())));
}
void setName(QWidget * w, const QString & name) {
Q_ASSERT(! name.isEmpty());
w->setObjectName(name);
m_map.insert(keyFor(w), w);
}
};
The lookup works as follows:
widgets2->lookup<QLineEdit>("myObject")->setText("foo");
I'm leveraging the QObject and QPointer to make the widget registry safe to widget deletions - you won't ever get a dangling pointer back.
It is also possible to track object name changes, if you so wish: QObject emits the objectNameChanged signal.
All of this is of course a horrible hack around a broken design of your code. The fact that you need this means that you're very tightly coupling business logic and the GUI. You should use some kind of model-view architecture.
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.
I'm trying to store enum in SQLite database using QSql.
I have following class:
partydao.h:
class PartyDao : public QObject
{
public:
enum partyType { typeCompany, typePerson };
private:
Q_OBJECT
Q_ENUMS(partyType)
Q_PROPERTY(partyType type READ type WRITE set_type)
// other declarations
};
Q_DECLARE_METATYPE(PartyDao::partyType)
partydao.cpp:
#include "partydao.h"
static int id = qRegisterMetaType<PartyDao::partyType>("partyType");
I do insert like this:
PartyDao p;
p.setProperty("type", QVariant::fromValue(PartyDao::typePerson));
QSqlQuery query;
query.prepare("INSERT INTO party (type) values (:type)");
qDebug() << p.property("type").isNull();
query.bindValue(":type", p.property("type"));
query.exec();
Although qDebug() prints "false" (i.e. property is not null), null value is stored in db.
I've tried with column of type TEXT and INTEGER with no success.
Can you tell me what I'm doing wrong?
EDIT:
I've just checked and QVariant holding my enum claims it can't be converted to QString or int (canConvert<int>() and canConvert<QString>() return false). Is there a way to add this conversion?
I didn't find any way to use auto conversion of QVariants of user type. Therefore I've created singleton storing rules for conversion.
struct DbConverter
{
virtual ~DbConverter() {}
virtual QVariant toDbValue(const QVariant &value) = 0;
virtual QVariant fromDbValue(const QVariant &value) = 0;
};
class DbConversion
{
public:
static QVariant toDbValue(const QVariant &value)
{
return instance()->m_converters.contains(value.userType()) ?
instance()->m_converters[value.userType()]->toDbValue(value) :
value;
}
static QVariant fromDbValue(int type, const QVariant &value)
{
return instance()->m_converters.contains(type) ?
instance()->m_converters[type]->fromDbValue(value):
value;
}
static int setConverter(int typeId, DbConverter *converter)
{
instance()->m_converters[typeId] = converter;
return typeId;
}
private:
static DbConversion *instance()
{
static DbConversion *inst = new DbConversion;
return inst;
}
QHash<int, DbConverter*> m_converters;
};
I register my custom types using value returned from qRegisterMetaType() and perform conversions on each app<->db transfer.
Please let me know if you find any more elegant solution and I'll be happy to accept your answer.
Just assign initial value to the first element in your enum, and the compiler will auto-increment any further definitions, then save this value to your database? You may have to cast it back and forth between an int, but qvariant should take care of that for you.
ie
enum partyType { typeCompany=1, typePerson };
The solution may be simple. Then again it may not be possible.
I have the base callback class:
class CFCallback {
int command_;
int transfer_rate_;
public:
CFCallback(int command, int transfer_rate = 0) {
command_ = command; transfer_rate_ = transfer_rate; }
virtual ~CFCallback() {}
virtual void operator()(void *data) = 0;
int GetCommand() { return command_; }
int GetTransferRate() { return transfer_rate_; }
};
And here's one example of deriving from CFCallback:
void CFPacketVersion::InitiateVersion() {
class InitiateVersionCB : public CFCallback {
CFPacketVersion *visitor_;
public:
InitiateVersionCB(CFPacketVersion *v, int command) :
CFCallback(command) {
visitor_ = v;
}
void operator()(void *data) {
Packet *pkt = (Packet *)data;
unsigned char *pkt_data = pkt->GetData();
std::string version = "";
for(unsigned int i = 0; i < pkt->GetDataLength(); i++ )
version+= pkt_data[i];
delete []pkt_data;
boost::regex rex("CFA(.*?):h(.*?),v(.*?)$");
boost::smatch what;
if( boost::regex_match(version, what, rex) ) {
if(visitor_->GetModel()->GetName() != what[1].str() )
LCDInfo("Crystalfontz: Model mismatch");
visitor_->SetHardwareVersion(what[2]);
visitor_->SetFirmwareVersion(what[3]);
}
}
};
GetVersion(new InitiateVersionCB(this, 1));
}
GetVersion(CFCallback *) is provided to the script engine.
I want to be able to do the same thing as seen in InitiateVersion, but on the javascript side of things. Is that possible?
I know I need to register meta type info for CFCallback. But I don't know if it's possible to use a pointer to a CFCallback. What I tried initially didn't work.
Also, seeing as CFCallback is a functor, I'm not sure how I translate that over to javascript. I imagine I can make CFCallback a QObject and provide a signal emitted from operator(). If you have any tips, please share.
I'm afraid it won't work the way you've set it up.
If you want to be able to create the callback in javascript, you need a QObject with an accessible GetVersion(QScriptValue) which the script will the use to pass a script-based implementation of the callback. Note, though, that the callback will not be able to work with untyped (void*) data - you need to pass either a valid QtScript object or QObject with a proper interface (like the Packet one in your example!)
You could then wrap it up like this:
QtScript:
function mycb(packet) {
var pkt_data = packet.getData(); // pkt_data is probably a String or custom object with proper interface so to simplify things get the version as string
var version = pkt_data.toString();
pkt_data.release(); // to simulate delete [] pkt_data; this is part of custom interface
// proceed further with the regex checks
}
GetVersion(mycb); // implies that you define the GetVersion() as a property of the global object
C++:
QScriptValue getVersion(QScriptContext *ctx, QScriptEngine *engine)
{
void *data = ...;
Packet pkt_data = wrapPacketData(data);
// Packet is interface registered with QtScript or inherits QObject
// it has methods getData(), toString() and release()
QScriptValueList args;
QScriptValue pkt_data_param = engine->newQObject(&pkt_data);
args << pkt_data_param;
QScriptValue cb = ctx->argument(0);
Q_ASSERT(cb.isFunction()); // we expect a function object!
cb.call(QScriptValue(), args);
}
QScriptValue getVersionFun = engine->newFunction(getVersion);
engine->globalObject().setProperty(QLatin1String("GetVersion"), getVersionFun);