我一直在尝试找出如何在我的成员函数之间传递QStringList
指针。由于某种原因,我收到一条错误消息,内容如下:
mainwindow.cpp:497:错误:在静态成员函数中无效使用成员'MainWindow :: emails'
// global definition
QStringList* MainWindow::emails;
mainwindow.h
class MainWindow : public QMainWindow{
...
private:
static QStringList * emails;
}
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
size_t MainWindow::WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
{
QString filteredNewLine = plainText.replace("\n"," ");
QRegularExpression re("[a-z0-9]+[_a-z0-9.-]*[a-z0-9]+@[a-z0-9-]+(.[a-z0-9-]+)");
QRegularExpressionMatchIterator i = re.globalMatch(filteredNewLine);
QStringList words;
while (i.hasNext()) {
QRegularExpressionMatch match = i.next();
QString word = match.captured(0);
words << word;
MainWindow::emails = &words;
qDebug() << MainWindow::emails
}
}
MainWindow::~MainWindow()
{
delete emails;
}
答案 0 :(得分:0)
您似乎在项目中使用curl。有一个C ++绑定可以卷曲,但是我们可以使用Qt。
首先,让我们将全局初始化和清理包装在RAII包装器中:
// https://github.com/KubaO/stackoverflown/tree/master/questions/curl-50975613
#include <QtWidgets>
#include <curl/curl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <cstdio>
#include <limits>
class CurlGlobal {
Q_DISABLE_COPY(CurlGlobal)
CURLcode rc;
public:
CurlGlobal() { rc = curl_global_init(CURL_GLOBAL_ALL); }
~CurlGlobal() { curl_global_cleanup(); }
CURLcode code() const { return rc; }
explicit operator bool() const { return rc == CURLE_OK; }
};
我们还需要在curl_slist
周围使用RAII包装:
class CurlStringList {
struct curl_slist *list = {};
public:
CurlStringList() = default;
explicit CurlStringList(const QStringList &strings) {
for (auto &s : strings)
list = curl_slist_append(list, s.toLatin1().constData());
}
CurlStringList &operator=(CurlStringList &&o) {
std::swap(o.list, list);
return *this;
}
~CurlStringList() {
curl_slist_free_all(list);
}
struct curl_slist *curl() const { return list; }
};
“简单”句柄CURL*
可以包装在QObject
中,以利用其灵活性。这是我们看到如何使用CURLOPT_xxxDATA
正确实现回调,而没有任何全局/静态变量的地方:
class CurlEasy : public QObject {
Q_OBJECT
friend class CurlMulti;
CURL *const d = curl_easy_init();
QAtomicInteger<bool> inMulti = false;
CURLcode rc = d ? CURLE_OK : (CURLcode)-1;
char err[CURL_ERROR_SIZE];
CurlStringList headers;
bool addToMulti();
void removeFromMulti();
static size_t write_callback(const char *ptr, size_t size, size_t nmemb, void *data) {
Q_ASSERT(data);
return static_cast<CurlEasy*>(data)->writeCallback(ptr, size * nmemb);
}
protected:
virtual qint64 writeCallback(const char *, qint64) {
return -1;
}
void setWriteCallback() {
curl_easy_setopt(d, CURLOPT_WRITEDATA, (void*)this);
curl_easy_setopt(d, CURLOPT_WRITEFUNCTION, (void*)write_callback);
}
virtual void clear() {}
在派生类中使用clear
方法在请求开始之前准备状态。
其余方法包装并公开了各种选项和错误处理:
public:
CurlEasy(QObject *parent = {}) : QObject(parent)
{
curl_easy_setopt(d, CURLOPT_ERRORBUFFER, err);
}
~CurlEasy() override {
removeFromMulti();
curl_easy_cleanup(d);
}
CURL *c() const { return d; }
operator CURL*() const { return d; }
bool ok() const { return rc == CURLE_OK; }
QString errorString() const { return QString::fromUtf8(err); }
bool setUrl(const QUrl& url) {
rc = curl_easy_setopt(d, CURLOPT_URL, url.toEncoded().constData());
return ok();
}
void setMaxRedirects(int count) {
curl_easy_setopt(d, CURLOPT_FOLLOWLOCATION, count ? 1L : 0L);
curl_easy_setopt(d, CURLOPT_MAXREDIRS, (long)count);
}
void setVerbose(bool v) {
curl_easy_setopt(d, CURLOPT_VERBOSE, v ? 1L : 0L);
}
bool get() {
if (inMulti.loadAcquire())
return false;
clear();
curl_easy_setopt(d, CURLOPT_HTTPGET, 1L);
if (addToMulti())
return true;
rc = curl_easy_perform(d);
if (!ok())
qDebug() << errorString();
return ok();
}
void setHeaders(const QStringList &h) {
headers = CurlStringList(h);
curl_easy_setopt(d, CURLOPT_HTTPHEADER, headers.curl());
}
void setWriteTo(FILE *);
void setReadFrom(FILE *);
void setWriteTo(QIODevice *dev);
void setReadFrom(QIODevice *dev);
};
可以跳过setWriteTo
和setReadFrom
方法的详细信息:它们用于使CurlEasy
对象与C文件和Qt I / O设备互操作。有关完整代码,请参见存储库。
现在,我们需要一个行解析器:使用writeCallback
解码单个行的类:
class CurlLineParser : public CurlEasy {
Q_OBJECT
QByteArray m_buf;
protected:
qint64 writeCallback(const char *ptr, qint64 count) override {
const char *start = ptr;
const char *const end = ptr + count;
for (; ptr != end; ptr++) {
if (*ptr == '\n') {
if (!m_buf.isEmpty())
m_buf.append(start, ptr-start);
else
m_buf = QByteArray::fromRawData(start, ptr-start);
emit hasLine(QString::fromUtf8(m_buf));
m_buf.clear();
start = ptr + 1;
}
}
// keep partial line
m_buf = QByteArray::fromRawData(start, ptr-start);
return count;
}
void clear() override {
m_buf.clear();
}
public:
CurlLineParser(QObject *parent = {}) : CurlEasy(parent) {
setWriteCallback();
}
Q_SIGNAL void hasLine(const QString &);
};
可以通过监听hasLine
信号来实现特定于应用程序的行过滤。
这时,我们拥有运行同步(阻止)请求所需的一切。让我们暂时推迟CurlMulti
类的检查,并查看用户界面/演示的外观:
class Ui : public QWidget {
Q_OBJECT
CurlLineParser m_curl{this};
QRegularExpression m_re{"[a-z0-9]+[_a-z0-9.-]*[a-z0-9]+@[a-z0-9-]+(.[a-z0-9-]+)"};
QStringList m_emails;
QGridLayout m_layout{this};
QPlainTextEdit m_view;
QLineEdit m_url;
QCheckBox m_async{"Async"};
QPushButton m_get{"Get"};
public:
Ui() {
m_url.setPlaceholderText("Url");
m_view.setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
m_url.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
m_layout.addWidget(&m_view, 0, 0, 1, 3);
m_layout.addWidget(&m_url, 1, 0);
m_layout.addWidget(&m_async, 1, 1);
m_layout.addWidget(&m_get, 1, 2);
connect(&m_get, &QPushButton::clicked, this, [this]{
m_emails.clear();
emit requestGet(m_url.text());
});
connect(&m_async, &QCheckBox::toggled, this, &Ui::requestAsync);
}
Q_SIGNAL void requestGet(const QString &);
Q_SIGNAL void requestAsync(bool);
void setUrl(const QString &url) { m_url.setText(url); }
void setAsync(bool async) { m_async.setChecked(async); }
void processLine(const QString &line) {
auto it = m_re.globalMatch(line);
while (it.hasNext()) {
auto email = it.next().captured(0);
m_emails.push_back(email);
m_view.appendPlainText(email);
}
}
};
Ui
的方法可以处理任何来源的行,并使用正则表达式对其进行过滤。该类与卷发机的细节完全脱节。
curl“堆栈”和Ui之间的连接是通过main
完成的:
int main(int argc, char* argv[]) {
CurlGlobal curlGlobal;
QApplication app(argc, argv);
QThread netThread;
CurlMulti multi;
CurlLineParser curl;
multi.moveToThread(&netThread);
Ui ui;
QObject::connect(&ui, &Ui::requestGet, [&](const QString &url){
curl.setUrl(url);
curl.get();
});
QObject::connect(&ui, &Ui::requestAsync, [&](bool async){
QMetaObject::invokeMethod(&curl, [&curl, &multi, async]{
if (async) curl.moveToThread(multi.thread());
curl.setParent(async ? &multi : nullptr);
if (!async) curl.moveToThread(qApp->thread());
});
});
QObject::connect(&curl, &CurlLineParser::hasLine, &ui, &Ui::processLine);
curl.setMaxRedirects(2);
curl.setVerbose(false);
curl.setHeaders({"Accept: text/plain; charset=utf-8"});
ui.setUrl("https://gist.github.com/retronym/f9419787089149ad3a59835c2e1ab81a/"
"raw/8cd88ab3645508ae1243f3aa4ec7c012af4fae7b/emails.txt");
ui.show();
netThread.start();
int rc = app.exec();
QMetaObject::invokeMethod(&multi, [&]{
if (!curl.parent())
curl.moveToThread(app.thread());
multi.moveToThread(app.thread());
netThread.quit();
});
netThread.wait();
return rc;
}
#include "main.moc"
选择异步选项后,CurlLineParser
对象将移至工作线程,并成为CurlMulti
对象的子代。线程争用仅是必要的,因为可以在阻塞,同线程实现和异步,独立线程实现之间进行配置翻转。当然,异步,同线程配置也是可能的-只需删除所有moveToThread
调用即可。
CurlMulti
环绕CURLM*
,并使用Qt的套接字通知程序将curl与事件循环接口。每个套接字的通知程序使用curl_multi_assign
存储为每个套接字的指针。
class CurlMulti : public QObject {
Q_OBJECT
friend class CurlEasy;
struct Notifiers {
QScopedPointer<QSocketNotifier> read, write, exception;
};
CURLM *m = curl_multi_init();
QBasicTimer m_timer;
void timerEvent(QTimerEvent *ev) override {
int running;
if (ev->timerId() == m_timer.timerId()) {
m_timer.stop();
curl_multi_socket_action(m, CURL_SOCKET_TIMEOUT, 0, &running);
processInfo();
}
}
static int socket_callback(CURL *, curl_socket_t s, int what, void *userp, void *socketp) {
Q_ASSERT(userp);
return static_cast<CurlMulti*>(userp)->
socketCallback(s, what, static_cast<Notifiers*>(socketp));
}
static int timer_callback(CURLM *, long ms, void *userp) {
Q_ASSERT(userp);
auto *q = static_cast<CurlMulti*>(userp);
if (ms == -1)
q->m_timer.stop();
else if (ms >= 0)
q->m_timer.start(ms, q);
return 0;
}
int socketCallback(curl_socket_t s, int what, Notifiers* n) {
if (what == CURL_POLL_REMOVE) {
delete n;
curl_multi_assign(m, s, nullptr);
processInfo();
return 0;
}
if (!n) {
n = new Notifiers;
curl_multi_assign(m, s, n);
n->exception.reset(new QSocketNotifier(s, QSocketNotifier::Exception, this));
connect(&*n->exception, &QSocketNotifier::activated, [this, s]{
int running;
curl_multi_socket_action(m, s, CURL_CSELECT_ERR, &running);
});
}
if ((what & CURL_POLL_IN) && !n->read) {
n->read.reset(new QSocketNotifier(s, QSocketNotifier::Read, this));
connect(&*n->read, &QSocketNotifier::activated, [this, s]{
int running;
curl_multi_socket_action(m, s, CURL_CSELECT_IN, &running);
});
}
if ((what & CURL_POLL_OUT) && !n->write) {
n->write.reset(new QSocketNotifier(s, QSocketNotifier::Write, this));
connect(&*n->write, &QSocketNotifier::activated, [this, s]{
int running;
curl_multi_socket_action(m, s, CURL_CSELECT_OUT, &running);
});
}
n->exception->setEnabled(what & CURL_POLL_INOUT);
if (n->read)
n->read->setEnabled(what & CURL_POLL_IN);
if (n->write)
n->write->setEnabled(what & CURL_POLL_OUT);
processInfo();
return 0;
}
void processInfo() {
int msgq;
while (const auto *info = curl_multi_info_read(m, &msgq)) {
if (info->msg == CURLMSG_DONE)
for (auto *c : children())
if (auto *b = qobject_cast<CurlEasy*>(c))
if (b->d == info->easy_handle && b->inMulti)
b->removeFromMulti();
}
}
public:
CurlMulti(QObject *parent = {}) : QObject(parent) {
curl_multi_setopt(m, CURLMOPT_SOCKETDATA, (void*)this);
curl_multi_setopt(m, CURLMOPT_SOCKETFUNCTION, (void*)socket_callback);
curl_multi_setopt(m, CURLMOPT_TIMERDATA, (void*)this);
curl_multi_setopt(m, CURLMOPT_TIMERFUNCTION, (void*)timer_callback);
int running;
curl_multi_socket_action(m, CURL_SOCKET_TIMEOUT, 0, &running);
}
Q_SIGNAL void finished(CurlEasy *);
~CurlMulti() override {
for (auto *c : children())
if (auto *b = qobject_cast<CurlEasy*>(c))
b->removeFromMulti();
curl_multi_cleanup(m);
}
};
最后,CurlEasy
的两个单独定义的内部方法处理其从multi
对象的添加和删除:
bool CurlEasy::addToMulti() {
auto *m = qobject_cast<CurlMulti*>(parent());
if (d && m && inMulti.testAndSetOrdered(false, true)) {
QMetaObject::invokeMethod(m, [m, this]{
curl_multi_add_handle(m->m, d);
});
}
return inMulti;
}
void CurlEasy::removeFromMulti() {
auto *m = qobject_cast<CurlMulti*>(parent());
if (d && m && inMulti.testAndSetOrdered(true, false)) {
curl_multi_remove_handle(m->m, d);
emit m->finished(this);
}
}