Концепт метасистемы PHPQt5

идеи проектов

Модератор: ArtMares

WxMaper
Аватара пользователя
Администратор
 
Сообщения: 92
Благодарил (а): 8 раз
Поблагодарили: 4 раза
Смотрел исходники генерируемые метакомпилятором Qt для классов, интересовал в основном процесс вызова методов по их имени.
Оказалось все довольно просто: есть список имён методов (обычные строки, представляете? :scratch_one-s_head: даже не адреса функций, а просто имена), каждому методу присвоен свой индекс.
Перед вызовом "метасистема" ищет нужный индекс по сигнатуре метода: methodName(argType0,argtype1,argType2),
а потом просто обращается к статической функции для вызова, в которую передаются указатель на объект, индекс функции и набор аргументов.
А в этой статической функции!!! Бугагашеньки :biggrin:

Код:
static_metacall(object, index, params) {
    PQObject *_t = static_cast<PQObject *>(object);

        switch (_id) {
        case 0: _t->setObjectName((*reinterpret_cast< QString(*)>(_a[1]))); break;
        case 1: { QVariant _r = _t->getUserProperty((*reinterpret_cast< const QString(*)>(_a[1])));
            if (_a[0]) *reinterpret_cast< QVariant*>(_a[0]) = _r; }  break;
        case 2: { QString _r = _t->objectName();
            if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = _r; }  break;
/* и так далее весь список методов QObject */
}


Так вот к чему я все это :) есть мысль создать свою метасистему поверх существующей. Таким образом можно будет:
  • во первых: избавится от обёрток для методов (от обёрток для классов избавится все-же не получится);

  • во вторых: избавится от проблем с кастованием типов;

  • в третьих: значительно ускорить вызовы методов;

  • в четвертых: значительно упростить создание обёрток, фактически все сведётся только к описанию всех методов в заголовочном файле, создавать тела методов уже будет не нужно.


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

Как я вижу реализацию метасистемы PHPQt5?
Вся магия будет в макросах, т.е. минимум кода. Не нужно будет самостоятельно составлять функцию со списком методов, нужно будет просто перечислить методы в макросах, а макросы, распаковавшись прекомпилятором, создадут валидные тела функций.
К примеру, я хочу создать обертку для QTimer:

Код:
class PQTimer : public QTimer 
{
    Q_OBJECT // инициализация местасистемы Qt
    PQ_OBJECT // инициализация местасистемы PHPQt5

    // #define PQ_METHOD(returnType, methodName, paramType1)
    PQ_META_METHODS_START { // распакуется в
    pq_static_metacall(QObject *object, int methodIndex, void **params) {
        PQTimer *pobject = static_cast<PQTimer *>(object);
        switch(methodIndex) {

        PQ_METHOD_1(void, setInterval, int); // распакуется в
        0: pobject->setInterval( (*reinterpret_cast<int>(params[0])) );

        PQ_METHOD_0(void, start); // распакуется в
        1: pobject->start();

    } PQ_META_METHODS_END // распакуется в
    }}
}


Таким образом движку останется лишь найти нужный индекс метода и вызвать функцию pq_static_metacall со всеми аргументами, которые даже кастовать не придётся - они будут передаваться как есть, за исключением случаев с автоприведением int-bool-string-double.

Буду развивать эту идею, попутно экспериментировать. Возможно в будущем движок приобретёт новый облик :)
п.с. на стороне php ничего не изменится.

zazaka
Аватара пользователя
Ответственный глюкогенератор
 
Сообщения: 98
Поблагодарили: 2 раза
то что будет быстрее и проще это ж ведь замечательно
Унабле то лоад дынамиц либрары

rinart73
Аватара пользователя
 
Сообщения: 36
Skype: art7emw
Поблагодарили: 2 раза
Смущает меня слово "поверх". Обычно это ведет лишь к усложнению и ухудшению производительности...

Честно говоря, не совсем понял. В доп.обертке будут пере-реализованы методы, а все остальное будет обрабатываться Qt-шной оберткой?

Вот этот участок кода выглядит как серия if-ов...
phpqt Код:
PQ_METHOD_1(void, setInterval, int); // распакуется в
0: pobject->setInterval( (*reinterpret_cast<int>(params[0])) );

PQ_METHOD_0(void, start); // распакуется в
1: pobject->start();


Наверное, я чего-то не понимаю, т.к. знания в плюсах у мена скудные, а в плане соединения php с с++ вообще что-то с чем-то.

