错误:在静态成员函数中无效使用成员XXX

时间:2018-06-21 18:46:38

标签: c++ qt c++11 pointers

我一直在尝试找出如何在我的成员函数之间传递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;
}

1 个答案:

答案 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);
};

可以跳过setWriteTosetReadFrom方法的详细信息:它们用于使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);
   }
}

另请参见: