Wt, библиотека C++ для разработки веб-приложений

Всем привет!

Так случилось, что на Хабре не было ни одного упоминания Wt, C++ Web Toolkit, кроме одного комментария.

Библиотека Wt может заинтересовать тех, кто пишет на C++ и захотел посмотреть в сторону Web, но не хочет изучать и/или использовать HTML, CSS, JavaScript, SQL и дополнительные технологии, связанные с веб-разработкой и работой с БД. В данной статье моей целью было обратить внимание сообщества на Wt, а не освещать все его возможности.

Если Вы жаждете посмотреть, как это работает, вот пример, демонстрирующий различные виджеты: www.webtoolkit.eu/widgets

Обзор возможностей библиотеки на русском языке: www.webtoolkit.eu/wt/ru/features

Возможности Wt

Здесь я приведу некоторые из особенностей, которые мне кажутся наиболее привлекательными.

Wt является кроссплатформенной свободной поддерживаемой C++ библиотекой. Релизы выходят несколько раз в год. Доступна на условиях лицензии GNU GPL 2 или коммерческой лицензии. Напомню, что лицензия GNU GPL 2 не запрещает создавать приложение с закрытым кодом, чтобы пользоваться им лично, в частности для разворачивания на своем сервере.

Программирование с использованием Wt строится вокруг виджетов, подобно Qt. Многие вещи похожи на Qt, что делает Wt простым для изучения для тех, кто знаком с Qt. В отличие от Qt, не происходит кодогенерации — используются сигналы, основанные на boost.signals, которые представляют собой обычный код C++, не требующий генерации. Сигналы виджетов (например, сигнал нажатия на кнопку) можно присоединять к любым функциям C++ (и функциональным объектам). Полностью асинхронный вход/выход (на основе библиотеки boost.asio) управляется событиями. Не происходит постоянного порождения и завершения потоков выполнения. Для каждой сессии на сервере хранится объект WApplication, через который доступно всё дерево виджетов. Доступ к виджетам сессии осуществляется способом, защищенным от «гонок», благодаря чему возможно безопасное взаимодействие сессий между собой и с сервером. Это продемонстрировано в примере чата. В этом же примере показано использование событий, инициируемых сервером (а не клиентом) и возможность включения приложения в другие страницы или сайты в виде загружаемого скрипта (вроде того, как виджет google maps может встраиваться в друге сайты).

Wt-приложения работают в основных браузерах, причем если JavaScript работает, выдается Ajax-версия, а если не работает — аналогичный HTML код, что является уникальной возможностью Wt, по крайней мере среди C++ фреймворков. Это позволяет без дополнительных усилий со стороны разработчика создавать приложение, использующее преимущества таких технологий, как Ajax, Comet, WebSocket, но в то же время работающее, если они недоступны. Кроме того, есть возможность оптимизации выдачи для поисковых роботов (речь идет не о клоакинге, а об улучшениях вроде отказа от случайных идентификаторов объектов). Wt может использовать такие новшества HTML5, как переписывание URL в браузере (для создания REST-приложений), выбор нескольких файлов, теги video и audio, но если их нет, по возможности используются обходные пути (к примеру, звук и видео через flash вместо тегов video и audio).

Возможно создание веб-приложений, подключаемых через http, fastcgi или ISAPI (последний только для платформ Win32). Какой именно способ соединения будет использоваться, определяется на этапе компоновки, выбором нужной библиотеки. Компонуя свой код с библиотекой встроенного http-сервера, получаем полноценный Веб-сервер, который удобно использовать при отладке, но можно запускать и на production, хотя в последнем случае стоит также рассмотреть возможность использования других способов соединения.

В Wt встроено использование Юникода и локализации.

Низкое потребление ресурсов позволяет запускать приложения на устройствах с ограниченными ресурсами, например, на роутерах.

Библиотеку удобно использовать для создания веб-интерфейсов для существующих программ, написанных на C++.

Библиотека обеспечивает высокий уровень безопасности, в частности защиту от атак XSS или SQL-injection. К примеру, виджет WText, отвечающий за отображение текста, убеждается, что его текст не содержит «опасных» атрибутов и тегов (вроде script), и вычищает их при необходимости. Имеется поддержка HTTPS. Есть защита от DDoS атак.