В общем, по мере развития, если будет желание написать больше комментариев - буду рад глянуть :)
Почетный страдалец фигней
Пилю load_ui_file :good2:

WxMaper
Аватара пользователя
Администратор
 
Сообщения: 92
Благодарил (а): 8 раз
Поблагодарили: 4 раза
rinart73 писал:В доп.обертке будут пере-реализованы методы
Сейчас так и устроены расширения - они "пере-реализовывают" методы движка.

rinart73 писал:Вот этот участок кода выглядит как серия if-ов...
Это конструкция switch. И она успешно применяется во всём Qt - вся метасистема Qt так построена, раньше я не знал об этом, думал там что-то более серьезное, но оказалось все очень просто.  

rinart73 писал:Смущает меня слово "поверх". Обычно это ведет лишь к усложнению и ухудшению производительности...

Я наверное неправильно выразился, написав "поверх", скорее всего "вместе с существующей метасистемой".
Я пока не могу утверждать, что новый подход к разработке оберток над классами будет более производительным.
Наоборот - текущая реализация как раз является наиболее "скоростной", но это только внутри обёртки: на обертку поступает вызов, обертка перенаправляет его оригинальному классу. Но чтобы движок дошел до вызова метода обёртки, ему приходится проделать очень много манипуляций, которые и являются основным узким местом, замедляющим весь процесс. И если новый подход позволит обойти это узкое место, то, вероятно, приложения станут чуточку быстрее.

Пока вот что получается:

phpqt Код:
<?php /* c++ =) */

/** имитация работы движка **/
// получаем указатель на объект
PQWidget *widget = new PQWidget;

// вызов метода без аргументов
{
    void *params[] = {
        Q_NULLPTR
    };

    // достаём указатель на метаобъект PHPQt5
    PQMetaObject *metaObject = widget->pq_meta_object();

    // получаем индекс метода
    int indexOfMethod = metaObject->indexOfMethod("show()");

    // вызываем метод
    metaObject->invokeMethod(widget, indexOfMethod, params);
}

// вызов метода с неизвестными типами аргументов
{
    QVariant arg0 = 100;
    QVariant arg1 = 50;

    void *params[] = {
        Q_NULLPTR,
        const_cast<void*>(arg0.constData()),
        const_cast<void*>(arg1.constData())
    };

    PQMetaObject *metaObject = widget->pq_meta_object();
    int indexOfMethod = metaObject->indexOfMethod("move(int,int)");
    metaObject->invokeMethod(widget, indexOfMethod, params);
}

не так уж и накладно на первый взгляд: получаем указатели на данные и отправляем их в invokeMethod, а там объект уже сам знает что ему делать :)
Всего несколько строчек и два вызова! А в текущей реализации такой эффект достигается как минимум двухстами строками кода с вызовами нескольких вспомогательных функций.

Сама обёртка имела такой вид:
phpqt Код:
<?php /* c++ =) */

class PQWidget : public QWidget
{
    Q_OBJECT
    PQ_OBJECT

    // список методов
    PQ_METHODS_START(PQWidget) {
        PQ_V_METHOD_0(0, show);
        PQ_V_METHOD_2(1, move, int, int);
    } PQ_METHODS_END

    // список индексов
    PQ_METHODS_INFO_START {
        PQ_I_METHOD(0, show());
        PQ_I_METHOD(1, move(int,int));
    } PQ_METHODS_INFO_END
}

Это весь код обёртки - только заголовочный файл с описанием методов.

ArtMares
Аватара пользователя
 
Сообщения: 72
Откуда: Москва
Благодарил (а): 4 раза
Поблагодарили: 5 раз
Идея хорошая, готов к эксперементам)
Судя по тому что я монял это будет своя мета систмема PHPQt5, которая позволит частично избавиться от перебора необходимого метода при вызове.
Вопрос в том как ты будеь определять какой индекс тебе необходим так как в php возможно передать тоже самое число в виде строки.
WxMaper писал:за исключением случаев с автоприведением int-bool-string-double


Как вариант конечно напистаь маленькую обертку над методом которая будет производить преобразование входных аргументов в необходимый тим данных по количеству аргументов
PQStudio Development Progress: 75%
Version: 0.2

WxMaper
Аватара пользователя
Администратор
 
Сообщения: 92
Благодарил (а): 8 раз
Поблагодарили: 4 раза
ArtMares писал:Вопрос в том как ты будеь определять какой индекс тебе необходим так как в php возможно передать тоже самое число в виде строки.


