Reading string from struct in Arduino PROGMEM - arduino

I want to read a string from a struct stored in Arduino PROGMEM:
struct commandCode {
int code;
const char *name;
};
const PROGMEM commandCode commands[LAST_COMMAND] = {
{ CMD_DEMO, "DEMO" } ,
{ CMD_STOP, "STOP"} ,
{ CMD_FORWARD, "FORWARD"},
{ CMD_BACKWARD, "BACKWARD"},
{ CMD_TURN_LEFT, "TURN LEFT"},
{ CMD_TURN_RIGHT, "TURN RIGHT"},
{ CMD_WAIT, "WAIT"},
{ CMD_WAIT_DONE, "WAIT DONE"},
};
This code prints the string just fine:
void CommandCodes::show() {
Serial.print(LAST_COMMAND);
Serial.println(" Comands Defined:");
for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
CommandCodes::commandCode cmd = commands[i];
showCommand(cmd);
}
}
void CommandCodes::showCommand(commandCode cmd) {
if (cmd.code > FIRST_COMMAND) {
Serial.print(F("["));
Serial.print(cmd.code);
Serial.print(F("] "));
Serial.println(cmd.name);
}
}
This code bombs and restarts the program:
const char* CommandCodes::name(int code) {
for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
CommandCodes::commandCode cmd = commands[i];
if (cmd.code == code) {
return cmd.name;
}
}
return NULL;
}
What is the code to return a pointer to cmd.name?

As the structure only contains a pointer, not the string data, the strings are still stored in RAM.
Also you aren't reading from PROGMEM when you access the data, the fact it's working in certain situations is just luck, but it's still incorrect.
To place all the data in PROGMEM, you'll need to allocate space inside the struct for it. As the largest string is 11 chars + null you can make the length 12.
struct commandCode {
int code;
const char name[12];
};
const commandCode commands[] PROGMEM = {
{ CMD_DEMO, "DEMO" } ,
{ CMD_STOP, "STOP"} ,
{ CMD_FORWARD, "FORWARD"},
{ CMD_BACKWARD, "BACKWARD"},
{ CMD_TURN_LEFT, "TURN LEFT"},
{ CMD_TURN_RIGHT, "TURN RIGHT"},
{ CMD_WAIT, "WAIT"},
{ CMD_WAIT_DONE, "WAIT DONE"}
}
As the internals of each struct are in PROGMEM you need to read them using special functions. You cannot read them directly.
This also means you cannot copy an item like you have done:
CommandCodes::commandCode cmd = commands[i];
But you can use a reference.
const commandCode &cmd = commands[i];
However, like I mentioned above, the elements of the referenced struct still need to be accessed properly.
For an integer, you need to use pgm_read_word. For the strings, you can trick the Serial class into printing it for you as it handles flash strings (like where you use the F() macro). This can be done by casting the pointer to a const __FlashStringHelper*.
Here is a working sketch showing how to access each part properly. Give it a test and try and work out what I've done. I'm sure you'll have some questions, so just add them to the comments of this answer and I'll update my answer for you.
struct commandCode {
int code;
const char name[12];
};
enum COMMANDS{
CMD_DEMO,
CMD_STOP,
CMD_FORWARD,
CMD_BACKWARD,
CMD_TURN_LEFT,
CMD_TURN_RIGHT,
CMD_WAIT,
CMD_WAIT_DONE,
};
const commandCode commands[] PROGMEM = {
{ CMD_DEMO, "DEMO" } ,
{ CMD_STOP, "STOP"} ,
{ CMD_FORWARD, "FORWARD"},
{ CMD_BACKWARD, "BACKWARD"},
{ CMD_TURN_LEFT, "TURN LEFT"},
{ CMD_TURN_RIGHT, "TURN RIGHT"},
{ CMD_WAIT, "WAIT"},
{ CMD_WAIT_DONE, "WAIT DONE"}
};
#define FIRST_COMMAND 0
#define LAST_COMMAND sizeof(commands) / sizeof(*commands)
#define FSH (const __FlashStringHelper*) //A helper to allow printing the PROGMEM strings.
void show() {
for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
const commandCode &cmd = commands[i];
showCommand(cmd);
}
}
void showCommand(const commandCode &cmd) {
if ( pgm_read_word( &cmd.code ) > FIRST_COMMAND) {
Serial.print(F("["));
Serial.print( pgm_read_word( &cmd.code ) );
Serial.print(F("] "));
Serial.println( FSH( cmd.name ) );
}
}
const char* name(int code) {
for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
const commandCode &cmd = commands[i];
if (pgm_read_word(&cmd.code) == code) {
return cmd.name; //As cmd.name resolves to a pointer it can be passed back as is.
//However to use the 'pointed to data' it will have to be accessed properly.
}
}
return NULL;
}
void setup() {
Serial.begin(9600);
Serial.println("Show test");
show();
Serial.println("Name test");
for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
Serial.println( FSH( name(i) ) );
}
Serial.println("Done");
}
void loop() {}