Библиотека также включает ряд надстроек, полезных при веб-разработке, в частности, систему рисования, систему построения диаграмм, ORM (отображение классов C++ на структуры баз данных), систему аутентификации.

Вообще хочется отметить чистоту и высокое качество кода библиотеки: легко читается не только документация, но при необходимости и код реализации. Сделано общо и гибко. Нигде (даже при компиляции кода, связанного с БД) не происходит кодогенерации. Часть библиотеки, ответственная за работу с БД, может быть использована (или куплена лицензия) отдельно от остальных компонентов. Эта часть библиотеки заслуживает отдельной статьи.

Для Wt доступны версии на Java и других языках, но тут я ограничусь рассмотрением C++ версии. Версия 1.0.0 была опубликована в 2005 году, так что по своей «зрелости» Wt может соперничать с Django и Ruby on Rails. Стоит отметить, что на C++ существует ещё как минимум два активных проекта: CppCMS и Tntnet. Сравнение возможностей представлено в википедии. Мой выбор пал на Wt в первую очередь за возможность полного абстрагирования от HTML, CSS, JavaScript в пользу одного языка — C++, а также за автоматическую поддержку как Ajax браузеров, так и браузеров без Ajax.

Пример простого приложения

Пример взят отсюда: www.webtoolkit.eu/wt/ru/examples

#include <Wt/WApplication>
#include <Wt/WBreak>
#include <Wt/WContainerWidget>
#include <Wt/WLineEdit>
#include <Wt/WPushButton>
#include <Wt/WText>

using namespace Wt;

/*
 * A simple hello world application class which demonstrates
 * how to react to events, read input, and give feed-back.
 */
class HelloApplication : public WApplication
{
public:
  HelloApplication(const WEnvironment& env);

private:
  WLineEdit *nameEdit_;
  WText *greeting_;

  void greet();
};

/*
 * The env argument contains information about the new session, and
 * the initial request. It must be passed to the WApplication
 * constructor so it is typically also an argument for your custom
 * application constructor.
*/
HelloApplication::HelloApplication(const WEnvironment& env)
  : WApplication(env)
{
  setTitle("Hello world");                               // application title

  root()->addWidget(new WText("Your name, please ? "));  // show some text
  nameEdit_ = new WLineEdit(root());                     // allow text input
  nameEdit_->setFocus();                                 // give focus

  WPushButton *button
    = new WPushButton("Greet me.", root());              // create a button
  button->setMargin(5, Left);                            // add 5 pixels margin

  root()->addWidget(new WBreak());                       // insert a line break

  greeting_ = new WText(root());                         // empty text

  /*
   * Connect signals with slots
   *
   * - simple Wt-way
   */
  button->clicked().connect(this, &HelloApplication::greet);

  /*
   * using an arbitrary function object
   * (binding values with boost::bind())
   */
  nameEdit_->enterPressed().connect
    (boost::bind(&HelloApplication::greet, this));
}

void HelloApplication::greet()
{
  /*
   * Update the text, using text input into the nameEdit_ field.
   */
  greeting_->setText("Hello there, " + nameEdit_->text());
}

WApplication *createApplication(const WEnvironment& env)
{
  /*
   * You could read information from the environment to decide whether
   * the user has permission to start a new application
   */
  return new HelloApplication(env);
}

int main(int argc, char **argv)
{
  /*
   * Your main method may set up some shared resources, but should then
   * start the server application (FastCGI or httpd) that starts listening
   * for requests, and handles all of the application life cycles.
   *
   * The last argument to WRun specifies the function that will instantiate
   * new application objects. That function is executed when a new user surfs
   * to the Wt application, and after the library has negotiated browser
   * support. The function should return a newly instantiated application
   * object.
   */
  return WRun(argc, argv, &createApplication);
}


Это пример простого приложения, спрашивающего имя пользователя. Когда пользователь вводит имя и нажимает Enter или кнопку «Greet me.», приложение привествует его: «Hello there, %USERNAME%». Думаю, разбирать код подробно смысла нет, тем более в нем есть комментарии.

Посмотреть запущенный пример можно тут: www.webtoolkit.eu/wt/examples/hello/hello.wt

Чтобы скомпилировать этот пример, нам потребуется компилятор C++ и установленная библиотека Wt. Везучие пользователи Debian и Ubuntu могут просто установить пакет witty-dev.