Вопрос хороший :)
В приоритете всегда будет метод с такими типами аргументов, которые, собственно, ему отправили:

1. some_method(string a, string b);
2. some_method(int a, int b);

phpqt Код:
$some_object->some_method("1", "1"); // вызовет первый метод


Интереснее другая ситуация:
3. some_method(string c);

phpqt Код:
$var = 123;
$some_object->some_method(123); // вызывать как-бы нечего

в таком случае даже не удастся получить индекс функции, и снова придётся выёживаться с преобразованиями... прям те-же грабли получаются, только в профиль =)

ArtMares
Аватара пользователя
 
Сообщения: 72
Откуда: Москва
Благодарил (а): 4 раза
Поблагодарили: 5 раз
WxMaper писал:Интереснее другая ситуация:
3. some_method(string c);
в таком случае даже не удастся получить индекс функции, и снова придётся выёживаться с преобразованиями... прям те-же грабли получаются, только в профиль =)


Почему? Мы же знаем что у вызываемого метода с одним аргументом, первый в индексе идет метод который принимает строку, так почему нам перед вызовом метода объекта Qt не попробовать по умолчинию преобразовать число в строку.

Я не отрицаю что я сейчас говорю о каком то костыле и возможно оберткой над каждым методом. Да и возможно над своей реализацией Error Hadler
PQStudio Development Progress: 75%
Version: 0.2

WxMaper
Аватара пользователя
Администратор
 
Сообщения: 92
Благодарил (а): 8 раз
Поблагодарили: 4 раза
Ты немного не понял как работает вызов метода.
Из php приходит запрос на вызов метода FOO с аргументом BAR и типом BAZ. На основе этих данных я могу построить сигнатуру метода: FOO(BAZ). Только по этой сигнатуре я могу получить индекс в массиве существующих методов. И если такой сигнатуры не окажется в массиве, то индекс будет невалидным (отрицательным) - т.е. мы пытаемся передать в метод неподходящий тип аргумента.
Только в таком случае должен будет сработать вспомогательный алгоритм, который переберёт весь список методов, оставив только методы FOO (не забываем про перегрузку), и будет пытаться скастовать полученный int в какой-то тип.
Да, ты говоришь о костыле, который уже давным-давно врос в движок ;-) надо как то от него избавляться

ArtMares
Аватара пользователя
 
Сообщения: 72
Откуда: Москва
Благодарил (а): 4 раза
Поблагодарили: 5 раз
WxMaper писал:Да, ты говоришь о костыле, который уже давным-давно врос в движок ;-) надо как то от него избавляться


Да надо это как то вырезать уже ))

WxMaper писал:Ты немного не понял как работает вызов метода.
Из php приходит запрос на вызов метода FOO с аргументом BAR и типом BAZ. На основе этих данных я могу построить сигнатуру метода: FOO(BAZ). Только по этой сигнатуре я могу получить индекс в массиве существующих методов. И если такой сигнатуры не окажется в массиве, то индекс будет невалидным (отрицательным) - т.е. мы пытаемся передать в метод неподходящий тип аргумента.
Только в таком случае должен будет сработать вспомогательный алгоритм, который переберёт весь список методов, оставив только методы FOO (не забываем про перегрузку), и будет пытаться скастовать полученный int в какой-то тип.


Тогда может просто выводить сообщение об ошибке что вызван метод с невалидныйм аргументом. PHP все равно идет в сторону строгой типизации вот простой пример того что предлагают реализовать https://wiki.php.net/rfc/typed-properties. Тем более мы импользуем уже 7 версию в которой рано или поздно это появится. Да и тем самым разработчики начнут привыкать к сторогой типизации которая используется в тех же самых Java, C++, C# и тд.

P.S. Лично я везде стараюсь передавать методам только тот тип аргументов который прописан в документации.
Да и к тому же я прекрасно понимаю что данный проект для меня стал прекрасным учебным пособием для того чтобы начать програмировать на C++ Qt. Так как я стал достаточно хорошо разбирать примеры на C++ для того чтобы перенести их на PHP.
PQStudio Development Progress: 75%
Version: 0.2

WxMaper
Аватара пользователя
Администратор
 
Сообщения: 92
Благодарил (а): 8 раз
Поблагодарили: 4 раза
ArtMares писал:Тогда может просто выводить сообщение об ошибке что вызван метод с невалидныйм аргументом

ой я только за =) но мне кажется у php никогда не будет строгой типизации. Это ведь его отличительная особенность - что хочешь, то и вороти.