Related

memmove implementation throws segmentation fault while copying a character array

Hi I tried to write my own version of memmove and I find the following code resulting in a segmentation fault. It would be great if someone could help me figure out why this behavior would occur!
However, when I use something like:
char source[20] = "Hello, this is Piranava", the code works fine!
void *memmoveLocal(void *dest, const void *src, unsigned int n)
{
char *destL = dest;
const char *srcL = src;
int i = 0;
if(dest == NULL || src == NULL)
{
return NULL;
}
else
{
// if dest comes before source, even if there's an overlap, we should move forward
// because if there's an overlap (when dest < src) and we move backward, we'd overwrite the overlapping bytes in src
if(destL < srcL)
{
printf("Forward\n");
while(i < n)
{
destL[i] = srcL[i];
i++;
}
}
else // in all other cases (even if there's overlap or no overlap, we can move backward)
{
printf("Backward\n");
i = n - 1;
while(i >= 0)
{
destL[i] = srcL[i];
i--;
}
}
}
return dest;
}
void main()
{
char *source = "Hello, this is ABC";
char *destination = malloc(strlen(source)+1);
memmoveLocal(source+5, source, 5);
printf("Source: %s \nDestination: %s, size: %d\n", source, destination, strlen(destination));
}
However, if I replace
char *source = "Hello, this is ABC";
with
char source[20] = "Hello, this is ABC";
, it works fine!
memmoveLocal(source+5, source, 5);
You are trying to overwrite a string literal, which is not writable.
Did you intend to memmoveLocal(destination, source+5, 5) instead?
char source[20] = "Hello, this is ABC";
That turns source from a string literal into a char[] array initialized with a string literal. The array is writable, so your program no longer crashes.

I can not connect an external button to my Homekit system (ESP8266 without homebridge)

