I'm trying to integrate Lua with Qt's QMetaObject system. I have a class that derives from QObject that I bind to Lua based on the class name using QObject::staticMetaObject.
main.h:
#ifndef MAIN_H
#define MAIN_H
class Test : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE Test(QObject *parent = 0) : QObject(parent){}
~Test(){}
};
Q_DECLARE_METATYPE(Test*)
#endif
main.cpp
#include <QCoreApplication>
#include <QDebug>
#include "main.h"
#include "lua_src/lua.hpp" //Lua include
int CreateUserData(lua_State *L)
{
const QMetaObject *metaObject = (const QMetaObject*)lua_touserdata(L, lua_upvalueindex(1));
//PROBLEM AREA
int typeId = QMetaType::type(metaObject->className());
if(typeId != QMetaType::UnknownType)//typeId is always unknown
{
QMetaType meta(typeId);
void *ptr = lua_newuserdata(L, meta.sizeOf());
meta.construct(ptr);
}
//PROBLEM AREA
lua_newtable(L);
lua_setuservalue(L, 1);
return 1;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QString luaScript("local test = Test.new()");
lua_State *L = luaL_newstate();
//bind Test class to lua
lua_newtable(L);
lua_pushvalue(L, -1);
lua_setglobal(L, "Test");
lua_pushvalue(L, -1);
lua_pushlightuserdata(L, (void*)&Test::staticMetaObject);
lua_pushcclosure(L, CreateUserData, 1);
lua_setfield(L, -2, "new");
//start script
luaL_dostring(L, luaScript.toStdString().c_str());
lua_close(L);
}
The issue is that lua will allocate memory for userdata but will not construct the object it represents. All documentation says to use placement new to construct your object at the ptr of the lua userdata, however QMetaObject doesn't allow placement new out of the box.
I've included suggestions from ixSci about using QMetaType to construct the object at ptr. However, typeId always comes back as unknown.
Looks like what you need is available in the QMetaType class.
So to get what you ask for you need something like this (not tested!):
int typeId = QMetaType::type(metaObject->className());
if (typeId != QMetaType::UnknownType)
{
QMetaType meta(typeId);
meta.construct(ptr, objectToCopy);
}
Your Test class miss a
Q_DECLARE_METATYPE(Test*)
and a
qRegisterMetaType<Test*>("Test");
to have the type correctly registered in Qt Meta-system.
Note the pointer declared. You need to declare a pointer because the copy constructor is disabled for QObject.
than you can correctly call:
Test* test = new Test();
auto name = test.metaObject()->className();
auto type = QMetaType::type(name);
Test* instance = static_cast<Test*>(QMetaType::construct(type));
Edit: A complete working implementation (it actually add the qMetaTypeConstructHelper)
somevalue.h
#include <QObject>
#include <QMetaType>
class SomeValue : public QObject
{
Q_OBJECT
Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
public:
explicit Q_INVOKABLE SomeValue(QObject* parent = nullptr);
~SomeValue() override = default;
int value() const;
signals:
void valueChanged(int value);
public slots:
void setValue(int value);
private:
int _value;
};
somevalue.cpp
#include "somevalue.h"
Q_DECLARE_METATYPE(SomeValue*)
template <>
void* qMetaTypeConstructHelper<SomeValue>(const SomeValue*)
{
return new SomeValue();
}
static struct SomeValueMetaId
{
SomeValueMetaId()
{
qRegisterMetaType<SomeValue>("SomeValue");
}
} _SomeValueMetaId;
SomeValue::SomeValue(QObject* parent)
: QObject(parent),
_value{100}
{
}
int SomeValue::value() const
{
return _value;
}
void SomeValue::setValue(int value)
{
if (_value == value)
return;
_value = value;
emit valueChanged(_value);
}
main.cpp
int main()
{
SomeValue pint;
auto pintName = pint.metaObject()->className();
auto pintType = QMetaType::type("SomeValue");
qDebug() << pintName << pintType << QMetaType::typeName(pintType);
qDebug() << QMetaType::isRegistered(QMetaType::type("SomeValue*"));
auto otherObj = static_cast<SomeValue*>(QMetaType::construct(pintType));
qDebug() << pint.value();
qDebug() << otherObj->value();
qDebug() << "new classname" << otherObj->metaObject()->className();
qDebug() << otherObj->metaObject()->propertyCount();
int valueId = pint.metaObject()->indexOfProperty("value");
auto minname = pint.metaObject()->property(valueId).name();
qDebug() << "value name" << minname;
auto minvariant = pint.property(minname);
qDebug() << "value type name" << minvariant << minvariant.typeName();
qDebug() << QMetaType::type(minvariant.typeName());
return 0;
}
I have found a solution for my situation.
After reviewing the answers from Moia and ixSci, I have realized that I was correct in my statement that placement new cannot be used on a QObject because QObject has it's copy constructor private (and shouldn't be made public).
A more efficient method is to (obviously) store pointers to the QObject* created from metaObject->newInstance(). That's right, pointers to pointers.
New code is as follows:
const QMetaObject *metaObject = (const QMetaObject*)lua_touserdata(L, lua_upvalueindex(1));
uintptr_t *ptr = (uintptr_t*)lua_newuserdata(L, sizeof(QObject*));
QObject *object = metaObject->newInstance();
*ptr = reinterpret_cast<uintptr_t>(object);
And for retrieving:
uintptr_t *objectPointer = (uintptr_t*)lua_touserdata(L, -1);
QObject *object = static_cast<QObject*>((void*)*objectPointer);
The upside is that lua can allocate fixed size for any class object since it is always 4 (just a pointer). This means I don't have to do any type checking.
The obvious downside to this is that I can't do any type checking since it will always just be pointers. Also, all interactions with these types inside the Lua script will behave as pointers. All copies will be pointer copies instead of QObject copies. As a result, I will have to implement my own copy constructor for my QObject's depending on my specific use case.
Thanks for all your assistance!
Related
I have written a multi-threaded app in Qt/C++11 , Windows.
The idea was to have and recycle some strings from a pool, using smart pointers.
Here is stringpool.cpp:
#include "stringpool.h"
QMutex StringPool::m_mutex;
int StringPool::m_counter;
std::stack<StringPool::pointer_type<QString>> StringPool::m_pool;
StringPool::pointer_type<QString> StringPool::getString()
{
QMutexLocker lock(&m_mutex);
if (m_pool.empty())
{
add();
}
auto inst = std::move(m_pool.top());
m_pool.pop();
return inst;
}
void StringPool::add(bool useLock, QString * ptr)
{
if(useLock)
m_mutex.lock();
if (ptr == nullptr)
{
ptr = new QString();
ptr->append(QString("pomo_hacs_%1").arg(++m_counter));
}
StringPool::pointer_type<QString> inst(ptr, [this](QString * ptr) { add(true, ptr); });
m_pool.push(std::move(inst));
if(useLock)
m_mutex.unlock();
}
And here is stringpool.h:
#pragma once
#include <QMutex>
#include <QString>
#include <functional>
#include <memory>
#include <stack>
class StringPool
{
public:
template <typename T> using pointer_type = std::unique_ptr<T, std::function<void(T*)>>;
//
StringPool() = default;
pointer_type<QString> getString();
private:
void add(bool useLock = false, QString * ptr = nullptr);
//
static QMutex m_mutex;
static int m_counter;
static std::stack<pointer_type<QString>> m_pool;
};
And here is the test app:
#include <QtCore>
#include "stringpool.h"
static StringPool Pool;
class Tester : public QThread
{
public:
void run() override
{
for(int i = 0; i < 20; i++)
{
{
auto str = Pool.getString();
fprintf(stderr, "Thread %p : %s \n", QThread::currentThreadId(), str->toUtf8().data());
msleep(rand() % 500);
}
}
fprintf(stderr, "Thread %p : FINITA! \n", QThread::currentThreadId());
}
};
#define MAX_TASKS_NBR 3
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Tester tester[MAX_TASKS_NBR];
for(auto i = 0; i < MAX_TASKS_NBR; i++)
tester[i].start();
for(auto i = 0; i < MAX_TASKS_NBR; i++)
tester[i].wait();
//
return 0;
}
It compiles ok, it runs and produces the following result:
Well, the idea is that the app runs (apparently) OK.
But immediately after it finishes, I have this error:
Does anyone have an idea how can I fix this?
The reason for this error has to do with the smart pointer and not the multithreading.
You define pointer_type as an alias for unique_ptr with a custom deleter
template <typename T> using pointer_type = std::unique_ptr<T, std::function<void(T*)>>;
You create strings with custom deleters
void StringPool::add(bool useLock, QString * ptr)
{
if (ptr == nullptr)
{
ptr = new QString();
ptr->append(QString("pomo_hacs_%1").arg(++m_counter));
}
StringPool::pointer_type<QString> inst(ptr, [this](QString * ptr) { add(true, ptr); }); // here
m_pool.push(std::move(inst));
}
At the end of the program, m_pool goes out of scope and runs its destructor.
Consider the path of execution...m_pool will try to destroy all its members. For each member, the custom deleter. The custom deleter calls add. add pushes the pointer to the stack.
Logically this is an infinite loop. But it's more likely to create some kind of undefined behavior by breaking the consistency of the data structure. (i.e. The stack shouldn't be pushing new members while it is being destructed). An exception might occur due to function stack overflow or literal stack overflow (heh) when there is not enough memory to add to the stack data structure. Since the exception occurs in a destructor unhandled, it ends the program immediately. But it could also very likely be a seg fault due to the pushing while destructing.
Fixes:
I already didn't like your add function.
StringPool::pointer_type<QString> StringPool::getString()
{
QMutexLocker lock(&m_mutex);
if (m_pool.empty())
{
auto ptr = new QString(QString("pomo_hacs_%1").arg(++m_counter));
return pointer_type<QString>(ptr, [this](QString* ptr) { reclaim(ptr); });
}
auto inst = std::move(m_pool.top());
m_pool.pop();
return inst;
}
void StringPool::reclaim(QString* ptr)
{
QMutexLocker lock(&m_mutex);
if (m_teardown)
delete ptr;
else
m_pool.emplace(ptr, [this](QString* ptr) { reclaim(ptr); });
}
StringPool::~StringPool()
{
QMutexLocker lock(&m_mutex);
m_teardown = true;
}
StringPool was a static class but with this fix it must now be a singleton class.
It might be tempting to pull m_teardown out of the critical section, but it is shared data, so doing will open the door for race conditions. As a premature optimization, you could make m_teardown an std::atomic<bool> and perform a read check before entering the critical section (can skip the critical section if false) but this requires 1) you check the value again in the critical section and 2) you change from true to false exactly once.
I want to wirte a funtion CH1_Hard_Soft to process data, which accepts two arguments from two different function.
double MainWindow::getdata_CH1(double time)
{
...
double CH1_data=0;
switch (CH1.Source) {
case 0: //software-hard
CH1_data = CH1_Hard_Soft(time);
....
}
The function CH1_Hard_Soft need to accept an argument time from getdata_CH1 and accept a QVector from other thread. And the function CH1_Hard_Soft will process these data and then return a QVector to getdata_CH1(double time). I don't know how to do this. Please give me some suggestions on how to do this.THANKS!!!
You can use a Function Object: create a new class with two attributes (one per parameter). Create setter for each parameter (or redefine the operator () to be closer to the behavior of a real function).
Each setter should check if the others are setted also. In that case, call you algorithm and send the result with a signal.
For example:
A simple worker executed in another thread. It will send fake data after 3 seconds
class Worker: public QObject
{
Q_OBJECT
public:
Worker(): QObject()
{
}
void timerEvent(QTimerEvent* ev)
{
qDebug() << Q_FUNC_INFO;
emit getVector(QVector<int>() << 2 << 4 << 6 << 8);
killTimer(timerId);
}
public slots:
void run()
{
timerId = startTimer(3000);
}
signals:
void getVector(QVector<int> const& vec);
private:
int timerId;
};
The Function Object: it will accept two param (a double and a vector)
// For convenience. Define a value and a flag to check if the value is well set
template<typename T> struct Param
{
T value;
bool isInit;
Param(): isInit(false)
{}
void setValue(T const& v)
{
value = v;
isInit = true;
}
};
// The processor
class Processor: public QObject
{
Q_OBJECT
public:
Processor(QObject* parent=nullptr): QObject(parent)
{}
void operator()(QVector<int> const& vector)
{
values.setValue(vector);
if (time.isInit)
process();
}
void operator()(double t)
{
time.setValue(t);
if (values.isInit)
process();
}
signals:
void done(double result);
private:
// Will be called as soon as all the parameters are set
void process()
{
// DO something
qDebug() << Q_FUNC_INFO;
emit done(time.value * values.value.length());
}
Param<QVector<int> > values;
Param<double> time;
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// Run the thread
Worker* worker = new Worker();
QThread* th = new QThread();
worker->moveToThread(th);
QObject::connect(th, &QThread::started, worker, &Worker::run);
Processor CH1_Hard_Soft;
// Will be called when the CH1_Hard_Soft will send its result
QObject::connect(&CH1_Hard_Soft, &Processor::done, [=](double result) { qDebug() << "RESULT" << result; });
// Set the param vector
QObject::connect(worker, &Worker::getVector, [&](QVector<int> const& vec) { CH1_Hard_Soft(vec); });
// Call CH1_Hard_Soft with the first param
double time = 12.6;
CH1_Hard_Soft(time);
th->start();
return app.exec();
I have a FreeRTOS function xTaskCreate. Simplified declaration looks like
typedef void (*TaskFunction_t)( void* );
unsigned xTaskCreate( TaskFunction_t pxTaskCode, void*params );
And there are two classes:
class Super {
virtual void task(void*params) = 0;
};
class Derived1 : public Super {
virtual void task(void*params){ while(1){ blinkLed(1); delay_ms(333); } }
};
class Derived2 : public Super { ... ;}
In function init() I select one of derived classes and create its instance. Then want to create task
void init(){
Super *obj = condition ? new Derived1 : new Derived2;
xTaskCreate( obj->task ); // WRONG.
}
Upd. Add missed void*params in Simplified declaration of xTaskCreate.
TaskFunction_t is just a pointer to a function - so it can't take a pointer to a member function. Only a pointer to normal function. Or a static member function. Or a lambda with no capture. It's that last one that we'll take advantage of.
One of the arguments you removed from your simplified declaration is the context:
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
const char * const pcName,
unsigned short usStackDepth,
void *pvParameters, // <== this one!
UBaseType_t uxPriority,
TaskHandle_t *pxCreatedTask
);
You provide the Super* in the parameters and provide a lambda that knows what to do with it. Altogether:
void init(){
Super *obj = condition ? new Derived1 : new Derived2;
xTaskCreate([](void* o){ static_cast<Super*>(o)->task(); },
..., // other args here
obj,
... // more args
);
}
Note that task() should take no arguments. The void*is the context that we're converting to a Super*.
After several experiements of my own with answers here I prefered this simpler method giving Object oriented function calls to RTOS tasks.
//These are not full declaration of class IModule which is fully abstarct so //object that are IModule* are always inherited.
protected:
virtual int InitModule() = 0;
virtual bool PreLoop() = 0;
virtual bool DoLoop() = 0;
virtual bool PostLoop() = 0;
virtual bool DoShutdown() = 0;
//Return if this module implementation requires an RTOS task looping.
virtual bool isFreeRTOSTaskRequired() = 0;
private:
TaskHandle_t *moduleLoopTaskHandle;
bool CreateRTOSTask();
static void TaskStart(void* taskStartParameters);
void TaskLoop();
//END OF PARTIAL decleration
bool IModule::CreateRTOSTask()
{
xTaskCreate(IModule::TaskStart, "NAME", 2048, this, tskNO_AFFINITY, moduleLoopTaskHandle);
return true;
}
void IModule::TaskStart(void *taskStartParameters)
{
IModule *moduleObject = (IModule *)taskStartParameters;
moduleObject->TaskLoop();
}
void IModule::TaskLoop()
{
//TODO Buraya ölçüm koyalım ve bir değişkene yazalım
while (true)
{
ESP_LOGD("IModule::TaskLoop", "%s", "I am alive!");
if (!PreLoop())
{
}
if (!DoLoop())
{
}
if (!PostLoop())
{
}
}
vTaskDelete(NULL);
}
UPDATED: See below.
As explained better than I can here, you might get away with this. Hard to tell from your question if it will cover all of your requirements.
typedef void (Super::*TaskFunction_t)( void* );
Further Reading
UPDATE:
I fleshed out your example, and the results and code are below:
XXXXX:~/scratch/member_function_pointer$ bin/provemeright
Condition false
virtual void Derived2::task(void*)
XXXXX:~/scratch/member_function_pointer$ bin/provemeright foo
Condition true because of argument foo
virtual void Derived1::task(void*)
code (all one cpp file, bad form, but proves syntax):
#include <iostream>
class Super;
typedef void (Super::*TaskFunction_t)(void*);
unsigned xTaskCreate( TaskFunction_t pxTaskCode, void* params);
bool condition = false;
class Super {
public: virtual void task(void* params) = 0;
};
class Derived1 : public Super {
public: virtual void task(void* params) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
if(params) // Necessary to prevent unused parameter warning
std::cout << "Not Null" << std::endl;
};
};
class Derived2 : public Super {
public: virtual void task(void* params) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
if(params) // Necessary to prevent unused parameter warning
std::cout << "Not Null" << std::endl;
};
};
void init(){
Super *obj = condition ? (Super*)new Derived1 : (Super*)new Derived2;
xTaskCreate( &Super::task , obj);
}
int main(int argc, char **argv)
{
if(argc > 1)
{
std::cout << "Condition true because of argument " << argv[1] << std::endl;
condition = true;
} else {
std::cout << "Condition false" << std::endl;
}
init();
return 0;
}
unsigned xTaskCreate( TaskFunction_t pxTaskCode, void* params)
{
Super *obj = (Super*) params;
(obj->*pxTaskCode)(NULL);
return 0;
}
If you're concerned that the syntax is &Super::task instead of &obj->task, then you're misunderstanding how virtual functions work. (It turns out that the &obj->task syntax forbidden by ISO C++, but gcc says it's permissive, so you shouldn't but could force it to compile, and get exactly the same result)
The information about which virtual version of a function to call 'lives' in the object, not the type system. (Could probably phrase that better, open to suggestions, but I think it gets the general point across) It is impossible to call a member function without an object, so in order to make use of the function pointer, you'll have to have an object to 'call it on'. It is the type of that object which will determine which virtual function gets called. So the code above should achieve whatever you're going for, unless of course, this is a round-about way to determine the type of the object pointed to by obj, in which case, it's an awfully convoluted way of going about it.
Further Reading specifically in "Kerrek SB"s answer.
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);
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.