In one of my project I managed to fix a bug switching from a std::vector of objects to a std::vector of pointers to the object. Using the debugger I found that the error occurred when I was calling the std::vector<T,Allocator>::clear that it is supposed to destroy the objects stored in the vector. I am happy I solved the issue but I would like to know why I was able to!
Here I attach the code incriminated:
#include <vector>
#include <memory>
#include <string>
class Base
{
public:
Base(int a);
Base(const Base& o)=default;
Base& operator=(const Base& o)=default;
Base(Base&& o)=default;
Base& operator=(Base&& o)=default;
virtual ~Base()=default;
virtual void Do();
};
class Derived: public Base
{
private:
std::string b;
public:
Derived(int a, std::string& b) : Base(a), b(b) {}
};
//Other derived classes from Base overriding Do
class Main{
private:
std::vector<Derived> v;
public:
Main();
void Do(int i, std::string& b){
/*
if something happens
v.emplace_back(a, b);// push_back equivalently for our purpose
if something else happens
v.erase(iterator)
*/
}
void reset(){
v.clear();
}
};
class Main1{
private:
std::vector<std::unique_ptr<Derived>> v;
public:
Main1();
void Do(int i, std::string& b){
/*
if something happens
v.push_back(std::unique_ptr<Derived>(new Derived(i, b)));
if something else happens
v.erase(iterator)
*/
}
void reset(){
v.clear();
}
};
int main()
{
Main m;
for(int i=0; i<101; ++i){
//something
m.Do(i, b);
//something
if(i%10==0)
m.reset(); //The bug is here. If I use Main1 everything runs smoothly
}
return 0;
}
This is a simplified version of the code that creates me problem. I hope that the detail are enough to give the correct contest for the answer.
The debugger points me deep in the code defining what a deconstructor does reporting a segmentation fault, and personally I cannot understand what that code does.
The compiler I am using is g++ 10.1 with the std=c++17 flag.
Related
I have seen several versions of my question, but I still cannot find an answer that works. I have defined a base class called TwoPort and two derived classes called Reflector and Waveguide as follows:
#include <vector>
class TwoPort
{
public:
TwoPort() { yeast = ywest = 0.0; }
~TwoPort() {}
double getyeast() { return yeast; }
double getywest() { return ywest; }
virtual void step(double xeast, double xwest);
protected:
double yeast;
double ywest;
};
class Reflector :
public TwoPort
{
public:
Reflector() { Gamma = 0.0; }
~Reflector() {}
void step(double xeast, double xwest) override;
void setReflection(double G) { Gamma = G; }
private:
double Gamma;
};
class Waveguide :
public TwoPort
{
public:
Waveguide() { oldest = 0; }
~Waveguide() {}
void step(double xeast, double xwest) override;
void setDelay(unsigned int delay);
private:
std::vector<double> eastBuffer, westBuffer;
unsigned int oldest;
};
My goal is to create a vector containing a mixture of Reflectors and Waveguides. Based on the answers to previous questions like mine, I have tried a number of approaches, but so far none have worked. For example:
int main()
{
std::vector<std::unique_ptr<TwoPort>> tpcascade;
tpcascade.emplace_back(new Reflector);
tpcascade.emplace_back(new Waveguide);
tpcascade.emplace_back(new Reflector);
tpcascade[0]->setRefection(0.25);
}
In this case, the compiler does not recognize the setReflection method. So I tried this:
int main()
{
std::vector<std::unique_ptr<TwoPort>> tpcascade;
auto ref = std::make_unique<Reflector>();
ref->setReflection(0.25);
tpcascade.emplace_back(ref);
}
In this case I can set the reflection but I get a lengthy and complex error message about the emplace statement.
Help!
Did some research and tried a variation on the second approach above:
int main()
{
std::vector<std::unique_ptr<TwoPort>> tpcascade;
auto ref = std::make_unique<Reflector>();
ref->setReflection(0.25);
tpcascade.push_back(std::move(ref));
}
Switching to shared_ptr seems to work too, and is a bit cleaner:
int main()
{
std::vector<std::shared_ptr<TwoPort>> tpcascade;
auto ref = std::make_shared<Reflector>();
ref->setReflection(0.25);
tpcascade.push_back(ref);
}
This also seems to work, but seems risky to me:
tpcascade.push_back(std::make_shared<Reflector>());
std::dynamic_pointer_cast<Reflector>(tpcascade[0])->setReflection(0.25);
I have studied the qt documentation of qRegisterMetaType() where it says that this function must be called before the corresponding type can be used in signal/slot mechanism. However I couldn't find any code example where this has to be done by hand.
This page states, that the registration is done automatically by the moc if it can determine that the type may be registered as meta-type. It looks like this is right, because I tested QSignalSpy, QObject::connect() (direct and queued connection) and QVariant - with just using Q_DECLARE_METATYPE(type) and none of them needed a explicit call to qRegisterMetaType to work.
So my question is: when do I have to call qRegisterMetaType(), because otherwise the code won't work?
The Qt docs say that Q_DECLARE_METATYPE is necessary in case one has a connect being a queued connection.
Adding a Q_DECLARE_METATYPE() makes the type known to all template
based functions, including QVariant. Note that if you intend to use
the type in queued signal and slot connections or in QObject's
property system, you also have to call qRegisterMetaType() since the
names are resolved at runtime.
For this I build a small testing app, that exemplifies the behavior.
Just try to remove the Q_DECLARE_METATYPE(Message) and watch the warnings and output change. In case of the normal connect the macro seems to be unnecessary.
main.cpp
#include <QApplication>
#include <QThread>
#include "MyHeaderView.h"
Q_DECLARE_METATYPE(Message);
int main(int argc, char **args)
{
QApplication app(argc, args);
{
TestObject sender;
TestObject receiver;
QObject::connect(&sender, &TestObject::sendMessage, &receiver, &TestObject::onMessage);
sender.emitMessage(1, 2);
}
// This requires Q_DECLARE_METATYPE(Message);
QThread workerThread;
TestObject sender2;
TestObject receiver2;
receiver2.moveToThread(&workerThread);
workerThread.start();
QObject::connect(&sender2, &TestObject::sendMessage, &receiver2, &TestObject::onMessage, Qt::ConnectionType::QueuedConnection);
sender2.emitMessage(3, 4);
app.exec();
}
TestObject.h
#pragma once
#include <QObject>
#include <QDebug>
struct Message
{
int x;
int y;
};
class TestObject : public QObject
{
Q_OBJECT
public:
void emitMessage(int x, int y) { emit sendMessage(Message{ x,y }); }
signals:
void sendMessage(const Message&);
public slots:
void onMessage(const Message& m) { qDebug() << m.x << m.y; }
};
Is it possible to create variadic signal and connect generic lambda as slot? I mean something like (say, all definitions of involved functions are visible where needed (e.g. at points of instantiation)):
#include <QCoreApplication>
#include <QObject>
#include <QTime>
class A
: public QObject
{
Q_OBJECT
public :
A(QObject * const parent = Q_NULLPTR)
: QObject{parent}
{ ; }
signals :
template< typename ...Ts >
void infoMessage(Ts... args);
public slots :
void run()
{
emit infoMessage("Started at ", QTime::currentTime());
}
};
#include <QTimer>
#include <QtDebug>
#include "main.moc"
int main(int argc, char * argv [])
{
QCoreApplication a{argc, argv};
A a;
auto printInfoMessage = [&] (auto... args)
{
(qInfo() << ... << args);
};
QObject::connect(&a, SIGNAL(infoMessage), printInfoMessage);
QTimer::singleShot(0, &a, &A::run);
return a.exec();
}
Currently it gives an error message:
AUTOGEN: error: process for main.cpp:18: Error: Template function as signal or slot
moc failed...
Here macro SLOT() instead of &A::infoMessage does not help a lot. Is there any workarounds to overcome this limitation?
I know, that some of the answers will contain a using of std::make_tuple and std::index_sequence stuff. But is there less verbose solution?
There is no direct workaround for having template. On of thea reason is that the moc indexes all signals and slots, and this cannot be done for function templates as function templates will generate several functions depending code that is generally not accessible from the moc.
I don't think you can make it work with tuple and such as these are also templates.
A solution could be to use QVariant and/or QVariantList for your arguments.
Please note that the error is not caused by the QObject::connect line, but the the signal declaration in class A.
Also, you cannot replace SIGNAL() and SLOT() at your will, it is either a signal or a slot, it cannot be both.
And finally you should be using this form:
QObject::connect(&a, &A::infoMessage, printInfoMessage);
And since printInfoMessage is using auto parameters, you might need to force the auto resolution using qOverload:
QObject::connect(&a, &A::infoMessage, qOverload<QVariantList>(printInfoMessage));
I want to connect some object's signals derived from an interface class.
The connection is done in QWidget::listenToAnimal(AnimalInterface*).
This does not work because qt_metacall is not a member of 'AnimalInterface' and static assertion failed: No Q_OBJECT in the class with the signal.
Of course AnimalInterface does not have the Q_OBJECT macro and does not inherit QObject because it is an interface...
I want to connect through the interface class because I do not want to manually retype the same code for Cat and for Dog.
Is it possible to connect the signal the way I want to? Perhaps with templates? Is this perhaps a lambda-specific problem?
header:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class AnimalInterface{
public:
virtual ~AnimalInterface();
virtual void makeSound() = 0;
/*signals*/
virtual void madeSound() = 0;
};
Q_DECLARE_INTERFACE(AnimalInterface,"interface")
class Dog : public QObject, public AnimalInterface
{
Q_OBJECT
Q_INTERFACES(AnimalInterface)
public:
void makeSound();
signals:
void madeSound();
};
class Cat : public QObject, public AnimalInterface
{
Q_OBJECT
Q_INTERFACES(AnimalInterface)
public:
void makeSound();
signals:
void madeSound();
};
class Widget : public QWidget
{
Q_OBJECT
Cat *cat_;
Dog *dog_;
public:
Widget(QWidget *parent = 0);
~Widget();
void listenToAnimal(AnimalInterface *animal);
};
#endif // WIDGET_H
cpp:
#include "widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
dog_ = new Dog;
cat_ = new Cat;
listenToAnimal(dog_);
listenToAnimal(cat_);
dog_->makeSound();
cat_->makeSound();
}
void Widget::listenToAnimal(AnimalInterface *animal)
{
connect(animal, &AnimalInterface::madeSound,
this,
[](){
qDebug()<<"animal made sound";
});
}
Widget::~Widget()
{
}
void Cat::makeSound()
{
qDebug()<<"Cat says miaow";
emit madeSound();
}
void Dog::makeSound()
{
qDebug()<<"Dog says wuff";
emit madeSound();
}
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
Since you know the derived type at compile type, you can connect to the proper, statically-known QObject-derived type. No need for dynamic casting or anything of the sort. You just don't want the listenToAnimal method to be available for non-AnimalInterface-inheriting types, though, even if it they have a compatible madeSound method:
C++11
#include <type_traits>
template< class T,
typename =
typename std::enable_if<std::is_base_of<AnimalInterface, T>::value>::type >
void listenToAnimal(T * animal) {
connect(animal, &T::madeSound, this, []{ qDebug() << "animal made sound"; });
}
C++03
template <class T>
void listenToAnimal(T * animal) {
Q_UNUSED(static_cast<AnimalInterface*>(animal));
connect(animal, &T::madeSound, this, &Widget::onAnimalMadeSound);
}
You can then use it without having to spell out the type - it's already known to the compiler:
listenToAnimal(dog_);
listenToAnimal(cat_);
If the derived type is not known at compile time, you have to dynamically cast to QObject and connect by name, not by method pointer. It will assert at runtime if you've passed in a wrong type - after all, it's not enough for it to be an instance of AnimalInterface, it also needs to be a QObject instance.
void listenToAnimal(AnimalInterface * animal) {
auto object = dynamic_cast<QObject*>(animal);
Q_ASSERT(object);
connect(object, SIGNAL(madeSound()), this, SLOT(onAnimalMadeSound()));
}
The fact that the type AnimalInterface has a virtual madeSound method is somewhat relevant - it guarantees that the derived class implements the method with such a signature. It doesn't guarantee that the method is a signal, though. So you should probably rethink your design and ask yourself: "What do I gain by using a static type system when I can't really use it for static type checking"?
Most likely you should make any methods that would nominally accept the AnimalInterface*, be parametrized and take a pointer to the concrete class. Modern code generators and linkers will deduplicate such code if type erasure leads to identical machine code.
Found a solution with templates. Did not work the first time I tried, obviously did something wrong first. Here it goes...
Just replace the corresponding parts from the example in the question (and remove definition of listenToAnimal from the source file):
header:
template<class T>
void listenToAnimal(AnimalInterface *animal)
{
T *animal_derivate = dynamic_cast<T*>(animal);
if (animal_derivate){
connect(animal_derivate, &T::madeSound,
this,
[](){
qDebug()<<"animal made sound";
});
}
}
cpp:
listenToAnimal<Dog>(dog_);
listenToAnimal<Cat>(cat_);
Update:
After trying Kuba Ober's answer, it seems like this is working best now:
template<typename T>
typename std::enable_if<std::is_base_of<AnimalInterface, T>::value,void>::type
listenToAnimal(T *animal)
{
connect(animal, &T::madeSound, this, [](){ qDebug()<<"animal made sound"; });
}
However, the one point still not working is how to connect if I create an animal like AnimalInterface *bird = new Bird, because it throws the same error that the base class does not have the signal.
Is it possible to use a QFile like a std::iostream? I'm quite sure there must be a wrapper out there. The question is where?
I have another libs, which requires a std::istream as input parameter, but in my program i only have a QFile at this point.
I came up with my own solution using the following code:
#include <ios>
#include <QIODevice>
class QStdStreamBuf : public std::streambuf
{
public:
QStdStreamBuf(QIODevice *dev) : std::streambuf(), m_dev(dev)
{
// Initialize get pointer. This should be zero so that underflow is called upon first read.
this->setg(0, 0, 0);
}
protected:
virtual std::streamsize xsgetn(std::streambuf::char_type *str, std::streamsize n)
{
return m_dev->read(str, n);
}
virtual std::streamsize xsputn(const std::streambuf::char_type *str, std::streamsize n)
{
return m_dev->write(str, n);
}
virtual std::streambuf::pos_type seekoff(std::streambuf::off_type off, std::ios_base::seekdir dir, std::ios_base::openmode /*__mode*/)
{
switch(dir)
{
case std::ios_base::beg:
break;
case std::ios_base::end:
off = m_dev->size() - off;
break;
case std::ios_base::cur:
off = m_dev->pos() + off;
break;
}
if(m_dev->seek(off))
return m_dev->pos();
else
return std::streambuf::pos_type(std::streambuf::off_type(-1));
}
virtual std::streambuf::pos_type seekpos(std::streambuf::pos_type off, std::ios_base::openmode /*__mode*/)
{
if(m_dev->seek(off))
return m_dev->pos();
else
return std::streambuf::pos_type(std::streambuf::off_type(-1));
}
virtual std::streambuf::int_type underflow()
{
// Read enough bytes to fill the buffer.
std::streamsize len = sgetn(m_inbuf, sizeof(m_inbuf)/sizeof(m_inbuf[0]));
// Since the input buffer content is now valid (or is new)
// the get pointer should be initialized (or reset).
setg(m_inbuf, m_inbuf, m_inbuf + len);
// If nothing was read, then the end is here.
if(len == 0)
return traits_type::eof();
// Return the first character.
return traits_type::not_eof(m_inbuf[0]);
}
private:
static const std::streamsize BUFFER_SIZE = 1024;
std::streambuf::char_type m_inbuf[BUFFER_SIZE];
QIODevice *m_dev;
};
class QStdIStream : public std::istream
{
public:
QStdIStream(QIODevice *dev) : std::istream(m_buf = new QStdStreamBuf(dev)) {}
virtual ~QStdIStream()
{
rdbuf(0);
delete m_buf;
}
private:
QStdStreamBuf * m_buf;
};
I works fine for reading local files. I haven't tested it for writing files. This code is surely not perfect but it works.
I came up with my own solution (which uses the same idea Stephen Chu suggested)
#include <iostream>
#include <fstream>
#include <cstdio>
#include <QtCore>
using namespace std;
void externalLibFunction(istream & input_stream) {
copy(istream_iterator<string>(input_stream),
istream_iterator<string>(),
ostream_iterator<string>(cout, " "));
}
ifstream QFileToifstream(QFile & file) {
Q_ASSERT(file.isReadable());
return ifstream(::_fdopen(file.handle(), "r"));
}
int main(int argc, char ** argv)
{
QFile file("a file");
file.open(QIODevice::WriteOnly);
file.write(QString("some string").toLatin1());
file.close();
file.open(QIODevice::ReadOnly);
std::ifstream ifs(QFileToifstream(file));
externalLibFunction(ifs);
}
Output:
some string
This code uses std::ifstream move constructor (C++x0 feature) specified in 27.9.1.7 basic_ifstream constructors section of Working Draft, Standard for Programming Language C++:
basic_ifstream(basic_ifstream&& rhs);
Effects: Move constructs from the
rvalue rhs. This is accomplished by
move constructing the base class, and
the contained basic_filebuf. Next
basic_istream::set_rdbuf(&sb) is called to install the contained
basic_filebuf.
See How to return an fstream (C++0x) for discussion on this subject.
If the QFile object you get is not open for read already, you can get filename from it and open an ifstream object.
If it's already open, you can get file handle/descriptor with handle() and go from there. There's no portable way of getting a fstream from platform handle. You will have to find a workaround for your platforms.
Here's a good guide for subclassing std::streambuf to provide a non-seekable read-only std::istream: https://stackoverflow.com/a/14086442/316578
Here is a simple class based on that approach which adapts a QFile into an std::streambuf which can then be wrapped in an std::istream.
#include <iostream>
#include <QFile>
constexpr qint64 ERROR = -1;
constexpr qint64 BUFFER_SIZE = 1024;
class QFileInputStreamBuffer final : public std::streambuf {
private:
QFile &m_file;
QByteArray m_buffer;
public:
explicit QFileInputStreamBuffer(QFile &file)
: m_file(file),
m_buffer(BUFFER_SIZE, Qt::Uninitialized) {
}
virtual int underflow() override {
if (atEndOfBuffer()) {
// try to get more data
const qint64 bytesReadIntoBuffer = m_file.read(m_buffer.data(), BUFFER_SIZE);
if (bytesReadIntoBuffer != ERROR) {
setg(m_buffer.data(), m_buffer.data(), m_buffer.data() + bytesReadIntoBuffer);
}
}
if (atEndOfBuffer()) {
// no more data available
return std::char_traits<char>::eof();
}
else {
return std::char_traits<char>::to_int_type(*gptr());
}
}
private:
bool atEndOfBuffer() const {
return gptr() == egptr();
}
};
If you want to be able to more things like seek, write, etc., then you'd need one of the other more complex solutions here which override more streambuf functions.
If you don't care much for performance you can always read everything from the file and dump it into an std::stringstream and then pass that to your library. (or the otherway, buffer everything to a stringstream and then write to a QFile)
Other than that, it doesn't look like the two can inter-operate. At any rate, Qt to STL inter operations are often a cause for obscure bugs and subtle inconsistencies if the version of STL that Qt was compiled with is different in any way from the version of STL you are using. This can happen for instance if you change the version of Visual Studio.