Страницы

Поиск по вопросам

вторник, 31 декабря 2019 г.

Способ реализации “роутинга” в С++ программе

#cpp #qt5 #websocket


Во всех современных php фреймворках реализован функционал для определения того, какой
контроллер и с какими параметрами должен быть вызван на основе URL; называется роутинг
или маршрутизация.

Я пишу программу на Qt/C++, используя вебсокеты, и задумался о способе реализации
"роутинга". Мне показалось что паттерн "абстрактная фабрика" хорошо может подойти для
такой задачи, но дело в том, что всё равно придётся описывать какую-нибудь структуру,
чтобы знать, в какой момент какой метод какого класса вызывать. А если у меня функционал
разбросан по разным файлам, то придётся через #include подключить их все, где будет
реализована эта фабрика. Это я так понимаю, но могу ошибаться. В дополнение нужно будет
сделать возможность расширять этот "роутинг" плагинами. В Qt есть Qt plugins; надеюсь,
что он действительно поможет. 


Подойдёт ли для реализации "роутинга" паттерн "абстрактная фабрика"?
Как мне выполнить любую функцию/метод класса без подключения файла, где он реализован,
через #include?
Может, реализовать функционал "контроллера" в отдельных библиотеках и подключать
каждую из них при необходимости?


P.S. В php-фреймворках пишут свою маршрутизацию, которая возвращает информацию о
маршруте, включая данные о том, какой контроллер требуется выполнить. Я нечто подобное
хочу реализовать в своей программе, и как анализировать маршрут меня не особо беспокоит,
а вот то, как вызвать произвольный "контроллер" на основе этого анализа, пока не понимаю.
    


Ответы

Ответ 1



Так как Вы используете Qt, то можно использовать метаобъекты. Они дают возможность создавать объекты, зная только имя класса в виде строки и вызывать методы по имени. Как все это работает. Нужно будет "зарегистрировать" каждый класс, который будет использоваться в коде с помощью qRegisterMetaType. Так как этот код регистрации не хочется выносить в один файл (что бы туда incude'ами не подключать все файлы), то вот здесь предлагают как это сделать красиво, через шаблон и статическую переменную. В результате чего регистрация класса будет привязана только к cpp файлу. У меня есть подозрения, что точно также можно зарегистрировать с плугина классы и все будет доступно и работать. Теперь осталось научиться вызывать конструктор и методы. И самый последний шаг - сделать роутинг. Тут либо самому на базе хеша, либо на базе списка/вектора. Тут уже смотрите на свой любимый фреймворк и выбирайте свой путь.

Ответ 2



Почитайте про динамическую загрузку библиотек. Вы можете создать некий абстрактный класс, который будет содержать требуемые базовые методы. Унаследовать все контроллеры от него. Каждый контроллер собрать в отдельную .dll или .so и определить в нем, вне класса, обычную функцию с объявлением extern 'C' с именем, например имя-контроллера_main без параметров (или с таковыми, главное определитесь что вы ей хотите передавать, у всех контроллеров функции должны быть аналогичны). Эта функция должна создать экземпляр контроллера и вернуть его адрес, с типом абстрактного класса родителя контроллеров, конечно. Когда вы из URL получаете имя контроллера, которое до этого не встречалось, вы динамическим загрузчиком грузите имя-контроллера.dll (.so) и получаете у него адрес функции с заданным именем (т.е. имя-контроллера_main) (загрузчик умеет искать функции по имени). Этот адрес сохраняете себе в какой нибудь таблице сопоставления имен контроллеров с их базовыми функциями. После чего вызываете функцию по полученному адресу, она возвращает вам объект, с которым вы уже работаете единообразно, как определено базовым классом.

Ответ 3



Данный ответ построен по принципу дополнения к ответу @KoVadim и должен рассматриваться в соответствующем контексте. Регистрация классов и их методов при помощи qRegisterMetaType() позволяет простым образом превратить строку, полученную из запроса от клиента, непосредственно в вызов метода, однако всё равно влечёт за собой необходимость следить за корректностью полученных символов. Проще говоря, если случилось "404", то требуется соответствующая обработка. Ничего страшного не произойдёт, если вызвать QMetaObject::invokeMethod() для несуществующего метода, однако в логи посыпятся сообщения от Qt, которые будут напрягать одним своим существованием. Конечно можно предварительно позаботиться об их фильтрации при помощи QLoggingCategory::setFilterRules(), но эта мера скорее с душком, нежели с душой. Придётся выполнять проверку, зарегистрирован ли такой-то класс и его такой-то метод, что в итоге приведёт к выводу, что в данной ситуации нет никакой разницы, использовать ли регистрацию методов или какой-либо незатейливый хэш, с заранее добавленными в него допустимыми вариантами. Ключом в таком хэше, очевидно, может быть строка с наименованием контроллера (или пути из нескольких контроллеров), плюс наименование метода, который должен быть выполнен. В значения хэша вовсе необзательно вносить какой-то свой базовый класс, достаточно и того, что контроллер будет унаследован от QObject. Данный факт позволит отправлять контроллерам события - QEvent. События примечательны тем, что им можно назначать приоритет доставки: QCoreApplication::postEvent(target_object1 , new RequestEvent(), Qt::LowEventPriority); QCoreApplication::postEvent(target_object2 , new RequestEvent(), Qt::HighEventPriority); Как показано в примере, несмотря на то, что событие для target_object1 будет назначено на отправку первым, дойдёт оно до своего получателя лишь после отправки и получения своего события объектом target_object2, так как имеет более низкий приоритет. Разделение обработки на приоритеты может быть чрезвычайно полезно, если типов запросов несколько, и некоторых из них, дабы не забивали своим присутствием очередь, вполне допустимо обработать позже. Например, это могут быть запросы на получение картинок, при выполнении которых клиент отнюдь не лопнет от негодования, если не получит изображения сию секунду. Как нетрудно догадаться, в примере объект класса RequestEvent может нести с собой любые параметры, которые понадобится передать целевому контроллеру. Под временный контейнер для таких данных обычно хорошо подходит QVariant. Поскольку автор настоящего вопроса привёл уточнение о том, что контроллеры, обрабатывающие запросы, могут подключаться в плагинах, то следует обратить внимание на особенность, связанную с регистрацией событий в Qt и рассмотренную уже в этом вопросе. Это, пожалуй, единственное неудобство, которое имеет место быть при совместном использовании плагинов и регистрации событий.

Комментариев нет:

Отправить комментарий