Снова о Gtkmm и Qt

On Gtkmm and Qt (again)
Автор: Guillaume Laurent
Перевод: AnDi Peredri

И давайте надеяться, в последний раз.

В этом интервью Murray Cumming, в настоящее время являющийся разработчиком библиотеки Gtkmm ( ранее известной как Gtk-- ), рассказывает о причинах моего ухода из проекта Gtkmm и о преимуществах Gtkmm в сравнении с Qt. Так как я в значительной степени не согласен с тем, что он говорит, считаю необходимым ответить.

Почему я оставил Gtkmm

Murray : (...) Я предполагаю, что у Guillaume в действительности никогда не было полного доступа к коду Gtkmm, что могло его обескураживать, и он воспользовался удобным случаем отказаться. (...)

Я объяснил, почему я оставил Gtkmm, и "удобного случая отказаться" не было среди причин. Хотя в одном Вы почти правы. Я действительно никогда не имел полного доступа к ядру Gtkmm, потому что оно было прерогативой Карла ( Karl Nelson ). Для работы над Gtkmm у меня было значительно меньше времени, чем у Карла, но прежде всего, я не был мотивирован. Моей целью было создание инструментария, который мы могли бы использовать для Rosegarden. Таким образом, моя основная работа над Gtkmm заключалась в обертывании виджетов.

Gtkmm и Inti

Murray : (...) На мой взгляд, Guillaume считал, что благодаря поддержке RedHat проект Inti преуспеет, а Gtkmm постепенно отойдет на второй план.

Да, я считал, что Inti преуспеет, и, очевидно, ошибался. Я полагал, что если разработчик сможет отдавать проекту все свое время, то проект станет лучшим. Но этого не произошло, так как Havoc ( Havoc Pennington, первый разработчик Inti - прим.пер.) в основном работал над Gtk+ 2.

Murray : (...) Большинство пользователей и разработчиков Gtkmm категорически не согласились с решениями дизайна Inti.

Насколько я знаю, "большинство" - это около 5-7 человек, судя из дискуссии, которую можно найти в архивах. Одним из них был Карл, с точкой зрения которого я до сих пор полностью не согласен. Мне нужен был не "хакерский инструментарий", а инструментарий, который бы работал и был бы удобным.

Совместная разработка

Murray : (...) Inti прекратил свое существование по той причине, что в его разработку не было вовлечено сообщество хакеров. (...) В принципе, Qt не является результатом совместной разработки, поэтому количество ошибок в ней не выдерживает никакой критики. (...) Наиболее важным является то, что при обнаружении проблем с Gtkmm, Вы можете обсудить их с разработчиками или прислать заплату.

Как я уже сказал, Inti прекратил свое существование по той причине, что его разработчик приостановил над ним работу. Что касается библиотеки Qt, она действительно не разрабатывалась сообществом, и я считаю, что это хорошо.

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

Согласитесь, хороших программистов немало даже в мире свободного ПО. Но лишь немногие реально оценивают свою фактическую квалификацию. Поэтому вовлечение множества людей в решение трудных проблем не помогает, а наоборот вредит.

Дизайн Gtkmm был совместно подробно обсужден, но все же кое-что осталось недоработанным ( например, управление памятью ). Главным образом из-за того, что фактически ни один из разработчиков, вовлеченных в обсуждение, не использовал Gtkmm для создания реальных приложений. Нами больше овладевала идея создания "настоящего API C++", нежели чего-либо удобного в использовании. Ранее приведенная ссылка на сообщение Карла является лучшим тому примером. На этой странице веб-журнала Мэтью Томаса ( Matthew Thomas ) представлен материал, который тщательно обсуждался на многих сайтах. В нем говорится о дизайне пользовательского интерфейса, но большинство моментов подходят также к дизайну API. И для пользователей, и для программистов проблема дизайна интерфейса заключается в гармоничном сочетании стандартов, эстетики и прагматизма.

