PDA

View Full Version : template to retrieve QIODevice subclass



onil
5th June 2010, 15:38
Hello everybody,

I am trying to develop a class which has two QIODevice * members
and forwards data from the input device to the output device.
My problem is that my devices are:
- a SerialIODevice * (derived from QextSerialPort)
- a SslIODevice * (derived from QSslSocket)
- other derived classes to come

when I call open, getChar, write, ... members,
the members of QIODevice are called instead of the members of the derived
classes.
I tried to perform qobject_cast based tests in my code and it works.
However the code becomes very compliated. Now I am trying to develop
a template function which will perform the casting work and return
a pointer to the derived class. As it is the first time I develop template
functions, I have compilation errors I don'manage to solve even after
a lot of googleing. Errors are related to unfound prototype where I use
my template function.

Below are relevant parts of my code. Thanks in advance for helping me.

Regards.

.h file:


#ifndef MY_CLASS_H
#define MY_CLASS_H

#include <QObject>
#include <QThread>
#include <QIODevice>

class MyClass : public QThread
{
Q_OBJECT

public:

explicit MyClass(QIODevice* inputDevice = 0, QObject *parent = 0);
~MyClass();

some members ...

private:

template <class T>
T* device_cast(QIODevice* device);

QIODevice* m_pInputDevice;
QIODevice* m_pForwardDevice;

some other members ...

};

#endif // MY_CLASS_H


.cpp file:


#include "MyClass.h"
#include <SerialIODevice.h>
//#include <SslIODevice.h>

MyClass::MyClass(QIODevice* inputDevice, QObject *parent) :
QThread(parent), m_pInputDevice(inputDevice), m_pForwardDevice(NULL), m_bForwardData(false)
{
}

MyClass::~MyClass()
{
if(m_pInputDevice && device_cast(m_pInputDevice)->isOpen())
device_cast(m_pInputDevice)->close();

if(m_pForwardDevice && device_cast(m_pForwardDevice)->isOpen())
device_cast(m_pForwardDevice)->close();
}

// use case example
void MyClass::sendData(quint8 id, QByteArray data)
{
if (!m_pInputDevice)
{
emit error("Input device not set!");
return;
}

if (!device_cast(m_pInputDevice)->isOpen())
{
emit error("Input device is not open");
return;
}

if (device_cast(m_pInputDevice)->openMode() == QIODevice::ReadOnly)
{
emit error("Input device is in read only mode!");
return;
}

// some processing...

if(device_cast(m_pInputDevice)->write(data) == -1)
emit error("Data output failed.");
}

template <class T>
T* MyClass::device_cast(QIODevice* device)
{
if(qobject_cast<SerialIODevice*>(device))
return qobject_cast<SerialIODevice*>(device);
else
return device;
}

borisbn
6th June 2010, 06:54
Just move implementation of a template to header

wysota
6th June 2010, 09:43
Q_OBJECT macro and template classes can't be used together. And also I fail to see the point of your "device_cast" method. The return type has to be known at the time of calling so essentially T can only be QIODevice or QObject because the method has to be able to return QIODevice* with each call. And if that's the case then the method doesn't do any casting, it simply checks whether the device inherits SerialIODevice. You can rewrite the method as:

QIODevice *MyClass::device_cast(QIODevice *device) {
return device;
}

... which doesn't make much sense, of course.

onil
6th June 2010, 13:39
Thank you for both your answers.

borisbn: I'm not at home right now but I will try your solution asap.

wysota: Do you have a better means to propose in order to achieve my goal? At the moment the best I can do apart testing the qobject_cast return value is to retrieve the class name as a string using QMetaObject but then I don't know how to get my pointer from this string (apart from testing all the possible cases which is quite the same as testing qobject_cast return value...)

thank you again.

wysota
7th June 2010, 09:14
wysota: Do you have a better means to propose in order to achieve my goal?
I have no idea what you want to achieve. You always need to assign a return value of a function to a variable of specific type, it won't be determined for you. If you have such constructions:


Class1* func(int) { return new Class1(); }
Class2* func(int) { return new Class2(); }
Then the compiler will throw a compilation error, regardless if you use a template or not, because it will never know which version of func() to choose, especially if Class1 is castable to Class2 or the other way round.

