Qt WebAssembly and inheritance of QVector - c++

I'm trying to us Qt for WebAssembly. I'm using QVector, but I want to add some convenient finder methods. So I've tried subclassing like the code below. The compiler complains that vec.begin() and vec.end() do not exist. If I change vec from a Foo_Vector to a QVector (no subclassing), it works.
Other methods from QVector also become undefined when using the subclass version -- such as size.
I tried defining a method on Foo_Vector:
size_t size() const { return QVector<Foo>::size(); }
That produces an error about non-static methods. So I tried to do this:
QVector<Foo> * ptr = static_cast<QVector<Foo>>(this);
And that produces an error that there's no conversion from a Foo_Vector * to a QVector *.
Does anyone have an explanation? Do I have to do this through composition instead and implement passthrough methods for anything I want to do?
#include <QCoreApplication>
#include <QVector>
class Foo {
public:
int a;
int b;
};
class Foo_Vector: public QVector<Foo> {
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Foo_Vector vec;
for (auto it = vec.begin(); it != vec.end(); ++it) {
}
return a.exec();
}

Related

Why am I getting weird crashes with Qt5 depending on how I create the application instance?

Here is a reduced version of my code:
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>
class MainView : public QMainWindow
{
public:
static MainView *Initialise(int argc, char **argv);
int Run() { return qApp->exec(); }
private:
MainView() { }
virtual ~MainView() { }
};
namespace
{
QApplication *s_app {nullptr};
MainView *s_instance {nullptr};
};
MainView *MainView::Initialise(int argc, char **argv)
{
if (nullptr == s_app) {
s_app = new QApplication(argc, argv);
}
if (nullptr == s_instance) {
s_instance = dynamic_cast<MainView *>(new MainView());
}
return s_instance;
}
class Framework
{
public:
Framework() { }
~Framework() { }
int Initialise(int argc, char **argv)
{
m_main_view = MainView::Initialise(argc, argv);
return 0;
}
int Run()
{
m_main_view->show();
return m_main_view->Run();
}
private:
MainView *m_main_view;
};
int main(int argc, char **argv)
{
Framework framework;
int result = 0;
// Weirdness here!!!!
MainView::Initialise(argc, argv); // Crashes even with this line.
result = framework.Initialise(argc, argv);
if (0 == result) {
result = framework.Run();
}
return result;
}
This code crashes when showing the MainWindow instance. In my full version of this code, where MainView inherits from a virtual interface class (so as to allow me to decouple the view and substitute a command line interface or a test interface) it doesn't crash unless I comment out the code marked as "weirdness here".
In this code, if I replace:
MainView::Initialise(argc, argv); // Crashes even with this line.
with:
if (nullptr == s_app) {
s_app = new QApplication(argc, argv);
}
then it works fine and doesn't crash.
Essentially if the application is instantiated within MainView::Initialise(), whether called from main() or from Framework::Initialise() then the program crashes, but if I initialise the application in main() first then it doesn't crash.
I don't want my main() function to have any knowledge of the UI at all, if possible, even taking my virtual base class interface into account. There should be no need and it makes main() messier than necessary.
So why is this crashing and how do I stop it?
There's a subtle difference between your invocation of the QApplication constructor in main() and the invocation in MainView::Initialise().
The documentation for the QApplication constructor says:
QApplication::QApplication(int &argc, char **argv)
< ... >
Note: argc and argv might be changed as Qt removes command line arguments that it recognizes.
So when invoking the constructor in MainView::Initialise(), you are actually passing a copy of the argc that came from main. Presumably, Qt stores a reference to this internally somewhere, causing it to crash when the QApplication later dereferences it during the construction of the window.
If you change the signature of both Initialise() functions to take a reference to argc, the program works without crashing.

RAII char ** to be used as exec argument vector