After watching this video. https://www.youtube.com/watch?v=QBj8OLig8Kg
Which is how to create a homekit system without having to use a brigde I wanted to create my own.
My idea is to have 2 different lights that are connected to the internet and I handle them with siri and 2 external buttons that can turn off the light without having to always use siri. (for example if my cell phone runs out of battery). All this in the same ESP 8266.
I could already operate the two lights without the need of a homebridge with the following code
#define Relay1 16 //D0
#define Relay2 5 //D1
#define button1 14 //D5
#define button2 12 //D6
#define button3 13 //D7
bool state1 = false;
bool state2 = false;
// ---------------------------------------------------- Inicializacion de wifi -------------------------------------------------
static void wifi_init() {
struct sdk_station_config wifi_config = {
.ssid = WIFI_SSID,
.password = WIFI_PASSWORD,
};
sdk_wifi_set_opmode(STATION_MODE);
sdk_wifi_station_set_config(&wifi_config);
sdk_wifi_station_connect();
}
// -------------------------------------------------------- Logica de Relay1 -----------------------------------------------------
void relay1_write(bool on) {
gpio_write(Relay1, on ? 1 : 0);
}
void relay1_init() {
gpio_enable(Relay1, GPIO_OUTPUT);
relay1_write(state1);
}
void relay1_identify_task(void *_args) {
for (int i=0; i<3; i++) {
for (int j=0; j<2; j++) {
relay1_write(true);
vTaskDelay(100 / portTICK_PERIOD_MS);
relay1_write(false);
vTaskDelay(100 / portTICK_PERIOD_MS);
}
vTaskDelay(250 / portTICK_PERIOD_MS);
}
relay1_write(state1);
vTaskDelete(NULL);
}
void relay1_identify(homekit_value_t _value) {
printf("Relay 1 identify\n");
xTaskCreate(relay1_identify_task, "Relay Identify", 128, NULL, 2, NULL);
}
homekit_value_t relay1_on_get() {
return HOMEKIT_BOOL(state1);
}
void relay1_on_set(homekit_value_t value) {
if (value.format != homekit_format_bool) {
printf("Invalid value format: %d\n", value.format);
return;
}
state1 = value.bool_value;
relay1_write(state1);
}
// -------------------------------------------------------- Logica de Relay2 -----------------------------------------------------
void relay2_write(bool on) {
gpio_write(Relay2, on ? 1 : 0);
}
void relay2_init() {
gpio_enable(Relay2, GPIO_OUTPUT);
relay2_write(state2);
}
void relay2_identify_task(void *_args) {
for (int i=0; i<3; i++) {
for (int j=0; j<2; j++) {
relay2_write(true);
vTaskDelay(100 / portTICK_PERIOD_MS);
relay2_write(false);
vTaskDelay(100 / portTICK_PERIOD_MS);
}
vTaskDelay(250 / portTICK_PERIOD_MS);
}
relay2_write(state2);
vTaskDelete(NULL);
}
void relay2_identify(homekit_value_t _value) {
printf("Relay 2 identify\n");
xTaskCreate(relay2_identify_task, "Relay Identify", 128, NULL, 2, NULL);
}
homekit_value_t relay2_on_get() {
return HOMEKIT_BOOL(state2);
}
void relay2_on_set(homekit_value_t value) {
if (value.format != homekit_format_bool) {
printf("Invalid value format: %d\n", value.format);
return;
}
state2 = value.bool_value;
relay2_write(state2);
}
// ------------------------------------------------ Configuracion del server de Homekit ------------------------------------------
homekit_accessory_t *accessories[] = {
HOMEKIT_ACCESSORY(.id=1, .category=homekit_accessory_category_lightbulb, .services=(homekit_service_t*[]){
HOMEKIT_SERVICE(ACCESSORY_INFORMATION, .characteristics=(homekit_characteristic_t*[]){
HOMEKIT_CHARACTERISTIC(NAME, "Cuarto"),
HOMEKIT_CHARACTERISTIC(MANUFACTURER, "Estonian Port"),
HOMEKIT_CHARACTERISTIC(SERIAL_NUMBER, "ASD123"),
HOMEKIT_CHARACTERISTIC(MODEL, "C.U.C.A"),
HOMEKIT_CHARACTERISTIC(FIRMWARE_REVISION, "0.1"),
HOMEKIT_CHARACTERISTIC(IDENTIFY, relay1_identify),
NULL
}),
HOMEKIT_SERVICE(LIGHTBULB, .primary=true, .characteristics=(homekit_characteristic_t*[]){
HOMEKIT_CHARACTERISTIC(NAME, "Luz"),
HOMEKIT_CHARACTERISTIC(ON, false,
.getter=relay1_on_get,
.setter=relay1_on_set
),
NULL
}),
NULL
}),
HOMEKIT_ACCESSORY(.id=2, .category=homekit_accessory_category_lightbulb, .services=(homekit_service_t*[]){
HOMEKIT_SERVICE(ACCESSORY_INFORMATION, .characteristics=(homekit_characteristic_t*[]){
HOMEKIT_CHARACTERISTIC(NAME, "Cuarto"),
HOMEKIT_CHARACTERISTIC(MANUFACTURER, "Estonian Port"),
HOMEKIT_CHARACTERISTIC(SERIAL_NUMBER, "ASD123"),
HOMEKIT_CHARACTERISTIC(MODEL, "C.U.C.A"),
HOMEKIT_CHARACTERISTIC(FIRMWARE_REVISION, "0.1"),
HOMEKIT_CHARACTERISTIC(IDENTIFY, relay2_identify),
NULL
}),
HOMEKIT_SERVICE(LIGHTBULB, .primary=true, .characteristics=(homekit_characteristic_t*[]){
HOMEKIT_CHARACTERISTIC(NAME, "Luz"),
HOMEKIT_CHARACTERISTIC(ON, false,
.getter=relay2_on_get,
.setter=relay2_on_set
),
NULL
}),
NULL
}),
NULL
};
homekit_server_config_t config = {
.accessories = accessories,
.password = "111-11-111"
};
// ----------------------------------------------------------- MAIN ----------------------------------------------------------
void user_init(void) {
uart_set_baud(0, 115200);
wifi_init();
relay1_init();
relay2_init();
homekit_server_init(&config);
}
but i cant figure out how to implemet the two external buttons. I tried implementing:
void setup()
{
pinMode(button1, INPUT);
pinMode(button2, INPUT);
}
void loop() {
if (digitalRead(button1))
{
relay1_write(state1);
}
}
but it gives me error, since, I have no way to implement the library .
I also try to understand this example.
https://github.com/maximkulkin/esp-homekit-demo/tree/master/examples/button
but it does not apply to what I want to do since in that case because the button is recognized by homekit and I want it to just turn off and turn on the light
Thanks so much for your time!
While writing my question I realized something and after thinking it better I think I should connect the relay of the output to a combined light key. in that way it would give the possibility of turning the light on and off in an analogical way. Sorry for the inconvenience! thank you

