Pull to refresh

Comments 54

Даже и не знаю, что ответить :) Вообще изначально концепцию слотов в boost как раз позаимствовали из Qt.
Но в таком виде (compile-time соединения), по-моему, boost был первым, или я тут не прав? :)
Соединение все-таки по-моему происходит в runtime. Просто на этапе компиляции теперь можно проверить, осуществимо ли оно.

Насколько я помню, в Qt изначально была идея реализовать механизм сигналов/слотов через шаблоны, как это сделано сейчас в boost. Но от нее отказались, потому что не все компиляторы в то время хорошо поддерживали шаблоны (а для кроссплатформенной библиотеки это критично), и потому что не все, что можно сделать с помощью moc, можно выразить шаблонами.

Подход Qt — более мощный, ценой немного меньшей производительности. А вообще в официальной документации Qt как всегда самое лучшее и полное объяснение: http://qt-project.org/doc/qt-4.8/templates.html

P.S. Я тоже не знаю за что вас минусуют. Вопрос как вопрос.
Вы думаете в бусте этот способ от хорошей жизни? boost::signals даже сравнивать c Qt signals нельзя. boost::signals менее функциональны и очень медленны, судя по тестам доступным в интернетах.
А вы можете дать пруф по поводу быстродействия, потому как в ссылке комментарием выше, в официально документации написано: «Qt's signals and slots implementation is not as fast as a template-based solution.»
тут

А я и не писал, что Qt быстрее чем буст. Хотя это может быть так. Потомучто boost::signals плохо написаны, а не потомучто их подход медленнее.
Интересно, за что хоть минусуют? Мне правда интересно, кто первоистичник.
С замыканиями, увы, всё же действует правило в виде недопустимости использования переменных со стека функции, этакая ложка дёгтя так сказать.
В смысле, полноценного использования, как в языках типа шарпа.
Упорно смотрю на код и понимаю, что ничего не понимаю в том, как работают плюсовые замыкания.
Можно понимать их как невидимый класс с перегруженным operator() и конструктором, который делает то, что написано в []. В данном случае
struct xxx
{
QDialog* dlg;
xxx(QDialog* _dlg) : dlg(_dlg) {}
operator()(int result)
{
...
}
};

Поэтому тягать с помощью [=] со стека можно что угодно; если нужна мутабельность — копировать указатель и как-то решить вопрос с мусором, как и сделано в примере с помощью new и deleteLater.
Я просто почему-то считал, что плюсовое замыкание всегда хранится как указатель на функцию, из-за чего при просмотре кода выше начал стремительно делить на ноль, пытаясь понять где хранятся затянутые в него переменные после выхода из saveDocumentAs.
Простите, если я чего-то не понимаю, но как заменить такой код в новом синтаксисе?
void SomeClass::Foo(const char *slot) {
  ...
  QAction *action = new QAction(...);
  connect(action, SIGNAL(triggered()), this, slot);
  ...
}
Старый способ же никуда не денется…
void SomeClass::Foo(void (SomeClass::* slot)()) {
  ...
  QAction *action = new QAction(...);
  connect(action, &QAction::triggered, this, slot);
  ...
}


Так как в Qt я не разбираюсь, с параметры слота взяты наугад.
А что насчёт скорости? Есть ли выигрыш/проигрыш, или же старый и новый способы соединения примерно равны по скорости исполнения?
Новый метод будет явно быстрее, так как со строками ему работать не надо. Только вот откуда беспокойство, Вы сигналы сотнями в цикле подключаете?
Никто не может обещать, что кто-то возьмет ваш класс и создаст сотни экземпляров в цикле.
Сам connect в новом стиле будет немного быстрее (не нужен просмотр таблицы во время выполнения), а в вызове слотов вряд ли что-то изменится.
умоляю дайте пару ссылок на книжки Qt для желающего-но-тупого… из нечестно скачанного… всё сложно =(.
ей богу куплю книгу… очень хочу научится программировать с использованием Qt.
По моему скромному личному мнению, самая лучшая книжка — документация Qt :)
Позвольте провести аналогию.
Лучше Handbook для FreeBSD нет ничего, НО в книге дают не только готовые команды, но и «зачем… почему так… что было до ...». Никогда не пожалел, что в своё время купил книгу.
Образно говоря, документация — это больше справочник.
А хорошая книга — это «ммм, ах вот зачем это делать так» и хорошая книга учит правильным вещам сразу и даёт направление.
В офоках очень много разжёванных примеров и туториалов. Единственный минус (для многих) — неоконченный и неофициальный перевод на русский. Книги, как мне кажется, нужны уже после прохождения туториалов — для более глубокого понимания.
Аналогия тут неуместна :) Доки и примеры Qt намного лучше чем Handbook.
>… в книге дают не только готовые команды, но и «зачем… почему так… что было до ...»
Образно говоря, документация — это больше справочник.