I'm currently creating an app to launch external apps.
The signature to launch the external apps is:
int launchApp(int argc, char** argv); // argc = amount of arguments, argv = arguments
To add arguments to a std::vector<char *> structure I use the following lambda:
auto addArgument = [](std::vector<char *> & lArguments,
const std::string & sArgument)
{
auto cstr = new char[sArgument.size() + 1];
std::copy(sArgument.cbegin(), sArgument.cend(), cstr);
cstr[sArgument.size()] = '\0';
lArguments.push_back(cstr);
};
And launching an external app:
std::vector<char *> lArguments;
addArgument(lArguments, "Argument 1");
addArgument(lArguments, "Argument 2");
launchApp(lArguments.size(),static_cast<char**>(lArguments.data());
//... Clean up arguments
How would I do this in a RAII manner instead?
I was thinking of using a std::vector<std::vector<char>> instead. However, how can I then pass the underlying raw data (char**) to launchApp()? static_cast<char**>(lArguments.data()) wouldn't work...
I usually do this in two parts:
Build a std::vector<std::string> containing the actual arguments.
Build a std::vector<const char*> where each element is the .data() of the corresponding element in the vector of strings.
The first vector, and the strings contained within it, handle your allocation, resizing, etc. while the second simply acts as an index into the memory that is being managed by the first. The lifetime of your second vector should be shorter than that of the first, which you can guarantee by wrapping both in a class.
Example:
#include <string>
#include <vector>
#include <unistd.h>
int main() {
std::vector<std::string> args = {"echo", "hello", "world"};
std::vector<const char*> argv;
argv.reserve(args.size());
for (auto& arg : args) {
argv.push_back(arg.data());
}
execvp("echo", const_cast<char**>(argv.data()));
}
(Note the ugly const_cast. Depending on how you look at it, this is either because the exec* family of functions don't follow const correctness, or because std::string::data() does not have a non-const version (prior to C++17)).
Or, with the class to wrap it all up nicely:
#include <string>
#include <vector>
#include <unistd.h>
class ExecArguments {
public:
ExecArguments(std::initializer_list<std::string> arguments)
: args(arguments) {
for (auto& arg : args) {
argv.push_back(const_cast<char*>(arg.data()));
}
}
char** data() {
return const_cast<char**>(argv.data());
}
private:
std::vector<std::string> args;
std::vector<char*> argv;
};
int main() {
ExecArguments args{"echo", "hello", "world"};
execvp(args.data()[0], args.data());
}
Collect your parameters as regular strings.
Use an inner class that:
provides a transparent implicit view to a raw array of pointers, and
takes care of managing this pointer array at the same time.
An instance of this inner class is returned by some access method. Its lifetime spans the invoking statement. See below for an example:
#include <iostream>
#include <vector>
class CollectArgs : public std::vector<std::string> {
// just for providing the raw view and taking care of its memory
class RawArgs {
std::vector<char const*> m_argv;
public:
RawArgs(std::vector<char const*> argv) {
std::swap(argv, m_argv);
}
~RawArgs() {}
public:
operator char const* const*() const { return m_argv.data(); }
};
public:
RawArgs raw_args() const {
std::vector<char const*> argv;
for(std::string const &arg : *this) argv.push_back(arg.c_str());
return argv;
}
};
// the application launcher
void call(unsigned argc, char const *const *argv) {
for(unsigned i = 0; i < argc; i++) {
std::cout << argv[i] << std::endl;
}
}
int main() {
CollectArgs args;
args.push_back("Arg1");
args.push_back("Arg2");
// create the raw view and have it destroyed immediately after this statement
call(args.size(), args.raw_args());
}

Qt C++ connect signal with non-void signature to lambda

I want to connect a signal with non-void signature to a lambda function.
My code looks like the following
QTimeLine *a = new QTimeLine(DURATION, this);
connect(a, &QTimeLine::valueChanged, [a,this](qreal r) mutable { this->setMaximumHeight(r);});
in a way similar to the SIGNAL-SLOT approach:
connect(a, SIGNAL(valueChanged(qreal),this,SLOT(doStuff(qreal)));
My connect-to-lambda compiles, but it won't change this->height().
What did I get wrong? How should I write the lambda so that it takes the qreal from valueChanged?
I read the related documentation, but I couldn't find useful examples.
****EDIT****
In fact it works, I was getting the QTimeLine settings wrong. And yes, I don't need to capture a.
I was trying to animate a custom insertRow() method of a QTableWidget.
I also made the lambda change the height of the table row instead of the contained widget's. For reference, here's the working snippet:
QTimeLine *a = new QTimeLine(DURATION,this);
connect(a,&QTimeLine::valueChanged,[this](qreal r) mutable {
this->list->setRowHeight(0,r * ROW::HEIGHT);
});
a->start();
Thanks a lot for the quick replies anyway.
Should just work. Here is a complete SSCCE that demonstrates it working. Check what you are doing different in principles.
main.cpp
#include <QTimeLine>
#include <QObject>
#include <QDebug>
#include <QCoreApplication>
class Foo
{
void setMaximumHeight(int h) {height = h; qDebug() << "Height:" << height;}
public:
void doStuff() { QObject::connect(&timeLine, &QTimeLine::valueChanged, [this](qreal r) mutable { setMaximumHeight(r);}); timeLine.start(); }
int maximumHeight() const { return height; }
int height{0};
int DURATION{100};
QTimeLine timeLine{DURATION};
};
int main(int argc, char **argv)
{
QCoreApplication application(argc, argv);
Foo foo;
foo.doStuff();
return application.exec();
}
main.pro
TEMPLATE = app
TARGET = main
QT = core
CONFIG += c++11
SOURCES += main.cpp
Build and Run
qmake && make && ./main

Using QHash in a custom class

I am newbie in Qt and learning to handle how QHash works. On working with this example I dont understand why this throws me an error. I may miss something, but please guide me to learn this.
main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QHash<QString,Person> hash;
QString key="1";
Person p;
p.name = name;
p.number = an;
hash.insert(key,p);
return a.exec();
}
person.h
class Person
{
public:
Person();
Person(QString name,QString num);
bool operator==(const Person & other) const; //== overloading to assign in QHash
QString name,number;
};
person.cpp
Person::Person()
{
}
Person::Person(QString name, QString num)
{
this->name=name;
this->number=num;
}
bool Person::operator==(const Person & other) const
{
bool state;
if (name == other.name )
state = true;
else
state = false;
return state;
}
and the error is:-'qHash':none of the 17 overloads could convert all the argument type.I knw im missing something.Please guide me.
You need the global qHash() function.
A QHash's key type has additional requirements other than being an
assignable data type: it must provide operator==(), and there must
also be a qHash() function in the type's namespace that returns a hash
value for an argument of the key's type.
See this for more info about that.
The code you've provided is not complete and not compilable - e.g. The symbol name is not found, no includes.
The op == is not required when you use the class as value. The only one requirement for value-classes - a default constructor.
I think you problem was something else. If you provided the complete code and the complete list of errors I could tell you what exactly.
Below you'll find the working version of you code-snippet. (For simplicity, everything in one file)
main.cpp
#include <QApplication>
#include <QHash>
#include <QString>
class Person
{
public:
Person(QString name=QString::null,QString num=QString::null);
QString name,number;
};
Person::Person(QString _name, QString _num)
:name(_name),number(_num)
{
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QHash<QString,Person> hash;
QString key="1";
Person p("Orhan Kemal", "1914");
hash.insert(key,p);
return a.exec();
}
Regards,
Valentin
EDIT:
Initialized members not in body

user's arguments are empty with QCoreApplication in mysterious cases

I'm trying to create a console application with Qt and been facing really strange behavior when attempting to retrieve the arguments. My class is derived from QCoreApplication which has a function that should normally put all the args in some list of strings. But in some cases that call ends in a segmentation fault.
Here's the code:
main.cpp
#include "Diagramm.h"
int main(int argc, char *argv[])
{
Diagramm application(argc, argv);
application.run();
return EXIT_SUCCESS;
}
Diagramm.h
#include <QCoreApplication>
#include <iostream>
#include <QStringList>
#include <QFile>
#include <QDebug>
class Diagramm : public QCoreApplication
{
Q_OBJECT
public:
Diagramm(int argc, char *argv[]);
void run();
private:
void testArguments();
signals:
public slots:
};
Diagramm.cpp
#include "Diagramm.h"
Diagramm::Diagramm(int argc, char *argv[]) : QCoreApplication(argc, argv)
{
//std::cout << "calling Diagramm constructor" << std::endl;
}
void Diagramm::run()
{
testArguments();
}
void Diagramm::testArguments()
{
//get source and target files from arguments
QStringList arguments = this->arguments();
if(arguments.count() < 2)
{
std::cout << "Missing arguments" << std::endl;
return exit(1);
}
}
When compiling and executing the code above, everything works fine, BUT when I uncomment the line in Diagramm's constructor I've got a segmentation fault on the first line of function testArguments (the call to arguments())
I've been on that for hours, reading Qt's doc, forums... Does anyone know where that can come from? Any idea would be greatly appreciated.
Note : I'm not calling the exec function on purpose because I don't need any event loop.
Q(Core)Application wants argc and argv by reference, so your constructor should read
Diagramm(int& argc, char **argv[])
If you don't to this, it may work in some cases and lead to segfaults or strange behavior in others, as you encountered. Seems to be a common error and isn't easy to spot when reading the documentation.
arguments() is a static function so the line should be:
QStringList arguments = QCoreApplication::arguments();