Высокое качество Qt обеспечивается разработчиками Trolltech: они талантливы и прислушиваются к своим клиентам ( я знаю это, потому что был одним из них ). Их клиенты занимаются разработкой больших реальных приложений. Их клиенты не заботятся о "преданности духу C++", их больше волнует решение насущных проблем и достижение поставленных целей. Разработчики Qt не стремятся пересмотреть, что является хорошим API, а что плохим. У них свыше 2000 клиентов, которые сопоставляют их решения с реальностью.

Я не утверждаю, что API Qt идеален, он имеет свои причуды. Я лишь хочу сказать, что он работает очень хорошо. Я утверждаю так, потому что использовал его ежедневно на протяжении последних двух лет в двух различных проектах ( Rosegarden и в проекте моего предыдущего работодателя ). Вам будет нелегко создать что-либо лучшее, и я не думаю, что Gtkmm движется в этом направлении, скорее наоборот.

И наконец, Вы можете послать заплату разработчикам Qt и обсудить с ними проблемы, поскольку поддержка клиентов - это часть их работы. Я знаю это на собственном опыте, как и разработчики KDE.

"Расширения" Qt для C++

Murray : (...) Важнейшим из этого является модификация языка C++ и использование собственного нестандартного строчного класса. Мы убедились, что это излишне.

Что касается QString, этот класс, несомненно, нужен, потому что std::string не обрабатывает уникод. Сам Gtkmm содержит класс Glib::ustring. Насколько я вижу, его интерфейс сходен с std::string, и я также считаю это плохой идеей. Потому что независимо от того, насколько это решение стандартно, оно не является ни удобным, ни мощным. QString предлагает очень полезные возможности, например, arg(), sprintf(), section(), simplifyWhiteSpace(), toInt(), подмножество setNum() и конечно же поддержку регулярных выражений, что является неоценимым в повседневной разработке.

Теперь о вечно спорных модификациях языка C++. Вы утверждаете, что они не нужны. Это верно, несмотря на то, что не доказано в целом, потому как Gtkmm не обеспечивает всех возможностей, предоставляемых "расширениями" Qt.

Сигналы и слоты

Я хочу остановиться на том, как происходит символьная обработка слотов и сигналов. В Gtkmm они являются шаблонными функциями, которые Вы передаете вызову connect. В Qt они представлены старым добрым типом const char*. Я в шоке ! Ужас ! QObject::connect принимает в качестве имени сигнала и слота тип const char* ! Такой же была моя первая реакция. Но позже я осознал два момента :

Второе преимущество стало наиболее важным для меня. Оно сэкономило мне несколько недель разработки. При запуске приложение создавало свой пользовательский интерфейс из XML-файла, и в этом файле я мог определить следующее:
< connect source="widgetname" dest="someclass"
   signal="buttonPressed()" slot="receivedButtonPress()" />

Управляющий код был написан в течении полудня. Я полагаю, в настоящий момент похожий механизм используется в приложении Qt Designer. Попробуйте сделать нечто подобное с libSigC, чтобы увидеть, насколько это "просто".

Динамичность также обеспечивает свободное связывание. На момент вызова connect Вы можете не знать тип адресата и источника. Однако они должны быть унаследованы от QObject. Поэтому в случаях, подобных вышеописанному ( когда связывание может осуществляться в любом месте кода ), Вам не нужно использовать #include для определения источника и адресата. Достаточно лишь написать следующее:

void do_connect(QObject *source, QObject *destination)
{
  QObject::connect(source, SIGNAL(buttonClicked()),
               destination, SLOT(gotButtonClicked()));
}

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

Такой механизм ( когда связывание двух объектов осуществляется третьим ) является весьма распространенным. Как только Вы поймете, что сигналы могут использоваться не только для обработки событий, но и для свободного связывания объектов между собой, Вы станете делать это весьма часто.

Связывание может осуществляться даже самим источником или адресатом. Этого можно достичь следующим образом :

void MyWidget::do_connect(QObject *destination)
{
  connect(this, SLOT(itemClicked(int)),
          destination, SLOT(gotItemClicked(int)));
}

