Pass class method to const char * parameter - c++

In Qt QShortcut has such constructor
QShortcut(const QKeySequence& key, QWidget *parent,
const char *member = 0, const char *ambiguousMember = 0,
Qt::ShortcutContext context = Qt::WindowShortcut);
As far as I understand from documentation I can pass a method to the third parameter instead of using connect to signal activated. But how can I pass method there? Using
QShortcut* sh = new QShortcut(QKeySequence(Qt::Key_Alt, Qt::Key_1), this, &OnShortcut);
or
QShortcut* sh = new QShortcut(QKeySequence(Qt::Key_Alt, Qt::Key_1), this, &MyClass::OnShortcut);
gives errors
error: no matching function for call to 'QShortcut::QShortcut(QKeySequence, KeyPressProcessing* const, void (*)())'

You have to use SLOT(OnShortcut()) instead of &OnShortcut because the QShortcut class constructor requires const char *as the second and third arguments and not the pointer to a function. Thus, Qt has dedicated conversion macro SLOT that accepts the function signature as an argument and converts it in the form that suitable for establishing connection.

Related

How to pass a function pointer to a method so that it can be used in Qt's connect()?

Background: I want to dynamically create the structure of a context menu and pass the slot of the action items to the method that creates the context menu.
The actual slot is in a QWidget class. I have tried different solutions by passing a function pointer. But they don't compile. Typical error message: "cannot initialize a parameter of type 'void (*)()' with an rvalue of type 'void (TextEdit::*)()'"
This compiles, but does not trigger the desired event:
MenuBuilder builder(&parentMenu);
auto *subMenu = builder.createMenu(SECTION, this, SLOT(TextEdit::onInsertChars()));
And the corresponding method:
QMenu *MenuBuilder::createMenu(const MenuDescription &menuDescription,
const QObject *receiver, const char *target) {
...
inlineMenu->addAction(text, receiver, target);
...
}
I'm sure there's an obvious solution, but I can't figure it out.
The solution: In this context you have to pass SLOT(onInsertChars()) instead of SLOT(TextEdit::onInsertChars()).
auto *subMenu = builder.createMenu(SECTION, this, SLOT(TextEdit::onInsertChars()));
The above should be:
auto *subMenu = builder.createMenu(SECTION, pointerToTheTextEdit, SLOT(onInsertChars()));
(of course if the onInsertChars() method is a slot in the class whose method is making the above call, then you can pass this as the pointer to the object that has the onInsertChars() slot)
Also, you may want to rename const char * slot in your createMenu() function to something else, as Qt's MOC preprocessor has kind of claimed the word slot for its own purposes and that might cause problems for you if you try to use it as a parameter name. Maybe rename the parameter to const char * slotName or something instead.

How to invoke QMetaMethod with an array of QStrings?

QMetaObject system provides some basic reflection API. What I want to do is to call given slot or method that's available through this system.
However, the arguments and names of method are initially all QString values. The documentation shows an example:
QString retVal;
QByteArray normalizedSignature = QMetaObject::normalizedSignature("compute(QString, int, double)");
int methodIndex = obj->metaObject()->indexOfMethod(normalizedSignature);
QMetaMethod method = obj->metaObject()->method(methodIndex);
method.invoke(obj,
Qt::DirectConnection,
Q_RETURN_ARG(QString, retVal),
Q_ARG(QString, "sqrt"),
Q_ARG(int, 42),
Q_ARG(double, 9.7));
If the "compute" slot does not take exactly one QString, one int and
one double in the specified order, the call will fail.
Emphasis mine. Now with this example, I'm pretty clueless. It's clearly using templates, which are useless in dynamic context. I don't really see a purpose in using this API statically, when I could just as well call the method directly.
So let's say I have this function:
void invokeSlot(QObject* target, const QString& slotName, const QStringList& arguments) {
// This is how I can get list of overloads by given name
const QMetaObject* obj = target_->metaObject();
QList<QMetaMethod> overloads;
for (int i = 0, l = obj->methodCount(); i < l; ++i) {
const QMetaMethod method = obj->method(i);
if( method.name() == name )
overloads.append(method);
}
// Now how to convert the arguments?
}
You can assume that I have prepared this method for type conversion:
QVariant convertType(const QString& source, const QVariant::Type requiredType);
But how to pass the arguments even if I chose correct overload and convert strings to QVariants of required type?
You can prepare this function:
QGenericArgument convertType(const QString& source, const QVariant::Type requiredType);
By using switches and Q_ARG inside it.
Then a big switch on the argument count with invokes in it.