How to write a dummy network device driver

I'm trying to write a dummy network driver and have written the code, but I'm facing issue while trying to load driver i.e. it's crashing the kernel sometimes and sometimes it doesn't respond.
Dummy device code
#include <linux/module.h>
#include <linux/netdevice.h>
int virtualNIC_open(struct net_device *dev) {
printk("virtualNIC_open called\n");
netif_start_queue(dev);
return 0;
}
int virtualNIC_release(struct net_device *dev) {
printk("virtualNIC_release called\n");
netif_stop_queue(dev);
return 0;
}
int virtualNIC_xmit(struct sk_buff *skb, struct net_device *dev) {
printk("dummy xmit function called...\n");
dev_kfree_skb(skb);
return 0;
}
int virtualNIC_init(struct net_device *dev);
const struct net_device_ops my_netdev_ops = {
.ndo_init = virtualNIC_init,
.ndo_open = virtualNIC_open,
.ndo_stop = virtualNIC_release,
.ndo_start_xmit = virtualNIC_xmit,
};
int virtualNIC_init(struct net_device *dev) {
dev->netdev_ops = &my_netdev_ops;
printk("virtualNIC device initialized\n");
}
struct net_device virtualNIC = {
.netdev_ops = &my_netdev_ops,
/* .netdev_ops.ndo_init: virtualNIC_init*/
};
int virtualNIC_init_module(void) {
int result;
strcpy(virtualNIC.name, "virtualNIC");
if((result = register_netdev(&virtualNIC))) {
printk("virtualNIC: Error %d initalizing card ...", result);
return result;
}
return 0;
}
void virtualNIC_cleanup (void)
{
printk ("<0> Cleaning Up the Module\n");
unregister_netdev (&virtualNIC);
return;
}
module_init(virtualNIC_init_module);
module_exit(virtualNIC_cleanup);
MODULE_LICENSE("GPL");
Please help me to figure, where I'm going wrong.
Thanks in Advance
There is already network dummy codec in the mainline kernel. But still if you want to write for the practice. Then I think you can proceed with your own driver as well.
I have modified some of things in your driver. I think you can give one try to it see whether you can see the dummy interface in your ifconfig or not. It is just a sample code (for the interface entry in the ifconfig) and I am not handling any kind of locking or network packet transmission or reception.
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/kernel.h>
#include <linux/etherdevice.h>
struct net_device *virtualNIC;
int virtualNIC_open(struct net_device *dev) {
printk("virtualNIC_open called\n");
return 0;
}
int virtualNIC_release(struct net_device *dev) {
printk("virtualNIC_release called\n");
netif_stop_queue(dev);
return 0;
}
int virtualNIC_xmit(struct sk_buff *skb, struct net_device *dev) {
printk("dummy xmit function called...\n");
dev_kfree_skb(skb);
return 0;
}
const struct net_device_ops my_netdev_ops = {
.ndo_init = virtualNIC_init,
.ndo_open = virtualNIC_open,
.ndo_stop = virtualNIC_release,
.ndo_start_xmit = virtualNIC_xmit,
};
int virtualNIC_init(struct net_device *dev) {
printk("virtualNIC device initialized\n");
return 0;
};
static void virtual_setup(struct net_device *dev){
dev->netdev_ops = &my_netdev_ops;
}
int virtualNIC_init_module(void) {
int result;
virtualNIC = alloc_netdev(0, "virtnC%d", virtual_setup);
if((result = register_netdev(virtualNIC))) {
printk("virtualNIC: Error %d initalizing card ...", result);
return result;
}
return 0;
}
void virtualNIC_cleanup (void)
{
printk ("<0> Cleaning Up the Module\n");
unregister_netdev (virtualNIC);
}
module_init(virtualNIC_init_module);
module_exit(virtualNIC_cleanup);
MODULE_LICENSE("GPL");
This is very helpful, I just want to add this part of code:
virtualNIC = alloc_netdev (0, "virtnC%d", NET_NAME_UNKNOWN, virtual_setup);
this has 4 parameter in new kernel...