Понятно: документацию Qt вы в глаза не видели :)
UFO just landed and posted this here
Документация и официальные примеры. В книгах вы ничего лучше все равно не найдете.
Когда-то давно, когда я почти не знал английского, но тоже хотел изучить Qt мне помог сайт doc.crossplatform.ru/qt/4.3.2/how-to-learn-qt.html
И в частности doc.crossplatform.ru/qt/4.3.2/tutorial.html
Да, информация слегка устаревшая, но общее представление дает.
После этого можно запускать-читать примеры из Qt Examples и изучать по готовому коду.
Я, когда-то тоже искал книгу по Qt. В результате что-то нашел, прочитал первые главы и плюнул. Документация тут решает, в отличии от многих других библиотек.
Классика: Бланшет/Саммерфилд. Предпочтительнее читать на английском (если нет серьёзных проблем с языком), там всё очень просто и подробно разжёвано.
Наконец-то. Сколько проблем с connect возникало хотя бы из-за неймспейсов — везде приходилось писать идентификатор одинаково…
Можно ли будет писать так:
class Test : public QObject
{ Q_OBJECT
public:
    Test() {
        connect(this, &Test::someSignal, this, &Test::someSlot);
    }
signals:
    void someSignal(const QString &);
public:
    void someSlot();
};

Всегда не хватало возможности соединять сигналы-слоты с разными списками аргументов. В примере выше хочется просто вызова без передачи чего-либо.
Думаю, что нет. Не вижу способа посчитать количество аргументов переданной функции.
Вообще-то это всегда было возможно :) Смотрите документацию.

Нельзя только чтобы в слоте было меньше аргументов чем в сигнале. Поэтому иногда приходилось использовать QSignalMapper или свои велосипеды. Теперь с C++11 замыкания существенно упростят нам жизнь!
0 >= 1? Или нулевое количество — исключение?
Ой, это опечатка у меня, конечно. Больше, а не меньше.

В вашем примере у слота 0 аргументов, а у сигнала 1. Этот аргумент будет проигнорирован, тут ничего страшного.

Вот вам рабочий пример из документации:

connect(sender, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed()));
Так как показано в вашем примере, всегда было можно соединять. Наоборот нельзя, потому что если слот что-то принимает, то ему это, очевидно, надо дать. Ну и соответственно нельзя соединять сигналы и слоты с разными типами параметров, что на мой взгляд очевидно.
Ну, в новом синтаксисе можно будет соединять сигналы и слоты с разными типами параметров, если параметры сигнала имеют возможность неявного приведения к типам параметров слота.
ну неявное преобразование это само собой разумеется, это возможности языка а не фреймворка, я говорю о не совместимых типах.
Окей, а как насчет аргументов с дефолтными значениями? Можно ли давать меньше аргументов слоту, в таком случае?
>Всегда не хватало возможности соединять сигналы-слоты с разными списками аргументов.

Уж не знаю, почему вам её «всегда не хватало», но она в Qt всегда была :) Главное, чтобы количество аргументов слота было меньше либо равно количеству аргументов сигнала. Т.е. чтобы не оставалось «висячих» аргументов в слоте.
Избыточная информация — можно, недостаточная — нельзя.
UFO just landed and posted this here
Спасибо за статью.

Просто на всякий случай напомню: у метода connect есть еще и пятый аргумент (спасибо Нокии Троллтеку за это — он такой клевый!) — Qt::ConnectionType. С его использованием механизм сигналов/слотов играет еще более яркими красками!
На самом деле, не всё с новым синтаксисом сигналов и слотов так хорошо. В статье на Qt-project есть такой вот адский пример:

QTcpSocket *socket = new QTcpSocket;
... 
QObject::connect(socket, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error), [socket] (QAbstractSocket::SocketError) {
    qDebug()<< "ERROR " << socket->errorString();
    socket->deleteLater();
});


Комментарий к примеру оттуда же:
As you might see in the example, connecting to QAbstractSocket::error is not really beautiful since error has an overload, and taking the address of an overloaded function requires explicit casting.

Some macro could help (with c++0x or typeof extensions)

The best thing is probably to recommend not to overload signals or slots …

… but we have been adding overloads in past minor releases of Qt because taking the address of a function was not a use case we support. But now this would be impossible without breaking the source compatibility.

Не знаю, насколько проблема актуальна на данный момент, но случай вполне себе из реальной жизни и использование нового синтаксиса превращает код в тотальный треш, угар и содомию. Хорошо, что это хоть чуть-чуть компенсируется возможностью использовать лямбды в качестве слотов.
Some macro could help

Я только что попробовал, и таки да, при помощи несложных манипуляций с препроцессорными макросами и новыми штуками C++ (я использовал decltype, но должно сработать и с нестандартным typeof) можно делать красиво. Правда, я трогал не Qt5, а просто написал сферический тест в вакууме, но в С++ принципиально возможно вместо
connect(testObject1, &TestClass1::someSignal1, testObject2, &TestClass2::someSlot1);
писать
connect(testObject1, someSignal1, testObject2, someSlot1);
с сохранением проверки на этапе компиляции и прочих плюшек.
Ну так значимый фрагмент сферического теста в студию! :)
Это заклинание чинит decltype в текущей версии gcc.
template<typename T>
struct decltype_t
{
    typedef T type;
};

#define DECLTYPE(expr) decltype_t<decltype(expr)>::type

А это — кошки, на которых я тренировался:
template <class T>
void _invoke(T & obj, void (T::*method)())
{
    (obj.*method)();
}

#define invoke(obj, method) (_invoke((obj), &DECLTYPE(obj)::method))

class TestClass
{
public:
    void someSlot() { cout << "TestClass::someSlot invoked" << endl; }
};

int main()
{
    TestClass test;
    _invoke(test, &TestClass::someSlot);
    invoke(test, someSlot);
    return 0;
}
Последний пример приятно напомнил jQuery. Частенько подобного не хватало.
Sign up to leave a comment.

Articles