ArtMares
Аватара пользователя
 
Сообщения: 72
Откуда: Москва
Благодарил (а): 4 раза
Поблагодарили: 5 раз
WxMaper писал:ой я только за =) но мне кажется у php никогда не будет строгой типизации. Это ведь его отличительная особенность - что хочешь, то и вороти.


Предлагаю остановится на варианте со строгой типизацией и выводом сообщений об ошибках, так сказать придерживаться идеологии C++ Qt. И конечно в документации указать причину почему было решено так сделать)
PQStudio Development Progress: 75%
Version: 0.2

rinart73
Аватара пользователя
 
Сообщения: 36
Skype: art7emw
Поблагодарили: 2 раза
Все-таки php на то и php чтобы быть языком со слабой неявной динамической типизацией. Привязка к Qt, конечно, создает надобность в строгой типизации...
А много ли случаев с двойственной ситуацией? Помимо QSettings?


Есть такая идея, проводить проверку:
phpqt Код:
//Имеем 2 параметра типа string:
if($arg0*1 == $arg0)
  //Вызываем метод с int-ами
else
  //Вызываем метод со строками

Т.е. если был вызван:
phpqt Код:
method("5","17")

То 5 будет успешно преобразовано в число, а затем сравнено со строкой (и окажется равно самому себе)
А если был вызван:
phpqt Код:
method("5.txt","17")

То "5.txt" будет преобразовано в 5 и уже не будет равно самому себе, значит вызываем method(string,string)

Этот костыль, конечно, приемлем не во всех случаях...
Почетный страдалец фигней
Пилю load_ui_file :good2:

zazaka
Аватара пользователя
Ответственный глюкогенератор
 
Сообщения: 98
Поблагодарили: 2 раза
Главное чтобы валидный код в чистом пхп оставался валидным и тут. Чтоб результат выполнения был предсказуемым - таким же как и на чистом пхп. Имею ввиду что если я напишу '5.txt'+2 в чистом пхп получу результат...то желательно и там такойже результат получить
Унабле то лоад дынамиц либрары

rinart73
Аватара пользователя
 
Сообщения: 36
Skype: art7emw
Поблагодарили: 2 раза

Такого вот вообще не надо писать:
phpqt Код:
echo '5.txt'+2;//7
echo gettype('5.txt'+2); //double

:D
Почетный страдалец фигней
Пилю load_ui_file :good2:

ArtMares
Аватара пользователя
 
Сообщения: 72
Откуда: Москва
Благодарил (а): 4 раза
Поблагодарили: 5 раз
Я конечно понимаю что PHP позволяет делать такие вещи, но вы должны понимать что когда разрабатываете, то еще на уровне самого приложения необходимо исключать и отсекать такие варианты входящих данных. И это совершено не задача движка.

Движок это инструмент разработчика и как любой подобный инструмент имеет документацию по использованию(мы ее допилим). Любые попытки подать на вход некоректные данные должны вызывать ошибки и остановку работы движка.
Простой пример, посмотрите какую либо нормально оформленную библиотеку написанную на PHP. Там везде идет проверка на валидность данных переданных в методы. Если данные не валинды у вас вылазиет Exception что является правильным решением.

По этому приведение движка к строгой типизации будет совершено логично. Движок не должен повторять гибкости PHP, так как он по сути служит прослойкой между PHP и С++ Qt.  Да и не возможно оставить гибкость из-за того что есть слабо типизированный язык PHP и сильно типизированный C++. Второй по сути является конечным исполнителем вашего кода, пусть и интерпритированного.

P.S. Потеря в гибкости не так сильно скажется как потеря в производительности и скорости
PQStudio Development Progress: 75%
Version: 0.2

rinart73
Аватара пользователя
 
Сообщения: 36
Skype: art7emw
Поблагодарили: 2 раза
Ну, учитывая, что все существующие языки, имеющие прослойку, соединяющую с Qt сильно/строго типизированы (я сейчас не про "only for QML"), думаю, и правда лучше в отношении обращений к Qt сделать строгую типизацию.

Просто тем JavaScript/PHP и хороши - можно полентяйничать и не беспокоиться особо о типах.

Но учитывая текущую разницу в производительности... Лучше потом кто-то, кому это не понравится, напишет поверх библиотеку, если что.
Почетный страдалец фигней
Пилю load_ui_file :good2:


Вернуться в Идеи проектов

Кто сейчас на конференции
Сейчас этот форум просматривают: нет зарегистрированных пользователей и 2 гостя
cron