How to attach to existing shared memory from Qt?

I have created a shared memory segment with the help of a binary in C and written some data into it. Now I want read that data from Qt. How to attach to existing shared memory from Qt?
QSharedMemory isn't really meant to interoperate with anything else. On Unix, it is implemented via SYSV shared memory, but it passes Qt-specific arguments to ftok:
::ftok(filename.constData(), qHash(filename, proj_id));
You could emulate this behavior in your C code, but I don't think it's necessary.
Instead of opening a shared memory segment, simply map a file to memory, and access it from multiple processes. On Qt, QFile::map does what you need.
The example below shows both techniques: using SYSV shared memory and using memory-mapped files:
// https://github.com/KubaO/stackoverflown/tree/master/questions/sharedmem-interop-39573295
#include <QtCore>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <stdexcept>
#include <string>
First, let's have a shared data structure.
struct Data {
int a = 1;
bool b = true;
char c = 'S';
bool operator==(const Data & o) const { return o.a == a && o.b == b && o.c == c; }
static void compare(const void * a, const void * b) {
auto data1 = reinterpret_cast<const Data*>(a);
auto data2 = reinterpret_cast<const Data*>(b);
Q_ASSERT(*data1 == *data2);
}
};
We definitely want error checking, so let's add some helpers that make that easier:
void check(bool ok, const char * msg, const char * detail) {
if (ok) return;
std::string str{msg};
str.append(": ");
str.append(detail);
throw std::runtime_error{str};
}
void check(int f, const char * msg) { check(f != -1, msg, strerror(errno)); }
void check(void * f, const char * msg) { check(f != MAP_FAILED, msg, strerror(errno)); }
void check(bool rc, const QSharedMemory & shm, const char * msg) { check(rc, msg, shm.errorString().toLocal8Bit()); }
void check(bool rc, const QFile & file, const char * msg) { check(rc, msg, file.errorString().toLocal8Bit()); }
And we need RAII wrappers for C APIs:
struct noncopyable { Q_DISABLE_COPY(noncopyable) noncopyable() {} };
struct ShmId : noncopyable {
int id;
ShmId(int id) : id{id} {}
~ShmId() { if (id != -1) shmctl(id, IPC_RMID, NULL); }
};
struct ShmPtr : noncopyable {
void * ptr;
ShmPtr(void * ptr) : ptr{ptr} {}
~ShmPtr() { if (ptr != (void*)-1) shmdt(ptr); }
};
struct Handle : noncopyable {
int fd;
Handle(int fd) : fd{fd} {}
~Handle() { if (fd != -1) close(fd); }
};
Here's how to interoperates SYSV shared memory sections between C and Qt. Unfortunately, unless you reimplement qHash in C, it's not possible:
void ipc_shm_test() {
QTemporaryFile shmFile;
check(shmFile.open(), shmFile, "shmFile.open");
// SYSV SHM
auto nativeKey = QFile::encodeName(shmFile.fileName());
auto key = ftok(nativeKey.constData(), qHash(nativeKey, 'Q'));
check(key, "ftok");
ShmId id{shmget(key, sizeof(Data), IPC_CREAT | 0600)};
check(id.id, "shmget");
ShmPtr ptr1{shmat(id.id, NULL, 0)};
check(ptr1.ptr, "shmat");
new (ptr1.ptr) Data;
// Qt
QSharedMemory shm;
shm.setNativeKey(shmFile.fileName());
check(shm.attach(QSharedMemory::ReadOnly), shm, "shm.attach");
auto ptr2 = shm.constData();
Data::compare(ptr1.ptr, ptr2);
}
Here's how to interoperate memory-mapped files:
void mmap_test() {
QTemporaryFile shmFile;
check(shmFile.open(), "shmFile.open");
shmFile.write({sizeof(Data), 0});
check(true, shmFile, "shmFile.write");
check(shmFile.flush(), shmFile, "shmFile.flush");
// SYSV MMAP
Handle fd{open(QFile::encodeName(shmFile.fileName()), O_RDWR)};
check(fd.fd, "open");
auto ptr1 = mmap(NULL, sizeof(Data), PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd.fd, 0);
check(ptr1, "mmap");
new (ptr1) Data;
// Qt
auto ptr2 = shmFile.map(0, sizeof(Data));
Data::compare(ptr1, ptr2);
}
And finally, the test harness:
int main() {
try {
ipc_shm_test();
mmap_test();
}
catch (const std::runtime_error & e) {
qWarning() << e.what();
return 1;
}
return 0;
}