Using non-static method in qmlRegisterSingletonType

I am trying to use a non-static method as callback method for qmlRegisterSingletonType in Qt 5. My code looks like this:
PersistentLong couponCounter("couponCounter", handler.getSubjectRunner(), handler.getDbManager());
qmlRegisterSingletonType<PersistentLong>("MyNamespace", 1, 0, "CouponCounter", couponCounter.factory);
But I get the following compiler error:
/.../src/Main.cxx: In function ‘int main(int, char**)’:
/.../src/Main.cxx:22:102: error: no matching function for call to ‘qmlRegisterSingletonType(const char [9], int, int, const char [14], <unresolved overloaded function type>)’
qmlRegisterSingletonType<PersistentLong>("MyNamespace", 1, 0, "CouponCounter", couponCounter.factory);
^
/.../src/Main.cxx:22:102: note: candidate is:
In file included from /usr/local/Qt-5.3.2/include/QtQml/QtQml:9:0,
from /.../src/Main.cxx:6:
/usr/local/Qt-5.3.2/include/QtQml/qqml.h:476:12: note: template<class T> int qmlRegisterSingletonType(const char*, int, int, const char*, QObject* (*)(QQmlEngine*, QJSEngine*))
inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName,
^
/usr/local/Qt-5.3.2/include/QtQml/qqml.h:476:12: note: template argument deduction/substitution failed:
/.../src/Main.cxx:22:102: note: cannot convert ‘couponCounter.PersistentLong::factory’ (type ‘<unresolved overloaded function type>’) to type ‘QObject* (*)(QQmlEngine*, QJSEngine*)’
qmlRegisterSingletonType<PersistentLong>("MyNamespace", 1, 0, "CouponCounter", couponCounter.factory);
The factory method of PersistentLong looks like this:
QObject * factory(QQmlEngine *engine, QJSEngine *scriptEngine) {
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return this;
}
Questions
Any ideas as to why I can't use this method as argument to qmlRegisterSingletonType?
Is there another way to register a non-static instance as a singleton in QML?
Edit 1
couponCounter is constructed in main() in the following way:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtSubjectHandler handler("burgundy_frontend");
PersistentLong couponCounter("couponCounter", handler.getSubjectRunner(), handler.getDbManager());
qmlRegisterSingletonType<PersistentLong>("MyNamespace", 1, 0, "CouponCounter", couponCounter.factory);
}
If I use a function pointer, how can I reference couponCounter in this function?
Any ideas as to why I can't use this method as argument to qmlRegisterSingletonType?
Given the usage of the factory method, I can guess it is a member function of the class what you are using as a callback. However, the registration function has the following signature:
int qmlRegisterSingletonType(const char * uri, int versionMajor, int versionMinor, const char * typeName, QObject *(* ) ( QQmlEngine *, QJSEngine * ) callback)
As you can see the last parameter is a function pointer but you are (trying to) pass a pointer to a member function which is quite different. Note that neither a friend function nor a static function could be exploited in this case, since both of them lack the access to the this pointer.
Is there another way to register a non-static instance as a singleton in QML?
It is perfectly feasable to write and compile a global function like this (static, where are you?):
QObject *provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return new MyNiceObject();
}
and register it like this:
qmlRegisterSingletonType<AppInfo>("AppInfo", 1, 0, "AppInfo", provider);
Digia examples uses this approach, apart from the static I've removed. Unfortunately, documentation is quite vague and do not provide a specific reason for using a static modifier so the only reason I can think about is visibility purposes. Anyone more informed/skilled can correct me if I'm wrong and tell us if there is a more specific/thought reason (e.g. any specific memory management reason).
In the end, the whole point is to provide the QML Engine with a function which creates the instance of our type and such a result cannot be achieved with a member function.
On a side note, if you prefer to return also a singleton instance, you can exploit the singleton pattern (which again uses the static modifier) and rewrite the function like this:
QObject *provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
MyNiceObject * p = MyNiceObject::instance(); // uhmmmm static...
QQmlEngine::setObjectOwnership(p, QQmlEngine::CppOwnership);
return p;
}
Note that the ownership of the created object is given to the QML engine by default. to avoid the problem the QQmlEngine::setObjectOwnership() call can be used.

Qt 5: const char * QString cast

