Anatoly
🐙 Antony
@antoshkka вы знакомы с концепцией reactive programming?
Вот сейчас прочитал вики и ознакомился. А что?
Constantine
дело в том, что эта концепция удивительно хорошо работает в определенных задачах, при этом для этих приложений нужно написать намного меньше, чем все эти библиотеки
Constantine
идея примерно в следующем: сделать условное reactive_value<T> [реактивная функция, вычисляющая T без использования параметров] в MoveConstructible виде, отдельные точки ветвления (когда нужно копировать) и источник (точка входа "грязного" значения)
Constantine
при помощи std::invoke/std::apply/какой-то матери можно написать небольшую обертку, позволяющую строить reactive_value из reactive-параметров и callable-объекта и на этом реализация заканчивается
Aidar
Может быть много таких объектов
Aidar
На один участок памяти
Aidar
Это потенциальный баг в апи
Constantine
после этого, когда условная гуи-кнопка получает снаружи значение is-default она получает reactive_value<bool> is_default и вешает оповещение/пишет чистый render
Constantine
я могу рассказать идеи и подводные камни, но я не могу писать public domain реализацию
Constantine
потому как я по сути это реализовал на рабочем проекте
Aidar
Этож обсуждения
Aidar
А не пропосал
Aidar
Пусть пролайкают для начала главное опиши нормально
Constantine
там нет необходимости расширять стандарт, это все замечательно реализуется на C++17
🐙 Antony
я могу рассказать идеи и подводные камни, но я не могу писать public domain реализацию
Яб хотел посмотреть на реализацию в виде генератора: reactive_value<T> my_reactive_function(int& , std::string&, bla-bla-bla) { while (1) co_yeld your_expression_goes_here; }
🐙 Antony
там нет необходимости расширять стандарт, это все замечательно реализуется на C++17
Да, но вот готовый reactive_value хотелось бы видеть в стандартной библиотеке
🐙 Antony
ну или в Boost, для начала
🐙 Antony
О! Даже работает: https://wandbox.org/permlink/FG439ozyfEJG9QHA
Constantine
грубо говоря, реализации на динамических типах это выглядит как reactive_value<int> operator + (reactive_value<int> a, reactive_value<int> b) { return reactive::make(std::move(a), std::move(b), std::plus<int, int>()); }
Constantine
грубо говоря модель построена точно так же, как если бы вам нужно было выводить future<int> operator + (future<int>, future<int>)
Constantine
Динамические типы мне делают звучать опасно.
динамические здесь def. через динамический полиморфизм; reactive_value это что-то вроде unique_ptr на интерфейс с двумя методами - прочитать и перезаписать слот, хранящий вызываемое оповещение
Constantine
MoveConstructible делается чтобы не было вопросов со временем жизни и не надо было отписываться
Constantine
если условный Button хранит и слушает условное IsDefault, то он может безопасно передавать this при прослушивании оповещения, потому что IsDefault вместе с callable оповещения будет уничтожено деструктором Button
🐙 Antony
мнеб прям кусок кода, чтобы видеть использование и начальную инициализацию
Constantine
мне бы чуть более подробное описание, что вы хотите видеть
🐙 Antony
Например - смотрим на изменения двух переменных a и b. При их изменении - выводим сумму
Anonymous
😉Всем драсте, никому в команду или на проект не нужен начинающий разрабочик C/C++/C# ? на НЕ коммерческой основе, а для набора опыта. Если такие есть, то прошу в ЛС:)
☕️
😉Всем драсте, никому в команду или на проект не нужен начинающий разрабочик C/C++/C# ? на НЕ коммерческой основе, а для набора опыта. Если такие есть, то прошу в ЛС:)
добро пожаловать на гитхаб. там куча проектов с кучей разных задач под любой уровень и ориентацию разработчика
☕️
всяко лучше чем проекты анонов запиливать
Constantine
Например - смотрим на изменения двух переменных a и b. При их изменении - выводим сумму
struct storage { reactive_source<int> a, b; //для DefaultConstructible объекта source будет DefaultConstructible; source по сути заменяет переменную reactive_value<int> create_sum_value() { //создающая функция как форма источника значения return reactive::make( create(a), create(b), //синтаксис через унифицированные функции, я фанат этого ;) [](int a, int b) { return a + b; } ); }; void magic_action() { set_checked(a, 10); } }; struct outputter { void gogogo(reactive_value<int> value) { value_ = std::move(value); if (value_) { value_->reset_callback([this](){on_value_changed();}); on_value_changed(); //хех, пропустить это - типичная бажка, правда, быстро отлавливается } } void on_value_changed() { std::cout << value->get() << std::endl; //деталька: чтение при оповещении подразумевает не-cached модель вычисления; услышанное изменение reactive_cached_value должно откладывать чтение } reactive_value<int> value_; }; struct app { storage storage_; outputter outputter_; app() { outputter.gogogo(storage_.create_sum_value()); } }; void main() { app app_; //выводит 0 app_.storage_.magic_action(); //выводит 10 }
Constantine
собственно, вся эта модель пытается полностью отделить outputter от источника значения
Constantine
/*деталька: чтение при оповещении подразумевает не-cached модель вычисления; услышанное изменение reactive_cached_value должно откладывать чтение*/ вот эта деталька вызвана тем, что можно сделать вычисление ромбом и корень ромба будет слышать оповещение до правой ветки; ничего не упадет, просто performance bug, поскольку корень перевычисляться два раза будет, если правая ветка сбрасывает кэш по оповещению и достает значение из кэша
🐙 Antony
А чем от сигнал/слотов отличается?
Constantine
я не самый большой специалист по сигнал-слотам, но сигнал подразумевает, как я понимаю, передачу значения
🐙 Antony
А что тут происходит: if (value_) { value_->reset_callback([this](){on_value_changed();}); on_value_changed(); //хех, пропустить это - типичная бажка, правда, быстро отлавливается } } Зачем второй вызов on_value_changed(); ?
Constantine
до первого чтения и в промежуток между оповещением и следующим чтением нет гарантии вызова оповещения
Constantine
например, если записано && и первое значение в false, то второй операнд не читают и поэтому его не вычисляют и его изменения не несут смысловой нагрузки
Constantine
формально говоря, вы не знаете, что лежит в значении, пока его не читали и поэтому какая вам разница, что оно изменилось
Constantine
собственно из всей реализации здесь опущены еще две версии фабрики по прототипу (с точки зрения reactive programming - динамическая версия) в shared и не shared версии
Constantine
они нужны, например, вот в такой конструкции reactive_source<int> dpi_system_; reactive_proto_factory<int> dpi_factory; //раздаем отсюда, инициализируем на create(dpi_system_); если приходит дебажка и хочет dependency inject на другой способ вычисления, перезаписываем прототип в фабрике, тем самым дергая все оповещения и полностью подменяя способ вычисления значения
melvin
эй
melvin
у меня столько вопросов было
melvin
эх
Anonymous
Давай задавай
Dr. Friedrich
Можешь ему задавать, он не забанен (пока что).
Constantine
а я уже хотел задать вопрос, каким образом реализовать shared_ptr так, чтобы shared_ptr<Derived> был совместимым типом с shared_ptr<Base>
Constantine
и каким образом structure bracet init мог бы передаваться через perfect forwarding :)
Anonymous
Страх, он такой
Anonymous
Видно нубы
Dr. Friedrich
Anonymous
а зачем сообщения удалять
Constantine
мм было бы неплохо это все
в данном случае мне нужно найти еще одного адепта, чтобы он все это написал
Anonymous
я хочу посмотреть, что он слал
Constantine
я хочу посмотреть, что он слал
он вызывающе указывал, что он - д'артаньян, а мы все - известно кто
Anonymous
вот можно конкретно сообщение за которое он получил этот варн?
Anonymous
жуть как интересно
Dr. Friedrich
Юзер после бана удалился, кстати.
Pavel
Ага
Pavel
Есть такое
Pavel
Обиженка
Constantine
мм было бы неплохо это все
со своей стороны могу сказать, что у меня написан GUI-рендерер с полным расчетом сцены через такую штуку и это на самом деле работает ;)
🐙 Antony
Через сигнал слоты поробовал сделать аналогичное: struct storage { int a, b; boost::signal<void(int)> onChange; void fire() { onChange(a + b); } void magic_action() { a = 10; fire(); } }; struct outputter { void gogogo(storage& ы) { s.signal.connect(on_value_changed); s.fire(); } void on_value_changed(int value) { std::cout << value << std::endl; } }; struct app { storage storage_; outputter outputter_; app() { outputter.gogogo(storage_.onChange); } }; void main() { app app_; //выводит 0 app_.storage_.magic_action(); //выводит 10 } Чувствую что упустил что-то важное...
Constantine
Хотя некоторые тонкости будут присутствовать: например, вряд ли вам будет комфортно при реализации примера с &&, видимо, вам придется блокировать пробрасывание оповещения
Constantine
storage помрет после outputter, при этом storage держит connect на outputter
Constantine
на меня за такое дебажка бы сразу начала плевать
Anonymous
доброго
брат
тебе того же