QSignalSpy to capture a reference argument

It is not possible to capture an argument that has been passed as reference with a QSignalSpy:
QSignalSpy spy( myObject, SIGNAL(foo(int&)));
...
int& i=spy.at(0).at(0).value<int&>();
Since a QVariant can not contain a reference member. Plain logic.
But are there other solutions to check the passed-in argument?
Since Qt 5, we can simply connect to a lambda function, which makes the use of the QSignalSpy unnecessary:
std::vector<Value> values;
QObject::connect(myObject, &MyObject::foo,
[&](const auto &value)
{ values.emplace_back(value); });
myObject.somethingCausingFoo();
ASSERT_EQ(1u, values.size());
EXPECT_EQ(expectedValue, values.at(0));
An "ugly solution" would be to hack the fairly simple QSignalSpy code in order to handle the reference passed arguments. I provide a minimal working example for int reference arguments. The only changes were made to initArgs and appendArgs functions.
Notice that with this approach you will only be able to check the value of the passed argument by reference. You will not be able to change it's value.
In the initArgs function we check if we have references by argument and we populate the shouldreinterpret list.
void initArgs(const QMetaMethod &member)
{
QList<QByteArray> params = member.parameterTypes();
for (int i = 0; i < params.count(); ++i) {
int tp = QMetaType::type(params.at(i).constData());
if (tp == QMetaType::Void)
{
qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.",
params.at(i).constData());
// Check if we have a reference by removing the & from the parameter name
QString argString(params.at(i).constData());
argString.remove("&");
tp = QMetaType::type(argString.toStdString().c_str());
if (tp != QMetaType::Void)
shouldReinterpret << true;
}
else
shouldReinterpret << false;
args << tp;
}
}
and the appendArgs function, where we reinterpret the passed by reference arguments:
void appendArgs(void **a)
{
QList<QVariant> list;
for (int i = 0; i < args.count(); ++i) {
QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
if (shouldReinterpret.at(i))
{
switch (type)
{
case QMetaType::Int:
list << QVariant(type, &(*reinterpret_cast<int*>(a[i + 1])));
break;
// Do the same for other types
}
}
else
list << QVariant(type, a[i + 1]);
}
append(list);
}
Complete code for reference:
class MySignalSpy: public QObject, public QList<QList<QVariant> >
{
public:
MySignalSpy(QObject *obj, const char *aSignal)
{
#ifdef Q_CC_BOR
const int memberOffset = QObject::staticMetaObject.methodCount();
#else
static const int memberOffset = QObject::staticMetaObject.methodCount();
#endif
Q_ASSERT(obj);
Q_ASSERT(aSignal);
if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE) {
qWarning("QSignalSpy: Not a valid signal, use the SIGNAL macro");
return;
}
QByteArray ba = QMetaObject::normalizedSignature(aSignal + 1);
const QMetaObject *mo = obj->metaObject();
int sigIndex = mo->indexOfMethod(ba.constData());
if (sigIndex < 0) {
qWarning("QSignalSpy: No such signal: '%s'", ba.constData());
return;
}
if (!QMetaObject::connect(obj, sigIndex, this, memberOffset,
Qt::DirectConnection, 0)) {
qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect.");
return;
}
sig = ba;
initArgs(mo->method(sigIndex));
}
inline bool isValid() const { return !sig.isEmpty(); }
inline QByteArray signal() const { return sig; }
int qt_metacall(QMetaObject::Call call, int methodId, void **a)
{
methodId = QObject::qt_metacall(call, methodId, a);
if (methodId < 0)
return methodId;
if (call == QMetaObject::InvokeMetaMethod) {
if (methodId == 0) {
appendArgs(a);
}
--methodId;
}
return methodId;
}
private:
void initArgs(const QMetaMethod &member)
{
QList<QByteArray> params = member.parameterTypes();
for (int i = 0; i < params.count(); ++i) {
int tp = QMetaType::type(params.at(i).constData());
if (tp == QMetaType::Void)
{
qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.",
params.at(i).constData());
QString argString(params.at(i).constData());
argString.remove("&");
tp = QMetaType::type(argString.toStdString().c_str());
if (tp != QMetaType::Void)
shouldReinterpret << true;
}
else
shouldReinterpret << false;
args << tp;
}
}
void appendArgs(void **a)
{
QList<QVariant> list;
for (int i = 0; i < args.count(); ++i) {
QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
if (shouldReinterpret.at(i))
{
switch (type)
{
case QMetaType::Int:
int k = (*reinterpret_cast<int*>(a[i + 1]));
list << QVariant(type, &k);
break;
}
}
else
list << QVariant(type, a[i + 1]);
}
append(list);
}
// the full, normalized signal name
QByteArray sig;
// holds the QMetaType types for the argument list of the signal
QList<int> args;
// Holds the indexes of the arguments that
QList<bool> shouldReinterpret;
};

Resources