Unsolved Is this possible?
-
@Chris-Kawa , thank you Chris I’ll respond properly tomorrow.
-
@Chris-Kawa said in Is this possible?:
You can't make up connections between arbitrary functions at runtime. Their signature needs to be known at compile time so that an instantiation of the connect template exists for that specific signature.
The older-style
connect()
s work by doing string comparisons, so the evaluation of the signature occurs at run-time. This allows connections to be made between C++ and QML (where the latter is not pre-compiled): https://doc.qt.io/qt-5/signalsandslots-syntaxes.html#connecting-c-objects-to-qml-objectsQMetaObjectBuilder
even lets you create new signals/slots/properties at runtime. It is officially private API, but it shows what's possible: https://www.qtdeveloperdays.com/sites/default/files/QtDevDays2014US-DIY-moc.pdfStill, I agree with you on your other comments about the "static"-ness of C++.
@SPlatten said in Is this possible?:
What I am ultimately aiming for is a way to create a generic and dynamic map capable of storing the information for any QT control or derived control and it’s signals. Where each single entry in the map would contain a pointer or reference to the control and the signal associated with the entry.
How do you plan to use this map?
Is there anyone available that is very familiar with templates and QT signal prototypes that can tell me if this is possible?
It is indeed possible to create a database of QObjects and signals, but templates can't contribute anything to it.
Start with
QMetaObject::methodCount()
andQMetaObject::method()
: https://doc.qt.io/qt-5/qmetaobject.html#methodCode
QWidget w; for (int i = 0; i < w.metaObject()->methodCount(); ++i) { auto met = w.metaObject()->method(i); qDebug() << met.methodType() << met.methodSignature(); }
Output
1 "destroyed(QObject*)" 1 "destroyed()" 1 "objectNameChanged(QString)" 2 "deleteLater()" 2 "_q_reregisterTimers(void*)" 1 "windowTitleChanged(QString)" 1 "windowIconChanged(QIcon)" 1 "windowIconTextChanged(QString)" 1 "customContextMenuRequested(QPoint)" 2 "setEnabled(bool)" 2 "setDisabled(bool)" 2 "setWindowModified(bool)" 2 "setWindowTitle(QString)" 2 "setStyleSheet(QString)" 2 "setFocus()" 2 "update()" 2 "repaint()" 2 "setVisible(bool)" 2 "setHidden(bool)" 2 "show()" 2 "hide()" 2 "showMinimized()" 2 "showMaximized()" 2 "showFullScreen()" 2 "showNormal()" 2 "close()" 2 "raise()" 2 "lower()" 2 "updateMicroFocus()" 2 "_q_showIfNotHidden()" 0 "grab(QRect)" 0 "grab()"
See also
QMetaMethod::methodType()
: https://doc.qt.io/qt-5/qmetamethod.html#MethodType-enum0 = QMetaMethod::Method
1 = QMetaMethod::Signal
2 = QMetaMethod::Slot -
@JKSH said:
QMetaObjectBuilder even lets you create new signals/slots/properties at runtime. It is officially private API, but it shows what's possible:
I think you misunderstood what I was trying to convey. You can create connections at runtime. You can enumerate methods etc. but those are all operations on pre-existing, pre-compiled static data embedded in the executable (meta objects or otherwise). You can't create a new type at runtime. You can't dynamically invent a function/lambda. You can only work with what's already there and was known statically.
So yeah, you can pre-register a set of types, expose them to whatever system you're creating and operate on that data at runtime. But you can't create new types or function signatures at runtime. There's no "eval()" in C++ like in javascript (unless you actually add a compiler to your program and create and load shared libraries, which would probably spook all existing antivirus software out there). -
@JKSH said in Is this possible?:
QWidget w;
for (int i = 0; i < w.metaObject()->methodCount(); ++i)
{
auto met = w.metaObject()->method(i);
qDebug() << met.methodType() << met.methodSignature();
}Ok, my application is intended to be as generic as possible. The application will be an engine that will read the application configuration in from XML. The XML specifies what widgets are to be included and signals from a widget can be subscribed to by anything in the XML, sockets are in JavaScript. I already have a framework working where signals in the C++ engine are connected to the slots in JavaScript. My intention is to reduce and simplify the current code that currently connects signals to slots where I have a single connect for each signal and slot.
What I want to do is almost exactly what has been posted here, produce a map of available signals then connect all these signals to a single slot in C++ which will package up the signal content and parameters into an object which is then emitted in a generic signal that all JavaScript slots can subscribe to.
If there is any clarification or if I have been vague please ask.
-
@SPlatten said in Is this possible?:
produce a map of available signals then connect all these signals to a single slot in C++
How are you going to handle different parameters of all these different signals with just one slot?
-
@jsulm , I don't think this is an issued when connecting a C++ signal to a Lambda slot.
-
@SPlatten
But we have said before (read above): using a lambda is not relevant to solving the issue of parameters & types. You seem to think lambdas will avoid the issue which would be present if you have signal/slot functions, and we are saying that is not the case, the issue remains.... -
I've already connected a C++ signal to a Lambda slot and whilst there are rules about slots having to match the signal parameters in C++, this is not the case when connecting a C++ signal to a Lambda slot. I've already done this and it works, which is why my request was for a way of creating a generic map of signals that I can iterate through and connect them all up to a single Lambda slot.
-
@SPlatten said in Is this possible?:
whilst there are rules about slots having to match the signal parameters in C++, this is not the case when connecting a C++ signal to a Lambda slot.
That's not what I find. I guess we'll have to beg to differ.
-
@JonB, actually, sorry I forgot, what I actually did was add an additional signal. The control signals are connected to a slot that has matching parameters this is all in C++, that slot then creates an object that has everything in it required to identify the originating slot, finally a generic signal is emitted with the new object and its this that is connected to Lambda.
So going back, what I really need to (if possible) dynamically create the repeater slot for each signal, which means I wouldn't have to code up and think of every possibly signal up front.
-
@SPlatten said in Is this possible?:
, which means I wouldn't have to code up and think of every possibly signal up front.
But I think that's precisely what the discussion here is all about! C++ is statically typed, and you cannot think of every possible signal which is what you need at compile-time, and that is the issue! And I/others do not see how using lambdas would obviate that, lambdas are still subject to compile-type type checking, for both their parameters and the methods they might call....
Perhaps we should leave it at that and you see how you get on, because we're not getting very far with a "You say, I say" ... :)
-
@JonB , the reason for Lambda is purely to provide an interface between the C++ and the JavaScript. Using a repeater signal allows me to use the same signal out of the repeater with parameters that will never change, the receiving signal which fires the repeater is the one that will have the differences.
Please don't get me wrong, I'm not arguing with you, I was under the impression that what I'm trying to achieve is actually possible.
-
@SPlatten Let's say you have 3 signals with different parameter lists:
void signal1(int); void signal2(char*, float); void signal3(QString, QVariant, QString);
How will you connect them to ONE slot (as you wrote: "produce a map of available signals then connect all these signals to a single slot in C++") and handle all 3 different parameter lists in that slot? Don't forget: C++ is statically typed language as already pointed out in this thread. You have different parameter number and different parameter types.
-
@SPlatten The way I understand your approach is this:
cpp_signal(int) js_slot(int) cpp_signal(float) js_slot(float) cpp_signal(string, int) -> cpp_signal(common_param) -> cpp_slot(common_param) -> js_slot(string, int) cpp_signal(custom_class) js_slot(custom_class_projection) cpp_signal(whatever) js_slot(whatever_projection)
While you can do the two transitions on the right I don't see how you can accomplish the one on the left. The target of connection takes either the same amount of the same typed parameters as the sender or less. You would have to add an extra layer of forwarding signals between the two columns on the left, but that just means a slot with every possible combination of parameters.
Lambdas have nothing to do with any of that. A lambda, for all intents and purposes of this topic, is just a struct with () operator and follows the same static typing rules as a struct.
That being said I really don't like to say something can't be done, so for the sake of being open minded I would imagine this could be implemented with a new type of connect that in pseudo code works like this:
struct Param { QString name; QVariant value; } using ParamPack = QVector<Param>; connect(sender, &Sender::signal, func);
where
func
is of fixed signature e.g.void (*)(const ParamPack& params)
and connect is a template function that does parameter packing. This packing could in theory be done with some tuple like template shenanigans, but I suspect you'd have to use private Qt headers to get to all the stuff needed.
To be fair, this would only work for an explicit connect statement with explicit signal so that template can take apart its signature. I don't see a way to do that by iterating over a list of signals. There's just no static type information in that case that the template could get to. -
@jsulm, ok if its not possible then I will have to implement my original design which is to produce a slot for each signal type and internal to that slot I will construct an object and then emit the generic signal with the constructed object which will pass on the originating signal details. This signal will be use Lambda.
To illustrate:
C++ signal(int) -> Internal slot (repeater) (int) | C++ signal(float) -> Internal slot (repeater) (float) | C++ signal(string, int) -> Internal slot (repeater) (string, int) |- Emit C++ signal(custom_class) -> Internal slot (repeater) (custom_class) | C++ signal(whatever) -> Internal slot (repeater) (whatever) |
The repeaters will create a JSON object which will include details of the source signal and control associated with the signal. The Emit on the right will be a signal that will be common to all control signals and have only the constructed JSON object built by the repeater.
-
That's doable, but it means you will have to implement a repeater for every possible combination of any types of parameters. Unless you heavily limit possible signal signatures I don't think that's practical.
-
@Chris-Kawa , Yep, thats what I was hoping to avoid, but if its unavoidable, then I'll just have to do it and release patches / updates as I add additional controls to the engine.
-
@SPlatten said in Is this possible?:
then I'll just have to do it and release patches / updates as I add additional controls to the engine.
Originally you wrote:
dynamic map capable of storing the information for any QT control or derived control and it’s signals.
I don't mean to pour cold water/put up obstacles gratuitously for your work. But if you are going to have to release patches for each signal, what happens if I derive my own
QWidget
s and add my own new, dedicated signals? Which I do frequently! -
My project is to create an engine that allows anyone to develop GUI applications for any platform that the engine is available.
Is it NOT intended to allow developers to modify the engine itself. It will allow anyone using the engine to develop GUI applications with XML and JavaScript.
-
@SPlatten
OK, but a limitation is I cannot define any signals in my code? E.g. to go with a new widget I derive. You regard that as "modify the engine itself"?