In Qt 4 it is possible to automatically cast a QString to a "const char *", e.g. I could pass a QString to a function that expected a "const char *".
void myFunction(const char *parameter);
QString myString;
myFunction(myString); //works in QT4
In Qt 5 I would however get an "error C2440: 'type cast' : cannot convert from 'QString' to 'const char *'" (that is the Visual C++ 2008 compiler, other compilers would throw something similar). If I understand the documentation correctly, it is because the Qt 3 compatibility layer is not included anymore in QT5.
Of course, I could change the function call from
myFunction(myString);
to
myFunction(myString.toLatin1().data());
However, I have a huge code base that compiles fine with Qt 4 and I would really like to have my old code compile with Qt 5 without modifying it. Is there any way to achieve this?
You could create a macro or inline function for your purpose to minimize the changes, but since that would also require a grep alike operation, there is not much difference.
#define QString2charp(myString) myString.toLatin1().data()
or
inline char* QString2charp (const QString &myString)
{
return myString.toLatin1().data();
}
and then:
myFunction(QString2charp(myString));
BUT
of course, in an ideal world, it would be nice if your "myFunction" could get an overload expecting a QString argument.
void myFunction(const char *parameter);
void myFunction(QString parameter);
and then the overload would be implemented like this:
void myFunction(const QString &parameter)
{
myFunction(myString.toLatin1().data());
}
of course, this would require the constructor being explicit so that no implicit conversion can happen, otherwise the compiler will complain about ambiguity in presence of both when trying to pass const char*, but if you always use QString, it should just work with Qt 5.
This is somewhat equal to rewriting the original function to a different signature expecting QString though because unfortunately the corresponding constructor is not explicit. I imagine that was not changed in Qt 5 for compatibility as it would have broken too much code.
As you can see, you do not need to change anything in your code this way, other than adding a one-liner overload. It is neat, isn't it?
You could either
Change the signature of your function to MyFunction( const QString & parameter ) (of course that works only if you don't need the one that takes char * any more)
Overload MyFunction:
void myFunction(const char *parameter); //this is your normal function
void myFunction( const QString & parameter )
{
char * c_str = nullptr;
<convert parameter to char * in any way that suits you>
myFunction( c_str );
}
If the above is impossible for some reason, the best approach is probably to write a similar wrapper:
void MyFunctionWrapper( const QString & parameter )
{
<same as above>
}
It is no longer possible to cast QString to const char* in >= Qt 5, because QString is UTF-16 and no longer has an entry for caching the result of converting to ASCII.
The way to achieve your aim is
a) Add overloads to the functions expecting const char* which can handle QStrings,
b) Add the explicit conversion code to the call-site, or
c) Go to some char-based string, like std::string.
None of those fulfill your aim of nearly no changes though.
I found this on QT forum
const QByteArray byteArray = mytext = textBox.text().toUtf8();
const char *mytext = byteArray.constData();
This resolved my issue

how to implement the macros SIGNAL and SLOT as functions QT

Well, what i want is to implement functions that works like the macros SIGNAL and SLOT but with my own implementation, so the goal is that the function (or my own macro if i can create it) receive a parameter like an String "mycustomsignal" and he return a const char* "2mycustomsignal(double,int)" so i do that using a QMap for store this asociation QMap<"signal name","signal signature">, i fill it in another function.
in this case my QMap is this->signals so i search the signature "mycustomsignal(something)" with the key "mycustomsignal" and prepend the code that QObject::connect recognize for signals and i get "2mycustomsignal(something)" so i convert it in const char* because QObject::connect have this parameters in this format and i want to use also in conjuntion with SIGNAL and SLOT MACROS like this:
QObject::connect(customwidget, customwidget->getSignal("somesignal"),
somewidget, SLOT(someslot()));
the function that i use is (only for undertand what i do):
const char* SomeClass::getSignal(QString signalName) {
QString signalsignature = this->signals.value(signalName);
signalsignature.prepend(QString::number(QSIGNAL_CODE));
QByteArray ba = signalsignature.toLatin1();
return signalformated; //Here is the lost of data because QByteArray only exist in the function
}
but this return a pointer to local and the source of the data is destroyed when the function ends, so how i could do this with a function or creating my own MACRO?
Thanks for any help or suggestion.
You have to return QByteArray from your method, return ba;, then get the const char* from the return value:
QObject::connect(customwidget, customwidget->getSignal("somesignal").constData(),
somewidget, SLOT(someslot()));
If you really want to return char pointer, then you have to keep the QByteArray around, for example by adding it to a QList member variable of the same object, so it will get destructed when the instance gets destructed.