В линуксе команда компиляции выглядит так:

g++ hello.cpp -lwt -lwthttp -lboost_signals -o hello


Команда запуска:

./hello --docroot . --http-address 127.0.0.1 --http-port 8000


После чего приложени можно будет открыть в браузере по адресу 127.0.0.1:8000

Ссылки

Сайт проекта: www.webtoolkit.eu
Блог: www.webtoolkit.eu/wt/blog
Введение: www.webtoolkit.eu/wt/doc/tutorial/wt.html
Примеры: www.webtoolkit.eu/wt/examples
Скачать: www.webtoolkit.eu/wt/download
Документация: www.webtoolkit.eu/wt/documentation
Система отслеживания ошибок: redmine.emweb.be
Проект также представлен на гитхабе github.com/kdeforche/wt
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 30

    +6
    Мой выбор пал на Wt в первую очередь за возможность полного абстрагирования от HTML, CSS, JavaScript в пользу одного языка — C++, а также за автоматическую поддержку как Ajax браузеров, так и браузеров без Ajax.

    К сожалению это большое заблуждение, как только захочется сделать что-то выходящее за рамки элементов представленных в библиотеке виджетов, вам придется со всем этим столкнуться в полной мере.
      +1
      Вы правы, поэтому я написал "возможность абстрагирования". Если хочется чего-то особенного, скажем, прикрутить какой-нибудь jquery-плагин, то, разумеется, на JavaScript писать придется. Библиотека предоставляет ряд удобств, упрощающих подключение своего JavaScript: JavaScript сигналы и слоты, методы для загрузки JavaScript-библиотек и выполнения произвольного JavaScript-кода.

      Но при стандартном использовании можно обойтись без использования HTML, CSS и JavaScript.

      Кстати, в галерее виджетов не представлены некоторые виджеты библиотеки, например, WGLWidget для работы с WebGL.
        0
        Может вы мне подскажете? Нет ли у них чего-нибудь удобного, чтобы налету делать видео из множества snapshot'ов? Потому что я так и не смог заставить его показывать нормальное видео без проскакивания белых полосок при смене кадров.
        Не связано ли это со скоростью?
          0
          Думаю, склеить снимки в единый видео-файл проще всего внешними средствами (например, ffmpeg, mencoder), после чего показывать через WMediaPlayer.

          В галерее виджетов есть работающий пример показа видео.
            0
            К сожалению такой вариант не подходит, т.к. длительность видео заранее неизвестно и необходимо воспроизводить его в реалтайме, ну или очень близко к нему.
              0
              Мне не доводилось работать с видео в Wt, поэтому могу лишь предположить, что можно использовать WMediaPlayer, а в качестве источника использовать свою реализацию WResource, выдающую видео, склеенное из кадров. Так как WResource по умолчанию кеширует весь ответ, а потом отдает его, при этом нужно использовать ResponseContinuation, подавая каждый раз очередной небольшой кусочек видео.

              Не знаю, можно ли при этом обойтись без знания полного размера. В классе WStreamResource реализована отдача любого потока. Только, судя по его коду, полный размер потока ему таки требуется. В принципе, если допускается не полностью чистое решение, можно попробовать подать максимальную длину, которую размер потока точно не превысит.
          0
          Но при стандартном использовании можно обойтись без использования HTML, CSS и JavaScript.

          Звучит как «раздели на 0».
          +1
          К сожалению это большое заблуждение, как только захочется сделать что-то выходящее за рамки элементов представленных в библиотеке виджетов, вам придется со всем этим столкнуться в полной мере.
          это утверждение соответствует большинству фреймворков, и как правило всегда тебе или заказчику чего-то хочется большего, и ты вынужден копаться в тоне говнокода. И тогда по времени выходит больше, чем если бы реализовывал все сам.
            +1
            Вы сейчас стали по другую сторону баррикад. В итоге разработка любого продукта занимает в 10 раз больше времени, а отладка…
              0
              речь идет о некоторых фичах вашего Продукта, которые очень хочет Заказчик. Но, к сожалению эти фичи не предусмотрены в используемом фреймворке. Есть два пути реализации: Правильный — разбираться в тонне говнокода и пытаться их реализовать средствами фреймворка, путем написания дополнительных плагинов, использования кучи кэллбэков, обвязок и прочих вязок… Или не совсем верный — реализовать их самостоятельно и внедрить во вреймворк. Как показывает практика — второй путь короче.
                0
                Часть Заказчиков предпочитает не открывать код покупателям приложения. Ещё может так получиться, что распространять проприетарный плагин к фреймворку позволяется лицензией, а вносить изменения в код фреймворка и распространять измененный вариант без открытия кода — нельзя. Это не относится к Wt, но относится к фреймворкам, распространяемым под лицензией GNU Lesser General Public License.

                Мне кажется, для написания плагина требуется не такое глубокое «погружение» в код фреймворка, как для внесения своих изменений. Если мы внесли изменения в сам фреймворк, то как быть при выходе новой версии фреймворка? Наши изменения могут оказаться несовместимыми с изменениями разработчиков фреймворка. Так что в плане сопровождения плагин выглядит привлекательнее.
          +2
          Видел где-то некий проект под названием node.cpp, думаю такой подход был бы выгоднее. На плюсах всё-таки лучше делать числодробилки, а не генерилки html'ек, в которых от ЯП уже мало что зависит.
            0
            Речь идет об этом проекте?

            При подходе, в котором используются виджеты, объекты, отвечающие за них, приходится хранить на сервере (иначе не получится сделать HTML-версию, аналогичную Ajax-версии). При использовании других языков возросли бы затраты ресурсов, которые пришлось бы выделять на сервере для работы с деревом виджетов. Собственно, это заметно по самому Wt: для каждой версии Wt выходит аналогичная версия jWt, написанная на Java; было замечено, что jWt оказывается примерно в 2 раза медленнее, чем release-версия Wt. Это может быть иметь значение при использовании на устройствах с ограниченными ресурсами.

            В принципе, подобный подход применяется и в других библиотеках, написанных на других языках: Pyjamas (Python) и Google Web Toolkit (Java). Однако обе библиотеки не предоставляют HTML-версии, для ситуаций, когда JavaScript недоступен.

            К числодробилкам часто хочется иметь веб-интерфейс. Написать его можно на том же языке, что и саму числодробилку (часто это как раз C/C++/Java). Причем задача генерации HTML перекладывается с разработчика на библиотеку: написание веб-интерфейса напоминает написание обычного графического приложения, например на Qt.
              0
              Скорее про этот github.com/d5/node.native
                0
                Я использовал эту либу как раз для того, чтобы сделать сразу и GUI и веб интерфейс. В теории все было гладко и приятно. На деле пришлось воротить множество костылей, чтобы подружить Qt и Wt. В основном это было связано с тем, что они оба работают в mainLoop'ах.
                  0
                  Мне не доводилось писать общий код, из которого можно получить и Qt и Wt приложение. Так как их системы виджетов не связаны, а лишь похожи, сделать это сегодня будет нелегко. Разве что с кодогенерацией: заменять WPushButton на QPushButton и т.д., но не думаю, что так будут делать: классы всё-таки разные и не все методы одной библиотеки имеют аналог в дргой. Ситуацию мог бы исправить qt-backend для Wt, но его нет.

                  Если речь идет о приложении, которое было бы одновременно и Qt, и Wt-приложением (скажем, прикрутить веб-интерфейс на Wt к qbittorrent вместо того, что у него сейчас) — теоретически можно благодаря wtwithqt, но я этого не пробовал.
                    0
                    Для Qt полно движков, для чего вам понадобился Wt, который тянет еще Boost?
                      0
                      Что за движку, дайте ссылки?
                  +2
                  Странно что автор не упомянул о конкурентных проектах: CppCMS, Tntnet, которые как минимум проще, имеют меньше зависимостей и код чище.
                    0
                    Я их упомянул. В обоих этих проектах требуется писать HTML руками. В обязательных зависимостях Wt только boost.
                      0
                      Я их упомянул.<.blockquote>
                      Действительно, прошу прощения.
                      В обоих этих проектах требуется писать HTML руками


                      А вот ту я не совсем понял, что вы имеете ввиду?
                      К примеру, в CppCMS очень удобный архитектура, а шаблонизатор сделан по типу Django.
                        0
                        Когда мы пишем шаблоны в Django, мы пишем при этом HTML (с надстройками, касающимися шаблонов: подстановки, условия, циклы и т.п.). То есть, без знания HTML проект на Django (или CppCMS) не сделаешь.

                        Кстати, в Wt тоже есть виджет, отвечающий за шаблоны. Так что если в каком-то месте удобнее писать HTML напрямую, это возможно.
                          0
                          То есть, без знания HTML проект на Django (или CppCMS) не сделаешь.

                          Без знания HTML и CSS (как минимум) соваться в разработку каких угодно веб-приложений вообще нет смысла.
                            0
                            Поправлюсь: не знания, а применения. Знать их, конечно, желательно, чтобы понимать, как оно работает. Но применять их напрямую можно по минимуму. Wt позволяет подключать свои HTML, CSS и JavaScript, но для большинства задач есть встроенные инструменты, позволяющие этого избежать.
                        0
                        Как раз html в данном случае проще и эффективнее руками писать.
                          –1
                          Писать HTML руками просто до тех пор, пока это простой HTML. А стоит столкнуться с элементами, которые по-разному поддерживаются браузерами, проще пользоваться библиотекой виджетов, которая снимает с разработчика необходимость следить за особенностями разных браузеров.

                          К примеру, для WSpinBox (поле для ввода числа) используется встроенная возможность браузера, если она доступна, в противном случае используется собственная реализация на JavaScript.

                          Для JavaScript проблему несовместимости браузеров можно решить при помощи JavaScript-библиотеки, например, jquery, а в данном случае решается проблема и для браузеров, в которых JavaScript недоступен.

                          Эффективность (скорость создания) HTML самописными генераторами может быть довольно высокой, но в случае Wt преимуществом будет использование Ajax, позволяющее генерировать не всю страницу, а лишь изменившуюся часть. Это хорошо продемонстрировано на этой странице: при прокрутке таблицы загружаются только необходимые ряды, а не вся таблица. В то же время, если JavaScript недоступен, таблица будет выводиться постранично.
                    –1
                    Демонстрашка медленная, несмотря на всю мощь нативной природы С++. Не видел ещё ни одной web-платформы на С++, которая бы демонстрировала хорошую отзывчивость. Решения с интерпретаторами и garbage-коллекторами работают как правило не хуже. Не движется веб-прогресс в сторону С++. Жаль. Хотя может оно и к лучшему.
                      0
                      Несколько долгие ответы (особенно, время на создание сессии) могут быть связаны с нагруженностью или удаленностью сервера, где запущены демострации. При тестах нетривиального приложения на локальном компьютере ответы получаются довольно быстрые.

                      Приведу здесь немного цифр. Были скомпилированы release-версии приложения и библиотеки. Былы включена запись времени, затраченного на стороне сервера, в лог (запись времени можно включить, если добавить в файл wt_config.xml:
                      <log-response-time>true</log-response-time>). Затем я открывал приложение и делал несколько действий, приводящих к перерисовке части страницы.

                      В Ajax версии создание сессии состояло в нескольких относительно долгих запросах около 20 мс (загрузка jquery, стилей и других файлов, которые в Ajax приложение требуется загрузить 1 раз). Большинство дальнейших действий занимали меньше 1 мс (например, 0.4 мс).

                      В HTML версии время на каждое действие было чуть больше (2-3 мс). Думаю, это связано с тем, что нужно было перегенерировать всю страницу, а не её часть.

                      Тут речь идет о времени, потраченном одним из потоков выполнения сервера (их запускается по умолчанию 10, чтобы не было простоев, например в случае работы с БД).

                      Чтобы ускорить соединение с сервером, можно разрешить соединяться через WebSocket (<web-sockets>true</web-sockets> в настройках), если он доступен. Я не измерял ускорение, которое при этом получается, но на глаз всё движется мгновенно.
                        +1
                        Такой командой у меня не собралось:
                        g++ -lwt -lwthttp -lboost_signals hello.cpp -o hello
                        Собралось такой
                        g++ hello.cpp -o hello -lwt -lwthttp -lboost_signals
                          0
                          Спасибо за замечание! Ошибка исправлена. Библиотеки должны быть перечислены в списке аргументов компилятора после имени файла с исходным кодом. Некоторые компиляторы принимают неправильный порядок аргументов. Так поступает и мой компилятор (gcc 4.4.5), поэтому ошибку не заметили.

                        Only users with full accounts can post comments. Log in, please.