Вы не привязываетесь к определенному адресату, в любой момент Вы можете его переназначить или поменять. Это еще одно большое преимущество при разработке. Как Вы думаете, почему все новые языки, например, Java и C#, обладают такой же гибкостью? Потому что это позволяет выполнять Вашу работу быстрее. Я использую Qt в течение двух лет и никогда не сталкивался с ошибками, связанными с обработкой сигналов или слотов. Я думал, что отсутствие контроля соответствия типов может стать источником проблем, но этого не произошло. Для отслеживания ошибок достаточно предупреждающих сообщений.

Более подробную информацию по этому вопросу можно найти в документации Qt.

moc и дополнительные ключевые слова

Это наиболее часто приводимый аргумент против Qt. Все эти `slots`, `signals` и `emit`, которые Вы используете для определения и описания классов, не относятся к C++, и Вы можете обойтись без них. Да, это верно. Но факт в том, что, используя их, можно добиться лучшего результата. Благодаря им Ваш код становится более простым и легко читаемым.

Trolltech предлагает очень хорошее объяснение, почему в Qt для сигналов и слотов не используются шаблоны. Каждый пункт подтверждается моим личным опытом и, полагаю, опытом всех пользователей Qt.

Забавно, что один из пунктов касается вопросов синтаксиса. Так как в API GTK+ & Co. Вы использовали "ОО-подход", Вам лучше знать, насколько это верно. Но все же, Вы продолжаете считать ООП лишь синтаксической разновидностью. Подобно классам, сигналы и слоты являются четкой концепцией, поэтому их следует реализовывать на уровне языка, а не оставлять на усмотрение пользователя.

Поэтому Ваше решение проблемы сигналов и слотов средствами C++ подобно реализации классов средствами C. Мое решение заключается в том, чтобы сделать `slot` и `signals` ключевыми словами C++ :-). Не важно, как это будет реализовано: в виде библиотеки C++ или на машинном языке. Главное, что это добавит в язык возможности для выражения Ваших идей.

Что касается компилятора moc, его использование не создает никаких проблем. Я убедился в этом на собственном опыте. Если Вы самостоятельно создаете make-файлы, Вам придется добавить еще одну строку. В противном случае, за Вас это сделает KDE-макрос automake или Qt-инструмент qmake.

Murray : (...) Использовать Gtkmm совместно с другими API C++ очень легко. (...)

Какие Вы видите проблемы при использовании Qt совместно с другими API C++ ? В Rosegarden мы успешно использовали STL ( и весьма часто ) абсолютно без проблем, за исключением тех, которые были вызваны собственно самим STL :-). К примеру, std::vector<QString> работает, как и ожидалось.

Murray : (...) Я полагаю, что если Вы любите C++, то Вы полюбите Gtkmm, и что Gtkmm является лучшим примером для изучающих C++. (...)

Я не уверен в том, что Gtkmm является "лучшим примером" для изучающих C++, но Ваше высказывание "если Вы любите C++, то Вы полюбите Gtkmm" является сутью проблемы и причиной, по которой Вы не в состоянии понять, почему Qt лучше. Я не люблю C++ и мне нет до него дела. Меня интересует разработка полезных приложений. Поэтому я не против использования библиотеки, которая добавляет в C++ нестандартные ключевые слова, если это упрощает разработку. Но Вы любите C++, поэтому Вы не можете с этим согласиться. Вы обеспокоены языком настолько, что даже не обращаете внимания на его недостатки. Вы не понимаете, что стараясь остаться "верным" ему, Вы усложняете себе жизнь. Именно поэтому C-хакеры Gnome, успешно использующие объектную модель GTK+, не осознают, что могли достичь тех же целей быстрее, используя C++ и Qt. Они глубоко убеждены в том, что идут "простым" путем, поэтому они любят C и им не нужно изучать другой язык.

Стандарты не являются святыми. Теоретически, следуя им, Вы должны получать определенные преимущества. Но на практике это не всегда так, потому что они могут быть утверждены людьми, склонными ошибаться. Инструментальные средства или языки нельзя любить, они должны использоваться по назначению. Иначе, когда появится лучшая альтернатива, Вы проигнорируете ее, потому что не захотите поступиться собственными предпочтениями.

Сравнение Qt и Gtkmm

