I'm trying to hack with Qt's signals and slots, and I ran into an issue where QMetaType::invokeMethod won't properly pass pointer arguments to the slot being called.
call(QObject *receiver, const char *slot, const QList<QGenericArgument> &args)
{
const QMetaObject *meta = receiver->metaObject();
bool success = meta->invokeMethod(receiver, slot,
args.value(0, QGenericArgument()),
args.value(1, QGenericArgument()),
args.value(2, QGenericArgument()),
...
args.value(9, QGenericArgument()));
}
Then I call it the following way:
MyReceiver *receiver;
MyObject *myObject;
call(receiver, "mySlot", QList<QGenericArgument>() << Q_ARG(MyObject *, myObject));
Where class MyObject : public QObject { ... }. I also do Q_DECLARE_METATYPE(MyObject *) and qRegisterMetaType<MyObject *>("MyObject *")
What happens is that the slot on the receiver is being invoked, but with the value of the argument is always 0 no matter what I pass to the call(...) as Q_ARG
Out of curiosity I looked into the auto-generated MOC file of the receiver, and found that the slots are invoked with the following code:
void MyReceiver::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
Q_ASSERT(staticMetaObject.cast(_o));
MyReceiver *_t = static_cast<MyReceiver *>(_o);
switch (_id) {
case 0: _t->mySlot((*reinterpret_cast< MyObject*(*)>(_a[1]))); break;
default: ;
}
}
}
Turns out that the value of _a[1] bears proper address of MyObject *. But the reinterpret_cast turns it into 0.
Now I have the following questions:
1) How to programmatically invoke a slot and make sure that the pointer arguments are properly passed to the slot?
2) What does this *reinterpret_cast< MyObject*(*)>(_a[1]) mean? What the extra parentheses (*) mean, and how to interpret this piece of code?
Ok, I think I figured why it's not working... Q_ARG only will create a pointer to my pointer and store the former. I didn't mention that the call function was part of the Task call meant to invoke a slot later on - when the values wrapped into Q_ARG are already out of scope. Basically Q_ARG only maintains a weak reference to the argument object.
Related
While reading Writing R Extensions, Section 5.13 External pointers and weak references we see an example of a finalizer using R_ClearExternalPtr() albeit the comment next to it saying not really needed:
(...) The finalizer code is simply
static void chanFinalizer(SEXP ptr)
{
if(!R_ExternalPtrAddr(ptr)) return;
inRODBCClose(R_ExternalPtrAddr(ptr));
R_ClearExternalPtr(ptr); /* not really needed */
}
In the post Storing C objects in R by #Martin-Morgan he provides two finalizer examples, where he does include a call to R_ClearExternalPtr() in the first case but not in the second:
First example:
static void
_finalizer(SEXP ext)
{
if (NULL == R_ExternalPtrAddr(ext))
return;
Rprintf("finalizing\n");
char *ptr = (char *) R_ExternalPtrAddr(ext);
Free(ptr);
R_ClearExternalPtr(ext);
}
Second example:
static void
_finalizer(SEXP ext)
{
struct Foo *ptr = (struct Foo*) R_ExternalPtrAddr(ext);
Free(ptr);
}
Can someone help clarify the comment not really needed, and whether I should call R_ClearExternalPtr() at the end of the finalizer.
I am writing code for a school project that will be used for a Chromebook charging station with security. The problem I am having now is when I am detecting if a Chromebook is actually in the slot after the user has been assigned one, I am using a rocker switch to simulate this but when I am declaring the pin to the rocker, the arduino verfier comes up with that
"'slot1' does not name a type".
Code is below:
//class
class Chromebook_slot {
public:
String Name = "";
String RFID_tag = "";
int rocker = 0;
boolean chromebook_in = false;
//class function to check if chromebook is in.
//if not, redirect already to reassigning so chromebook slot is entered as open and free.
void set_if_in()
{
int momen_1_state = digitalRead(momen_1);
int momen_2_state = digitalRead(momen_2);
// the button has been pushed down and the previous process has been completed
// eg. servos would have been reset if there was a previous user
if (momen_1_state == HIGH || momen_2_state == HIGH)
{
chromebook_in = digitalRead(this->rocker);
if (chromebook_in == 0)
{
re_assigning();
}
else
{
return;
}
}
}
};
//this is now outside the class..
//class declarations
Chromebook_slot slot1;
Chromebook_slot slot2;
//variables for rocker switches which will act for detecting chromebooks.
// in my final version, this will replaced by a photoresistor and laser.
slot1.rocker = 3;
slot2.rocker = 2;
Where the function re_assigning() is a separate function declared further in the code and just resets the slot as open for future use.
slot1.rocker = 3;
slot2.rocker = 2;
These are statements that cannot be at the top level of a C++ (or .ino) file. They need to be inside of a function. What's happening is the compiler is looking looking at the slot1 identifier through the lens of potential valid constructions. It sees an identifier, and about the only thing that could legally exist at this point in the code that starts with an identifier like that is some declaration, e.g. int a = 7;, or more abstractly some_type some_more_stuff. So it expects slot1 to be a type, which it isn't, hence the message.
If you want an assignment like those to happen early on in an Arduino program, the simplest thing you could do is put them in setup():
void setup() {
slot1.rocker = 3;
slot2.rocker = 2;
// ...
}
Or, you'd make these part of the Chromebook_slot's constructor, such that they could be given in slot1 and slot2's declaration:
class Chromebook_slot {
public:
Chromebook_slot(int rocker_init_value) {
rocker = rocker_init_value;
}
// ...
Or in a maybe less familiar but more proper form, using the constructor's initialization list:
class Chromebook_slot {
public:
Chromebook_slot(int rocker_init_value)
: rocker(rocker_init_value) {}
// ...
Once you have a constructor for Chromebook_slot, your variables can become:
Chromebook_slot slot1(3);
Chromebook_slot slot2(2);
Consider this JS code:
function handleSig() {
emitter.someSig.disconnect(handleSig);
// do some work here
}
emitter.someSig.connect(handleSig);
Can it be written without the explicit disconnect and the named function?
Ideally, I'd like something like this:
emitter.someSig.connect(
function() {
// do some work here
},
Qt.SingleShotConnection
);
Near-duplicate: Automatically disconnect after first signal emission - but that question is about Python and mine is about QML and C++.
You can create a small helper function, which does the disconnecting for you, like such:
function connectOnce(sig, slot) {
var f = function() {
slot.apply(this, arguments)
sig.disconnect(f)
}
sig.connect(f)
}
As demonstration of the usage:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: myWindow
visible: true
width: 600
height: 600
color: 'white'
signal action(string name)
function slot(name) {
console.log(name)
}
Button {
text: 'connect'
onClicked: {
connectOnce(action, slot)
}
}
Button {
y: 80
text: 'action'
onClicked: {
action('test')
}
}
function connectOnce(sig, slot) {
var f = function() {
slot.apply(this, arguments)
sig.disconnect(f)
}
sig.connect(f)
}
}
The upper two Buttons will connect slot and slot2 in single-shot mode to the signal action.
The Button action will fire the signal action which will execute the slots as many times as they are connected. Then they will be immediately disconnected.
You might put the function connectOnce into a library to have it to your avail where ever you need it.
This solution is easily extended to a more general form, that will connect a function to be executed n times by introducing a counter in the closure:
function connectN(sig, slot, n) {
if (n <= 0) return
var f = function() {
slot.apply(this, arguments)
n--
if (n <= 0) sig.disconnect(f)
}
sig.connect(f)
}
I found an answer for C++ here, though it's a bit inelegant:
https://forum.qt.io/topic/67272/how-to-create-a-single-shot-one-time-connection-to-a-lambda/2
Code from that link:
QMetaObject::Connection * const connection = new QMetaObject::Connection;
*connection = connect(_textFadeOutAnimation, &QPropertyAnimation::finished, [this, text, connection](){
QObject::disconnect(*connection);
delete connection;
});
I'm still holding out for better C++ answers, and for a QML answer.
It would probably most convenient to make a template implementing the same syntax as a regular QObject::connect call.
I've made these two templates below, using Stefan Monov's own answer. The first is method pointer based, the second one is lambda based.
#ifndef CONNECT_ONCE_H
# define CONNECT_ONCE_H
# include <QObject>
# include <memory>
template<typename EMITTER, typename SIGNAL, typename RECEIVER, typename... ARGS>
void connectOnce(EMITTER* emitter, SIGNAL signal, RECEIVER* receiver, void (RECEIVER::*slot)(ARGS...), Qt::ConnectionType connectionType = Qt::AutoConnection)
{
auto connection = std::make_shared<QMetaObject::Connection>();
auto onTriggered = [connection, receiver, slot](ARGS... arguments){
(receiver->*slot)(arguments...);
QObject::disconnect(*connection);
};
*connection = QObject::connect(emitter, signal, receiver, onTriggered, connectionType);
}
template<typename EMITTER, typename SIGNAL, typename RECEIVER, typename SLOT, typename... ARGS>
void connectOnce(EMITTER* emitter, SIGNAL signal, RECEIVER* receiver, SLOT slot, Qt::ConnectionType connectionType = Qt::AutoConnection)
{
std::function<void (ARGS...)> callback = slot;
auto connection = std::make_shared<QMetaObject::Connection>();
auto onTriggered = [connection, callback](ARGS... arguments) {
callback(arguments...);
QObject::disconnect(*connection);
};
*connection = QObject::connect(emitter, signal, receiver, onTriggered, connectionType);
}
#endif
About the lambda based template, I couldn't get it to compile when directly declaring std::function as a parameter, as the compiler couldn't recognize a lambda parameter as a valid candidate... which is why I'm declaring SLOT as a template parameter, and then casting it to std::function. It's not as concise as I would've liked, but it works.
I am not able to call negedge of all the subscribers who register for clock, all subscribers also derive from ClkIf
class ClkAdapter : public ClkIf
{
virtual void negedge()
{
for(std::list<ClkIf*>::iterator it = clk_list.begin(); it != clk_list.end(); it++)
(it->negedge)();
}
virtual void posedge()
{ clk_cnt++; }
void registerForClock(ClkIf* module)
{ clk_list.push_back(module); }
std::list<ClkIf*> clk_list;
unsigned long long clk_cnt;
};
error: request for member 'negedge' in '* it.std::_List_iterator<_Tp>::operator-> with _Tp = ClkIf*', which is of non-class type 'ClkIf*'
Error in negedge function, What is wrong in this code??
You have a list of pointers, so the list iterator would work similarly to a double pointer (that is, ClkIf**). Thus, you would have to call (*it)->negedge() within the loop. The (*it) fetchs the current ClkIf* element first, and then the -> operator calls the function on that value.
I am fairly new to Qt, and I am trying to do some Android Development. I am working with Qt and using the QAndroidJNIEnvironment. In the code, I am implementing my native method using QMetaObject::invokeMethod to invoke a slot in the QMainWindow header. The problem is that the native method in the java file has a parameter that is a java integer array(equivalent type I believe in QAndroindJniObject is jintArray). I can't find the corresponding c++/Qt type to place in the Q_ARG(type, value ) macro to resolve the argument. Please help me understand what I am doing wrong, as i thought the equivalent type to jintArray was int [], but I receive error when I use that. Thanks in advance for the help.
onReceiveNativeMounted (JNIEnv * env, jobject obj,jint array_index,jintArray version)
{
QMetaObject::invokeMethod(&MainWindow::instance(), "onReceiveMounted"
, Qt::QueuedConnection, Q_ARG(int, array_index),Q_ARG(int[], version));
return array_index;
}
the error i receive is below:
error: no matching function for call to
'QArgument<int []>::QArgument(const char [6], _jarray*&)'
#define Q_ARG(type, data) QArgument<type >(#type, data)
^
As requested, the java function signature is below:
public static native int onReceiveNativeMounted(int array_index, int[] version);
You need to access the java arrays according to the JNI API. The easiest thing to do is to convert the data to a QVector. You need to copy the Java array since its lifetime is not under your control (unless you wish it to be, but that makes life much harder than it needs to be).
QVector toQVector(JNIEnv * env, jintArray arr) {
auto len = (*env)->GetArrayLength(env, arr);
QVector result(len);
auto data = (*env)->GetIntArrayElements(env, arr, 0);
for (int i = 0; i < len; ++i)
result[i] = data[i];
(*env)->ReleaseIntArrayElements(env, arr, data, 0);
return result;
}
It is a bit more performant to perform the call directly from a functor, rather than through invokeMethod. The functor can capture the vector:
int onReceiveNativeMounted (JNIEnv * env, jobject obj, jint array_index, jintArray version)
{
auto window = &MainWindow::instance();
auto vector = toQVector(env, version);
QObject sig;
sig.connect(&sig, &QObject::destroyed, window, [=]{
window->onReceiveMounted(array_index, vector.data());
}, Qt::QueuedConnection);
return array_index;
}