Здравствуйте, kov_serg, Вы писали:
_>Как такое написать по Сиплюсплюсному что бы без UB
Никак. То, что ты сделал — это offsetof, только через... ну ты понял. А offsetof без UB можно использовать только с POD типами (standard layout, как минимум).
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>Никак. То, что ты сделал — это offsetof, только через... ну ты понял. А offsetof без UB можно использовать только с POD типами (standard layout, как минимум).
Хочется вложенных классов реализующих интерфейс, как innerClass в java, но без лишних указателей. Неужели великий и ужасный c++ так не может?
Здравствуйте, kov_serg, Вы писали:
_>Хочется вложенных классов реализующих интерфейс, как innerClass в java, но без лишних указателей. Неужели великий и ужасный c++ так не может?
А для тех, кто пишет на C++ как на java, никто удобств не обещал.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, B0FEE664, Вы писали:
BFE>Тупое решение "в лоб":
Дык хотелось не заводить лишних указателей.
BFE>А зачем так издеваться над объектно ориентированным подходом?
Очень просто это указатель группу функций — переходник такой.
Идея примерно такая:
struct Listner {
virtual void begin(void *args)=0;
virtual int progress(double p)=0;
virtual void end(void *res)=0;
};
struct Worker { /*...*/ }; // постепенно исполняемая функция, иногда требующая осязаемого времени на выполнениеstruct Workers { /*...*/ }; // выполняет постепенно исполняемые функции в разных потоках, полю имеет очереть сообщений для коммуникации и сигналы для аварийной связиstruct Action1Args { string query; };
struct Action1Result { string result; };
struct Action1Worker : Worker { /*...*/ }; // например db querystruct Action2Args { int a,b; };
struct Action2Result { int c,f; };
struct Action2Worker : Worker { /*...*/ }; // или какой-нибудь https requeststruct GUIWindow {
struct:Listner {
GUIWindow* parent() { return parent_of(this,&GUIWindow::action1); }
void begin(void *args) { parent()->action1_begin((Action1Args*)args); }
int progress(double p) { return parent()->action1_progress(p); }
void end(void *res) { parent()->action1_end((Action2Result*)res); }
} action1[1];
struct:Listner {
GUIWindow* parent() { return parent_of(this,&GUIWindow::action2); }
void begin(void *args) { parent()->action2_begin((Action2Args*)args); }
int progress(double p) { return 0; }
void end(void *res) { parent()->action2_end((Action2Result*)res); }
} action2[1];
Workers workers[1];
Action1Worker a1w[1];
Action2Worker a2w[1];
enum { action1_id,action2_id };
void init() {
workers->set(action1_id,action1,a1w); // что-бы тут не передавать кучу функций
workers->set(action2_id,action2,a2w); // или не делать одну но с длинным switch и кучей event codes
};
void done() {
workers->done();
}
// ...void on_action1() { workers->start(action1_id); }
void on_action1_cancel() { workers->cancel(action1_id); }
void on_action2() { workers->start(action2_id); }
void on_action2_cancel() { workers->cancel(action2_id); }
void on_msg_loop() { workers->msg_loop(); } // тут собственно диспечеризируем сообщения по функциям (в основном потоке)
// ...void enable_action1(bool en) {
btn_action1.Enable(en);
btn_action1_cancel.Enable(!en);
progress.Show(!en);
}
void action1_begin(Action1Args *args) {
args->query=edQuery.Text;
enable_action1(false);
}
int action1_progress(double p) {
action1_progress.SetProgess(p);
return 0;
}
void action1_end(Action1Result *res) {
edResult.SetText(res->result);
enable_action1(true);
}
void action2_begin(Action2Args *args) {
args->a=10;
args->b=22;
action2_spinner.Active(true);
}
void action2_end(Action2Result *res) {
lbResult=fmt("result=%d",res->c);
action2_spinner.Active(false);
}
//...
};
Здравствуйте, kov_serg, Вы писали:
BFE>>Тупое решение "в лоб": _>Дык хотелось не заводить лишних указателей.
Это в прошлом веке имело иногда смысл экономить на указателях, а современном мире экономить на указателях — это себя не уважать.
BFE>>А зачем так издеваться над объектно ориентированным подходом? _>Очень просто это указатель группу функци — переходник такой.
Ну так и зачем эти извращения?
Если вам нужен указатель на группу функций, ну так и заведите группу функций и возьмите от неё указатель.
А вообще, здесь где-то нужен полиморфизм вида signal-slot (или шаблонные виртуальные функции, которые в C++ все еще не могут добавить из-за косности мышления)
Здравствуйте, B0FEE664, Вы писали:
BFE>Ну так и зачем эти извращения? BFE>Если вам нужен указатель на группу функций, ну так и заведите группу функций и возьмите от неё указатель. BFE>
BFE>А вообще, здесь где-то нужен полиморфизм вида signal-slot (или шаблонные виртуальные функции, которые в C++ все еще не могут добавить из-за косности мышления)
В C было просто достаточно двух указателей и кучи констант и никаких тебе UB
Вариант с лямбдами в std::function ещё более ужасен (на мой взгляд) чем другие альтернативы
Мой вариант работает достаточно хорошо и локанично, не смотря на то что это не достаточно сиплюсплюснуто.
А как написать parent_of следуя букве е6@нyтblх современных стандартов, я хз.
Здравствуйте, kov_serg, Вы писали:
BFE>>А вообще, здесь где-то нужен полиморфизм вида signal-slot (или шаблонные виртуальные функции, которые в C++ все еще не могут добавить из-за косности мышления) _>В C было просто достаточно двух указателей и кучи констант и никаких тебе UB
До тех пор, пока в арифметике операций с указателями не ошибся.
_>Вариант с лямбдами в std::function ещё более ужасен (на мой взгляд) чем другие альтернативы Если применять патерны C на С++, то так и получается.
А вот если использовать нормальный патерн, вроде signal-slot или, там std::future / std::async / std::promise, то код будет выглядеть совсем иначе.
_>Мой вариант работает достаточно хорошо и локанично, не смотря на то что это не достаточно сиплюсплюснуто.
Вариант, как вариант, видал я и позабористей. А вот то, что хорош — нет: если GUIWindow умрет раньше Workers, то это "всё", "падаем".
_>А как написать parent_of следуя букве е6@нyтblх современных стандартов, я хз.
Известно как — запомнить указатель и вернуть его.
Хотите экономить на спичках, пишите в C стиле: без виртуальности и наследований.
Здравствуйте, B0FEE664, Вы писали:
BFE> или шаблонные виртуальные функции, которые в C++ все еще не могут добавить из-за косности мышления
А для чего они могут понадобиться? И как это должно выглядеть?
Я вообще считаю то что C++ иногда скрывает слишком много деталей там где это не нужно. И вводит избыточные понятия там где совершенно не нужно.
Например таблицы виртуальных функций, состояние корутин, вводит не предсавимые значения для типов (на пример если байт это 8бит то в C++ он имеет 257 значений), который существуют паралельно с типом (pointer provenance), делает выводы из ложных предположений. Вообще иногда творит дичь, прикрываясь тем что это дозволено по корану стандарту и никак иначе пророк не велит поступать никак нельзя иначе оптимизировать не получится.
Здравствуйте, B0FEE664, Вы писали:
BFE>До тех пор, пока в арифметике операций с указателями не ошибся.
А чего бы это. Если не нравятся указатели всегда можно перейти к handles(ухватам) они гарантированно могут быть валидированы в любой момент.
BFE> Если применять патерны C на С++, то так и получается. BFE>А вот если использовать нормальный патерн, вроде signal-slot или, там std::future / std::async / std::promise, то код будет выглядеть совсем иначе.
signal-slot как в qt забабахав предкомпиляцию, что бы весь треш спрятать под ковёр.
futute/promise никаких гарантий не дают и общаться с рабочим потоком прдётся через свои прослойки. Если можете показать как это элегантно сделать — покажите.
BFE>Вариант, как вариант, видал я и позабористей. А вот то, что хорош — нет: если GUIWindow умрет раньше Workers, то это "всё", "падаем".
нет. Тут как раз при убийстве GUIWindow вызывается done он посылает всем работникам сигнал отмены, и ждёт завершениея и диспетчиризирует остатки сообщений, при этом гарантировано если был вызван begin будет вызван end.
И все работники гарантировано освободят ресурсы, у них тоже довольно структурированный вид, и они не подозревают в каком потоке работают.
BFE>Известно как — запомнить указатель и вернуть его. BFE>Хотите экономить на спичках, пишите в C стиле: без виртуальности и наследований.
Причем тут виртуальность и наследование. Просто в C++ нет единообразия, для разных сущностей всё делается по своему. Так и указатели на члены класса это отдельная песня.
Здравствуйте, kov_serg, Вы писали:
BFE>> Если применять патерны C на С++, то так и получается. BFE>>А вот если использовать нормальный патерн, вроде signal-slot или, там std::future / std::async / std::promise, то код будет выглядеть совсем иначе. _>signal-slot как в qt забабахав предкомпиляцию, что бы весь треш спрятать под ковёр.
Не надо как в Qt. Qt отягощён своим наследием и к использованию совсем не обязателен. На современном C++ патерн signal-slot пишется за пару дней, если не заворачиваться со скоростью.
_>futute/promise никаких гарантий не дают и общаться с рабочим потоком прдётся через свои прослойки. Если можете показать как это элегантно сделать — покажите.
Да, через свои прослойки.
BFE>>Вариант, как вариант, видал я и позабористей. А вот то, что хорош — нет: если GUIWindow умрет раньше Workers, то это "всё", "падаем". _>нет. Тут как раз при убийстве GUIWindow вызывается done он посылает всем работникам сигнал отмены,...
Из кода не видно, что это делается автоматически (например в деструкторе workers done() вызваться не может (т.е. может, но приведёт к падению)), так что скорее всего руками, а значит можно забыть прописать...
_>И все работники гарантировано освободят ресурсы, у них тоже довольно структурированный вид, и они не подозревают в каком потоке работают.
Что означает, что работники не могут одновременно работать на несколько окон.
_>Просто в C++ нет единообразия, для разных сущностей всё делается по своему.
Без микроменеджмента эффективности не будет. Где всё единообразно, там медленно.
Здравствуйте, B0FEE664, Вы писали:
_>>И все работники гарантировано освободят ресурсы, у них тоже довольно структурированный вид, и они не подозревают в каком потоке работают. BFE>Что означает, что работники не могут одновременно работать на несколько окон.
Обработчики выполняют конкретное действие. Другие окна могут использовать уведомления (Notifications) для отображения моделий происходящих процессов если это необходимо. При этом количество окон не ограничено.
Это не threadpool это именно действие, как вызов функции. Просто это постепенно исполняемые функции. Если её вызываю раньше чем она закончилась, сначала отменяется текущая, и затем запускается новая.
Например обработка поиска, на вход поступает запрос, и по мере исполнения заполняется список, при этом в любой момент можно прервать и выполнить новый запрос.
Если надо кучу параллельных запросов сделать — делается пачка таких функций и каждая их них мапится в свою модель, которая оповещает подпищиков. Главная задача скрыть многопоточность.
_>>Просто в C++ нет единообразия, для разных сущностей всё делается по своему. BFE>Без микроменеджмента эффективности не будет. Где всё единообразно, там медленно.
Эффективность и скорость это разные понятия. Эффективно это с высоким кпд, а быстро это не обязательно эффективно.
Ничто не мешает на медленном питоне построить вычислительный граф и потом его запулить на gpu. Что выполнит вычиления на порядки быстрее чем на cpu.
Я про другое, должна быть возможность любой сущности дать имя и по этому имени работать с ней. В C++ это не так, вы даже некоторым сущностям имя не можете дать, или хотя бы ссылку. Не говоря о том чо там наплодили вагон и маленькую тележку сущностей для которых немененно мелких нюансов. В результате создаём ненужные сложности, которые затем вынуждены героически преодолевать.
Здравствуйте, kov_serg, Вы писали:
BFE>> или шаблонные виртуальные функции, которые в C++ все еще не могут добавить из-за косности мышления _>А для чего они могут понадобиться? И как это должно выглядеть?
struct Action1 : Listner
{
virtual void begin(Action1Args* arg) { /* some usefull code */ };
virtual void begin(Action2Args* arg) { /* some usefull code */ };
virtual void begin(Action3Args* arg) { /* some usefull code */ };
// если перегрузка, скажем, для Action4Args* не найдена, то ошибка линковкиvirtual int progress(double p) { /* some usefull code */ };
virtual void end(Action1Result* arg) { /* some usefull code */ };
virtual void end(Action2Result* arg) { /* some usefull code */ };
virtual void end(Action3Result* arg) { /* some usefull code */ };
// если перегрузка не найдена, то ошибка линковки
};
Прошу заметить, что сам Listner не шаблонный, а значит указатели на объекты этого типа можно сложить в контейнер типа std::vector<Listner*>
В таком случае у Listner либо динамическая таблица виртуальных функций, либо таблица виртуальных функций формируется во время линковки, а не компиляции. Писатели компиляторов от такого сильно страдают: у них либо шаблон рвётся, либо мозг взрывается, либо это противоречит их философии раздельной компиляции и следовательно не существует в их мире.
Поэтому время от времени, читая чужой код, я сталкиваюсь с той или иной инкарнацией этой идеи, чаще с использованием старателя типа, чем в виде массивов функций. (Никогда не встречал в промышленном коде в виде двойной диспетчеризации, но теоретически это тоже самое и тоже вручную, когда шаблонную функцию расписывают в набор нешаблонных...)
Говорят, что Страуструп пытался что-то замутить с обобщёнными функциями, что связано с шаблонными виртуальными, но как-то мало новостей по этой теме...
Здравствуйте, B0FEE664, Вы писали:
BFE>>> или шаблонные виртуальные функции, которые в C++ все еще не могут добавить из-за косности мышления _>>А для чего они могут понадобиться? И как это должно выглядеть?
BFE>Взглянем на BFE>
BFE>Почему void *args и void *res? Потому, что это стирание типа. С потерей строгости.
Да это убирание типа за ненадобностью. Это не потеря строгости, а приобретение гибкости. Строгость обеспечивается на другом уровне.
BFE>Как могло бы быть? BFE>Я считаю, что, например, так: BFE>
BFE>struct Action1 : Listner
BFE>{
BFE> virtual void begin(Action1Args* arg) { /* some usefull code */ };
BFE> virtual void begin(Action2Args* arg) { /* some usefull code */ };
BFE> virtual void begin(Action3Args* arg) { /* some usefull code */ };
BFE> // если перегрузка, скажем, для Action4Args* не найдена, то ошибка линковки
BFE> virtual int progress(double p) { /* some usefull code */ };
BFE> virtual void end(Action1Result* arg) { /* some usefull code */ };
BFE> virtual void end(Action2Result* arg) { /* some usefull code */ };
BFE> virtual void end(Action3Result* arg) { /* some usefull code */ };
BFE> // если перегрузка не найдена, то ошибка линковки
BFE>};
BFE>
BFE>Прошу заметить, что сам Listner не шаблонный, а значит указатели на объекты этого типа можно сложить в контейнер типа std::vector<Listner*>
На самом деле у ActionN всегда есть вход и выход. Явный тип. При этом задача переходника просто преобразовать типы указателей с наименьшим гемороем. Более того есть некоторые договорённости (контракты) которые
должны соблюдасть обеими сторонами. Компилятор в подобными сущностями не оперирует и будет только мешать при избыточной назойливости.
BFE>В таком случае у Listner либо динамическая таблица виртуальных функций, либо таблица виртуальных функций формируется во время линковки, а не компиляции. Писатели компиляторов от такого сильно страдают: у них либо шаблон рвётся, либо мозг взрывается, либо это противоречит их философии раздельной компиляции и следовательно не существует в их мире. BFE>Поэтому время от времени, читая чужой код, я сталкиваюсь с той или иной инкарнацией этой идеи, чаще с использованием старателя типа, чем в виде массивов функций. (Никогда не встречал в промышленном коде в виде двойной диспетчеризации, но теоретически это тоже самое и тоже вручную, когда шаблонную функцию расписывают в набор нешаблонных...)
BFE>Говорят, что Страуструп пытался что-то замутить с обобщёнными функциями, что связано с шаблонными виртуальными, но как-то мало новостей по этой теме...
Так это можно реализовать на c++ и виртуальность там вообще не нужна. Будет просто динамическая диспечеризация.
Схематично: Делается вардик шаблонная функция котора получает свой typeinfo (использует его как ключ) сеариализует все свои параметры и ключ в пакет (packet/parcel/string ...) не суть.
Далее делаем мапу где ключ typeinfo метода и заполняем обработчики. Обработчик должен десериализовать параметры и вызвать функцию обработчик.
Но возникает куча засад с приведением типов. Поэтому такой подход будет крайне хреновым. Или придётся приводить ключи и потом типы что крайне накладно и не очевидно и не однозначно. Кучу типов не серализуются в лоб или вообще только с посредниками. В общем сразу нафиг.
Гораздо проще просто сделать диспечеризацию удалённых вызовов без такого гемороя и сериализовать и парсить аргументы для заранее определённого типа сигнатур с уникальными id-никами. Короче некий ограниченный апи.
В самом общем виде обработчик для обычных функций будет такой:
typedef int (*dispatch_fn)(void *ctx,Packet *result,Packet *input);