Мне хотелось бы обратиться к некоторым другим моментам Вашего доклада на конференции разработчиков GUADEC ( GNOME Users And Developers European Conference ).

Контейнеры данных

Вы говорите : QT дублирует много кода, который теперь есть в стандартной библиотеке, например, контейнеры и информацию о типе.

Это верно. Но :

Управление памятью

Во-первых, Вы утверждаете, что Qt требует использования указателей. Это неверно. Для виджетов Вы можете использовать обыкновенные переменные ( см. этот пример ), и если Вы используете указатели, Вы можете их удалить.

Предположим, что Вы создаете довольно сложный диалоговый класс с большим количеством элементов управления и текстовых полей. Естественно, Вы должны следить за состоянием элементов управления, поэтому Вы сохраняете их в виде данных-членов. А как поступить с текстовыми полями и компоновщиками виджетов ? Есть ли смысл сохранять указатели на них ? Не проще ли написать следующее :

    new QLabel("Some label here", vbox);

и забыть об этом, потому что Вы знаете, что Qt обеспечивает автоматическое уничтожение объектов ? На мой взгляд, это проще. Нет никакого смысла самостоятельно управлять памятью, если машина может сделать это за Вас. Она всегда справится с этим лучше.

Итак, Вы утверждаете, что предоставляя пользователю возможность выполнять однообразную работу, при которой легко допустить ошибки, Вы поступаете хорошо, так как используются "стандартные средства C++". Я утверждаю, что эти "стандартные средства C++" должны скрываться любой хорошей библиотекой C++. Беспокоят ли Вас std::string и все контейнеры STL тем, как они распределяют память ? Нет. В лучшем случае распределять память Вы захотите самостоятельно, потому что иногда у Вас могут быть особые требования, но к виджетам это не относится. Так почему в Qt должно быть иначе?

Контейнер виджетов

Вы утверждаете, что классы контейнеров и компоновки виджетов в Qt раздельны, в отличие от их реализации в Gtkmm. Я не уверен, что Ваc понимаю. В Qt имеются контейнеры виджетов, например, QVBox, аналогом которого в Gtkmm является VBox. Они ведут себя сходным образом, Вы лишь добавляете к ним виджеты. Если в качестве родителя вновь создаваемых виджетов Qt выступает объект QVBox, метод "add()" не используется. К тому же, в Qt есть классы компоновки виджетов ( layout classes ), которые обеспечивают более гибкое их размещение. Например, QGridLayout имеет больше возможностей, чем его более простой аналог QGrid. И конечно же, Вы можете создавать свои собственные классы компоновки.

Очевидно, что Qt предлагает те же возможности, что и Gtkmm, и даже больше. Отсюда мое непонимание Ваших слов.

Изучение и использование

Murray : Мы считаем, что Gtkmm сделает Ваш код более понятным и совместимым. А также упростит изучение и избавит Вас от лишних забот.

Мой личный опыт утверждает обратное. Спустя две недели работы с Qt, я приобрел больше опыта, чем за три года использования Gtkmm :-).

В течение последних двух лет я общался со многими "бывшими пользователями Gtkmm", которые перешли на Qt, Swing и даже обычный GTK+. Все они говорили мне одно и тоже: Gtkmm слишком запутан и очень сложен в использовании. Я полагаю, Вы улучшили API Gtkmm2, но Вам никогда не избежать необходимости постоянного согласования с другими библиотеками проекта. Также мне интересно, почему проект Terraform, который в свое время был практически единственным большим приложением, использующим Gtkmm, таковым больше не является.

Итак, позвольте мне снова сослаться на свой собственный опыт работы с Qt, который, как я могу судить, является достаточно посредственным: все не связанные с GUI классы Qt , например, классы потоков, даты и времени, регулярных выражений, разбора XML, и т.д. очень полезны. Они здесь не по прихоти разработчиков Qt, а по просьбе нуждающихся в них пользователей. Вам не удастся написать современное приложение без использования этих возможностей. Например, в качестве своего формата файла Rosegarden использует сжатый XML. Очевидность такого выбора объясняется поддержкой этого формата библиотекой Qt. Чтобы то же самое сделать с Gtkmm, нам пришлось бы воспользоваться библиотекой libxml, которая имеет другой API, более или менее документирована, и т.п. Если мы захотим ее обернуть, то получим больший объем кода.