onil
7th June 2010, 09:31
Hi,
borisbn: I have tried your solution but still have 'no matching function for call to 'MyClass::device_cast(QIODevice*&)'

wysota: Yes I know that a function has to return a specific type. However I was hoping (and maybe I'm wrong) that using a template function I can have a function that returns SerialIODevice* when my QIODevice* is also a SerialIODevice*, a SslIODevice* when my QIODevice* is also a SslIODevice* and so on.
If none of the tested types are found, the default return type would be a QIODevice*.

What I exactly want is to store pointers to QIODevices in my class members in order to be able to use any class derived from QIODevice, BUT to call the subclass's functions when I make a call to memeber's functions (getChar, write, open, ...).

Regards.

onil
25th June 2010, 17:54
Hello everybody,

I have been very busy in the last couple of weeks so I left this problem aside. Yesterday I managed to solve my problem using the QMetaObject functionalities. See code example below to see how I managed to achieve my goal.

Thank you all for your help.

Regards.


myClass.h:


#ifndef MY_CLASS_H
#define MY_CLASS_H

#include <QObject>
#include <QThread>
#include <QIODevice>

class MyClass : public QThread
{
Q_OBJECT

public:

explicit MyClass(QIODevice* inputDevice = 0, QObject *parent = 0);
~MyClass();

void sendData(quint8 id, QByteArray data);

some other members ...

private:

QIODevice* m_pInputDevice;
QIODevice* m_pForwardDevice;

some other members ...

};

#endif // MY_CLASS_H


myClass.cpp


#include "MyClass.h"

MyClass::MyClass(QIODevice* inputDevice, QObject *parent) :
QThread(parent), m_pInputDevice(inputDevice), m_pForwardDevice(NULL), m_bForwardData(false)
{
}

MyClass::~MyClass()
{
bool bRet;
if(m_pInputDevice)
{
QMetaObject::invokeMethod(m_pInputDevice, "isOpen",
Qt::DirectConnection, Q_RETURN_ARG(bool, bRet));
if(bRet)
{
QMetaObject::invokeMethod(m_pInputDevice, "close",
Qt::DirectConnection);
}
}

if(m_pForwardDevice)
{
QMetaObject::invokeMethod(m_pForwardDevice, "isOpen",
Qt::DirectConnection, Q_RETURN_ARG(bool, bRet));
if(bRet)
{
QMetaObject::invokeMethod(m_pForwardDevice, "close",
Qt::DirectConnection);
}
}
}

// use case example

void MyClass::sendData(quint8 id, QByteArray data)
{
bool bRet;
if (!m_pInputDevice)
{
emit error("Input device not set!");
return;
}

QMetaObject::invokeMethod(m_pInputDevice, "isOpen",
Qt::DirectConnection, Q_RETURN_ARG(bool, bRet));
if(!bRet)
{
emit error("Input device is not open");
return;
}

QFlags<QIODevice::OpenModeFlag> openMode;
QMetaObject::invokeMethod(m_pInputDevice, "openMode",
Qt::DirectConnection, Q_RETURN_ARG(QFlags<QIODevice::OpenModeFlag>, openMode));
if (openMode == QIODevice::ReadOnly)
{
emit error("Input device is in read only mode!");
return;
}

// some processing...

qint64 bytesWritten;
QMetaObject::invokeMethod(m_pInputDevice, "write",
Qt::DirectConnection, Q_RETURN_ARG(qint64, bytesWritten), Q_ARG(QByteArray, data));
if(bytesWritten == -1)
emit error("Data output failed.");
}




Example of SslIODevice

SslIODevice.h:


#ifndef SSL_IO_DEVICE_H
#define SSL_IO_DEVICE_H

#include <QSslSocket>

class SslIODevice : public QSslSocket
{
Q_OBJECT

public:

explicit SslIODevice();
~SslIODevice();

//For each QIODevice method I use
Q_INVOKABLE returnType method(args);
some other members ...

private:

some private members ...

};

#endif // SSL_IO_DEVICE_H


SslIODevice.cpp


#include "SslIODevice.h"

SslIODevice::SslIODevice() :
QSslSocket()
{
}

SslIODevice::~SslIODevice()
{
}

//For each QIODevice method I use
returnType SslIODevice::method(args)
{
return QSslSocket::method(args);
}