Хочу поделиться своим неудачным опытом использования Gtkmm при разработке Rosegarden ( для более подробного ознакомления см. историю разработки Rosegarden ). Сначала я попробовал создать простой виджет для отображения нот. Но для создания изображений нот на лету я должен был использовать библиотеки Gdk и Imlib. Ни одна из этих библиотек на то время не была до конца обернута, и обертки использовали отличную от принятой в Gtkmm политику управления памятью. Поэтому вначале мне пришлось заняться этими обертками. Затем для отображения нотного стана и нот мне понадобилась библиотека Gnome Canvas, которая также не была полностью обернута. И так далее, до бесконечности. Сегодня эта история повторилась бы с libxml, vfs и прочими не имеющими оберток библиотеками Gnome, возможности которых нам сейчас обеспечивают эквивалентные библиотеки KDE.

За месяц работы с Qt/KDE я достиг больших успехов в разработке, чем за 3 прошедших года. Спустя некоторое время ко мне присоединились Richard Bown и Chris Cannam, и сейчас мы уверенно продвигаемся вперед, потому что используемые нами средства разработки не стоят у нас на пути. И даже такие довольно сложные механизмы, как IPC, с помощью них реализуются просто. DCOP работает превосходно, его подключение заняло у меня 20 минут, начиная с полного незнания, что нужно делать, до получения приложения, реагирующего на сообщения DCOP. Большинство вопросов пользовательского интерфейса решается в течении часов, и затем мы можем сконцентрироваться на решении конкретных задач. Недавно я изложил некоторые свои размышления о разработке Rosegarden, и Вы можете ознакомиться с ними подробнее.

Что касается "более простого изучения", ознакомьтесь с документацией по управлению памятью в Gtkmm. Сравните ее с эквивалентной документацией в Qt.

Вывод

Murray : (...) Guillaume сейчас использует Qt. Он заявил, что для него важнее иметь работающий инструментарий, нежели совершенный API. Скоро станет устойчивым Gtkmm2 - и оба этих преимущества будут в одном инструментарии.

Да, я использую Qt/KDE, и я доволен тем, что после стольких лет разработки Rosegarden начинает выглядеть, как настоящее приложение, что всегда было моей целью. Этого никогда не произошло бы, если бы мы использовали Gtkmm.

Вопреки тому, что Вы говорите, Gtkmm2 нужны годы для достижения того же уровня возможностей. И даже если это когда-либо произойдет, это будет стоить Вам огромных трудозатрат, потому что Вам придется поддерживать каждую библиотеку и ее обертку. Даже с обертками всех библиотек Gnome ( Gnomemm и Bonobomm ) у Вас нет всех возможностей Qt и KDElibs. Глядя на иерархию простого виджета, я насчитываю 4 базовых "объекта" :

Задайтесь вопросом об их расходе памяти.

Второй вопрос: если Gtkmm стабилен уже на протяжении нескольких последних лет, то почему так мало использующих его приложений ?

Подобная дискуссия была у нас год назад. По-видимому, Ваша точка зрения не изменилась : то, что Вам не нравится Qt, это дело вкуса, но Вы четко не аргументировали, почему ее использование сделает Ваш код тяжелым в написании и сопровождении. Мой опыт состоит в том, что использование Qt для написания кода и дальнейшее его сопровождение является простой задачей. Только посмотрите, какое изобилие программного обеспечения это породило. Вы достигнете своего, только если Gtkmm будет также широко использоваться. Это свободное ПО, поэтому преобладать должна лучшая технология, правильно?

Вам не нравится Qt, потому что она не соответствует Вашим представлениям об использовании в библиотеке языка C++. И это все. Я думаю, что Вы не правы, и что Gtkmm повторяет те же самые ошибки, из-за которых C++ приобрел репутацию сложного языка.

Так что я желаю Вам удачи с Gtkmm2, а сам продолжу использовать Qt/KDE и разрабатывать Rosegarden вместе с Rich и Chris.