├── .gitmodules ├── .travis.yml ├── Jamroot.jam ├── README.md ├── Some fun with Reactive Programming in C++17.pdf ├── appveyor.yml ├── classify.cpp ├── event_basic.cpp ├── function_basic.cpp ├── function_decomposed.cpp ├── function_pipe.cpp ├── matrix.cpp ├── newton_raphson.cpp └── urp.hpp /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "telegram-reactive"] 2 | path = telegram-reactive 3 | url = https://github.com/Manu343726/telegram-reactive 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaquintides/usingstdcpp2019/381ccb9ebc0a420a9d37372758aa6a8849946598/.travis.yml -------------------------------------------------------------------------------- /Jamroot.jam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaquintides/usingstdcpp2019/381ccb9ebc0a420a9d37372758aa6a8849946598/Jamroot.jam -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaquintides/usingstdcpp2019/381ccb9ebc0a420a9d37372758aa6a8849946598/README.md -------------------------------------------------------------------------------- /Some fun with Reactive Programming in C++17.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaquintides/usingstdcpp2019/381ccb9ebc0a420a9d37372758aa6a8849946598/Some fun with Reactive Programming in C++17.pdf -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaquintides/usingstdcpp2019/381ccb9ebc0a420a9d37372758aa6a8849946598/appveyor.yml -------------------------------------------------------------------------------- /classify.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2019 Joaquin M Lopez Munoz. 2 | * Distributed under the Boost Software License, Version 1.0. 3 | * (See accompanying file LICENSE_1_0.txt or copy at 4 | * http://www.boost.org/LICENSE_1_0.txt) 5 | * 6 | * See https://github.com/joaquintides/usingstdcpp2019 for talk material. 7 | */ 8 | 9 | #include 10 | #include 11 | #include "urp.hpp" 12 | 13 | int main() 14 | { 15 | using namespace usingstdcpp2019::urp; 16 | 17 | trigger s; 18 | auto res=hold( 19 | s|group_by([](const std::string& str){return str.c_str()[0];}) 20 | |map([](auto e){ 21 | return hold(std::move(e)|collect()); 22 | }) 23 | |collect() 24 | ); 25 | 26 | auto names={ 27 | "John","Jack","Susan","Mary","Anne","Anthony","Bjarne","Margaret", 28 | "George","Barack","Sarah","Peter","Hillary","Ronda","Alice","Herbert", 29 | }; 30 | for(const auto& str:names)s=str; 31 | 32 | for(const auto& e:res.get()){ 33 | for(const auto& str:e.get())std::cout< 10 | #include 11 | #include "urp.hpp" 12 | 13 | int main() 14 | { 15 | using namespace usingstdcpp2019::urp; 16 | 17 | trigger s; 18 | auto n=s|map([](const std::string& s){return s.size();}); 19 | auto e=combine(s,n) 20 | |filter([](const auto& p){return std::get<1>(p)>=4;}) 21 | |map([](const auto& p){return std::get<0>(p);}) 22 | |accumulate(std::string{},std::plus<>{}); 23 | 24 | e.connect([](const auto&,const std::string& str){ 25 | std::cout< 10 | #include "urp.hpp" 11 | 12 | int main() 13 | { 14 | using namespace usingstdcpp2019::urp; 15 | 16 | value x=0,y=0; 17 | auto z=(x*x)+y+1; 18 | 19 | x=6; 20 | y=5; 21 | std::cout<<"z="< 10 | #include 11 | #include "urp.hpp" 12 | 13 | int main() 14 | { 15 | using namespace usingstdcpp2019::urp; 16 | 17 | value x=0,y=0; 18 | function w={std::multiplies<>{},x,x}; 19 | function v={std::plus<>{},w,y}; 20 | function z={[](int v){return v+1;},v}; 21 | 22 | // this is functionally equivalent 23 | function z1={[](int x,int y){return x*x+y+1;},x,y}; 24 | 25 | x=6; 26 | y=5; 27 | std::cout<<"z ="< 10 | #include "urp.hpp" 11 | 12 | int main() 13 | { 14 | using namespace usingstdcpp2019::urp; 15 | 16 | value x=0; 17 | 18 | auto f=[](int x){return x+1;}; 19 | auto g=[](int x){return 2*x;}; 20 | auto h=[](int x){return x*(x+1);}; 21 | 22 | auto y=x|f; 23 | auto w=y|g; 24 | auto z=w|h; 25 | 26 | auto y1=function{f,x}; 27 | auto w1=function{g,y1}; 28 | auto z1=function{h,w1}; 29 | 30 | auto z2=x|f|g|h; 31 | 32 | auto z3=function{h,function{g,function{f,x}}}; 33 | 34 | x=2; 35 | std::cout<<"z ="< 10 | #include 11 | #include 12 | #include "urp.hpp" 13 | 14 | int main() 15 | { 16 | using namespace usingstdcpp2019::urp; 17 | 18 | using matrix2x2=std::array,2>,2>; 19 | 20 | auto mult=[](auto& m,auto& n){ 21 | auto& [m00,m01]=std::get<0>(m); 22 | auto& [m10,m11]=std::get<1>(m); 23 | auto& [n00,n01]=std::get<0>(n); 24 | auto& [n10,n11]=std::get<1>(n); 25 | 26 | return std::tuple{ 27 | std::tuple{m00*n00+m01*n10,m00*n01+m01*n11}, 28 | std::tuple{m10*n00+m11*n10,m10*n01+m11*n11} 29 | }; 30 | }; 31 | 32 | auto print=[](const auto& m){ 33 | auto& [m00,m01]=std::get<0>(m); 34 | auto& [m10,m11]=std::get<1>(m); 35 | std::cout<< 36 | "/ " < 10 | #include "urp.hpp" 11 | 12 | int main() 13 | { 14 | using namespace usingstdcpp2019::urp; 15 | 16 | value x=1.0; 17 | auto y=x/2+882/x; 18 | 19 | // create a cycle 20 | y.connect([&](const auto& y){x=y.get();}); 21 | 22 | x=10.0; // should segfault, right? 23 | std::cout<<"y="< 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace usingstdcpp2019::urp{ 29 | 30 | namespace detail{ 31 | 32 | template struct overloaded:Ts...{using Ts::operator()...;}; 33 | template overloaded(Ts...)->overloaded; 34 | 35 | template 36 | using node_index_type=std::integral_constant; 37 | 38 | template 39 | class node; 40 | 41 | template 42 | void swap( 43 | node& x,node& y) 44 | { 45 | x.swap(y); 46 | } 47 | 48 | template 49 | class node 50 | { 51 | public: 52 | node()=default; 53 | node(const node&){}; 54 | node(node&& x):sig{std::move(x.sig)}{sig(this);} 55 | 56 | node& operator=(const node&){return *this;} 57 | node& operator=(node&& x) 58 | { 59 | if(this!=&x){ 60 | sig=std::move(x.sig); 61 | sig(this); 62 | } 63 | return *this; 64 | } 65 | 66 | void swap(node& x) 67 | { 68 | if(this!=&x){ 69 | sig.swap(x.sig); 70 | sig(this); 71 | x.sig(&x); 72 | } 73 | } 74 | 75 | template 76 | auto connect(const Slot& s) 77 | { 78 | return connect_node([=](auto arg){ 79 | std::visit(overloaded{ 80 | [](node*){}, 81 | [=](auto& sigargs){std::apply(s,sigargs);} 82 | },arg); 83 | }); 84 | } 85 | 86 | protected: 87 | void signal(SigArgs... sigargs) 88 | { 89 | sig(std::forward_as_tuple(std::forward(sigargs)...)); 90 | } 91 | 92 | auto get_srcs()const noexcept{return std::tuple{};} 93 | 94 | private: 95 | template friend class node; 96 | 97 | using extended_signature=void(std::variant>); 98 | 99 | template 100 | auto connect_node(const Slot& s){return sig.connect(s);} 101 | 102 | boost::signals2::signal sig; 103 | }; 104 | 105 | template 106 | class node: 107 | public node 108 | { 109 | using super=node; 110 | 111 | public: 112 | node(Srcs&... srcs):srcs{&srcs...}{} 113 | node(const node& x):super{x},srcs{x.srcs}{} 114 | node(node&& x):super{std::move(x)},srcs{x.srcs}{x.disconnect_srcs();} 115 | template 116 | explicit node(node&& x): 117 | srcs{x.srcs}{x.disconnect_srcs();} 118 | template< 119 | typename Derived2,typename Signature2,typename... Srcs2, 120 | typename Derived3,typename Signature3,typename... Srcs3, 121 | std::enable_if_t< 122 | std::is_same_v 123 | >* =nullptr 124 | > 125 | node( 126 | node&& x, 127 | node&& y): 128 | srcs{std::tuple_cat(x.srcs,y.srcs)} 129 | {x.disconnect_srcs();y.disconnect_srcs();} 130 | ~node(){disconnect_srcs();} 131 | 132 | node& operator=(const node& x) 133 | { 134 | if(this!=&x){ 135 | base()=x; 136 | srcs=x.srcs; 137 | disconnect_srcs(); 138 | conns=connect_srcs(); 139 | } 140 | return *this; 141 | } 142 | 143 | node& operator=(node&& x) 144 | { 145 | if(this!=&x){ 146 | base()=std::move(x); 147 | srcs=x.srcs; 148 | disconnect_srcs(); 149 | conns=connect_srcs(); 150 | x.disconnect_srcs(); 151 | } 152 | return *this; 153 | } 154 | 155 | void swap(node& x) 156 | { 157 | if(this!=&x){ 158 | base().swap(x.base()); 159 | std::swap(srcs,x.srcs); 160 | disconnect_srcs(); 161 | conns=connect_srcs(); 162 | x.disconnect_srcs(); 163 | x.conns=x.connect_srcs(); 164 | } 165 | } 166 | 167 | protected: 168 | auto& get_srcs()const noexcept{return srcs;} 169 | 170 | private: 171 | template friend class node; 172 | 173 | super& base()noexcept{return *this;} 174 | Derived& derived()noexcept{return static_cast(*this);} 175 | 176 | auto connect_srcs() 177 | { 178 | return connect_srcs(std::make_index_sequence{}); 179 | } 180 | 181 | #if defined(_MSC_VER) 182 | 183 | template 184 | struct slot 185 | { 186 | template 187 | void operator()(Arg arg)const{ 188 | std::visit(overloaded{ 189 | [this](auto* p){ 190 | auto& src=std::get(this_->srcs); 191 | src=static_cast>(p); 192 | }, 193 | [this](auto& sigargs){ 194 | std::apply([this](auto&&... sigargs){ 195 | this_->derived().callback( 196 | node_index_type{}, 197 | std::forward(sigargs)...); 198 | },sigargs); 199 | } 200 | },arg); 201 | } 202 | 203 | node* this_; 204 | }; 205 | 206 | template friend struct slot; 207 | 208 | template 209 | auto connect_srcs(std::index_sequence) 210 | { 211 | return std::array{std::get(srcs)->connect_node(slot{this})...}; 212 | } 213 | 214 | #else 215 | 216 | template 217 | auto connect_srcs(std::index_sequence) 218 | { 219 | return std::array{ 220 | std::get(srcs)->connect_node([this](auto arg){ 221 | std::visit(overloaded{ 222 | [this](auto* p){ 223 | auto& src=std::get(srcs); 224 | src=static_cast>(p); 225 | }, 226 | [this](auto& sigargs){ 227 | std::apply([this](auto&&... sigargs){ 228 | derived().callback( 229 | node_index_type{}, 230 | std::forward(sigargs)...); 231 | },sigargs); 232 | } 233 | },arg); 234 | })... 235 | }; 236 | } 237 | 238 | #endif 239 | 240 | void disconnect_srcs() 241 | { 242 | std::apply([](auto&&... conns){(conns.disconnect(),...);},conns); 243 | } 244 | 245 | std::tuple srcs; 246 | std::array conns=connect_srcs(); 247 | }; 248 | 249 | } /* namespace detail */ 250 | 251 | template class function; 252 | 253 | template 254 | class value:public detail::node,void(const value&)> 255 | { 256 | using super=detail::node; 257 | 258 | public: 259 | using value_type=T; 260 | 261 | value(const T& t):t{t}{} 262 | value(const value&)=default; 263 | value(value&&)=default; 264 | 265 | value& operator=(const value& x) 266 | { 267 | if(this!=&x){ 268 | base()=x; 269 | *this=x.t; 270 | } 271 | return *this; 272 | } 273 | 274 | value& operator=(value&& x)=default; 275 | 276 | value& operator=(const T& u) 277 | { 278 | if(!(t==u)){ 279 | t=u; 280 | this->signal(*this); 281 | } 282 | return *this; 283 | } 284 | 285 | value& operator=(T&& u) 286 | { 287 | if(!(t==u)){ 288 | t=std::move(u); 289 | this->signal(*this); 290 | } 291 | return *this; 292 | } 293 | 294 | void swap(value& x) 295 | { 296 | using std::swap; 297 | base().swap(x.base()); 298 | swap(t,x.t); 299 | } 300 | 301 | const T& get()const noexcept{return t;} 302 | 303 | template 304 | auto operator|(F f)&{return function{f,*this};} 305 | 306 | private: 307 | super& base()noexcept{return *this;} 308 | 309 | T t; 310 | }; 311 | 312 | template 313 | void swap(value& x,value& y){x.swap(y);} 314 | 315 | namespace detail{ 316 | 317 | template 318 | auto compose_function(F1 f1,F2 f2) 319 | { 320 | return[=](auto&&... x){ 321 | return f1(f2(std::forward(x)...));}; 322 | } 323 | 324 | template 325 | auto subtuple(Tuple&& t,std::index_sequence) 326 | { 327 | return std::forward_as_tuple(std::get(std::forward(t))...); 328 | } 329 | 330 | template 331 | auto subtuple(Tuple&& t) 332 | { 333 | return detail::subtuple( 334 | std::forward(t),std::make_index_sequence{}); 335 | } 336 | 337 | template< 338 | std::size_t Arity2,std::size_t Arity3, 339 | typename F1,typename F2,typename F3 340 | > 341 | auto compose_function(F1 f1,F2 f2,F3 f3) 342 | { 343 | return[=](auto&&... x){ 344 | auto t=std::forward_as_tuple(std::forward(x)...); 345 | auto t2=detail::subtuple<0,Arity2>(std::move(t)); 346 | auto t3=detail::subtuple(std::move(t)); 347 | return f1(std::apply(f2,t2),std::apply(f3,t3)); 348 | }; 349 | } 350 | 351 | struct identity_f 352 | { 353 | template 354 | auto operator()(const T& x)const{return x;} 355 | }; 356 | 357 | template 358 | auto identity_function(Arg& arg) 359 | { 360 | return function{identity_f{},arg}; 361 | } 362 | 363 | } /* namespace detail */ 364 | 365 | template 366 | class function: 367 | public detail::node< 368 | function,void(const function&),Args... 369 | > 370 | { 371 | using super=detail::node; 372 | 373 | public: 374 | using value_type=decltype(std::declval()(std::declval().get()...)); 375 | 376 | function(F f,Args&... args):super{args...},f{f}{} 377 | function(const function& x)=default; 378 | function(function&& x)=default; 379 | template 380 | function(F1 f1,function&& x): 381 | super{std::move(x)},f{detail::compose_function(f1,x.f)}{} 382 | template< 383 | typename F1,typename F2,typename... Args2,typename F3,typename... Args3 384 | > 385 | function(F1 f1,function&& x,function&& y): 386 | super{std::move(x.base()),std::move(y.base())}, 387 | f{detail::compose_function( 388 | f1,x.f,y.f) 389 | }{} 390 | template 391 | function(F1 f1,function&& x,Arg3& y): 392 | function{f1,std::move(x),detail::identity_function(y)}{} 393 | template 394 | function(F1 f1,Arg2& x,function&& y): 395 | function{f1,detail::identity_function(x),std::move(y)}{} 396 | 397 | function& operator=(const function& x) 398 | { 399 | if(this!=&x){ 400 | base()=x; 401 | f=x.f; 402 | update(); 403 | } 404 | return *this; 405 | } 406 | 407 | function& operator=(function&& x)=default; 408 | 409 | void swap(function& x) 410 | { 411 | using std::swap; 412 | base().swap(x.base()); 413 | swap(f,x.f); 414 | swap(t,x.t); 415 | } 416 | 417 | auto const& get()const noexcept{return t;} 418 | 419 | template 420 | auto operator|(G g)& {return urp::function{g,*this};} 421 | template 422 | auto operator|(G g)&&{return urp::function{g,std::move(*this)};} 423 | 424 | private: 425 | friend super; 426 | template friend class function; 427 | 428 | super& base()noexcept{return *this;} 429 | 430 | template 431 | void callback(Index,const Arg&){update();} 432 | 433 | auto value()const 434 | { 435 | return std::apply([this](const auto&... args){ 436 | return f(args->get()...);},this->get_srcs()); 437 | } 438 | 439 | void update() 440 | { 441 | if(const auto& u=value();!(t==u)){ 442 | t=u; 443 | this->signal(*this); 444 | } 445 | } 446 | 447 | F f; 448 | value_type t=value(); 449 | }; 450 | 451 | template 452 | function(F1,function&&)->function< 453 | decltype(detail::compose_function(std::declval(),std::declval())), 454 | Args... 455 | >; 456 | 457 | template< 458 | typename F1,typename F2,typename... Args2,typename F3,typename... Args3 459 | > 460 | function(F1,function&&,function&&)->function< 461 | decltype( 462 | detail::compose_function( 463 | std::declval(),std::declval(),std::declval())), 464 | Args2...,Args3... 465 | >; 466 | 467 | template 468 | function(F1,function&&,Arg3&)->function< 469 | decltype( 470 | detail::compose_function( 471 | std::declval(),std::declval(),detail::identity_f{})), 472 | Args2...,Arg3 473 | >; 474 | 475 | template 476 | function(F1,Arg2&,function&&)->function< 477 | decltype( 478 | detail::compose_function<1,sizeof...(Args3)>( 479 | std::declval(),detail::identity_f{},std::declval())), 480 | Arg2,Args3... 481 | >; 482 | 483 | template 484 | void swap(function& x,function& y){x.swap(y);} 485 | 486 | namespace detail{ 487 | 488 | template 489 | struct is_function_or_value_impl:std::false_type{}; 490 | template 491 | struct is_function_or_value_impl>:std::true_type{}; 492 | template 493 | struct is_function_or_value_impl>:std::true_type{}; 494 | template 495 | inline constexpr bool is_function_or_value= 496 | is_function_or_value_impl>::value; 497 | 498 | } /* namespace detail */ 499 | 500 | #define USINGSTDCPP2019_URP_DEFINE_UNARY_OP(name) \ 501 | template< \ 502 | typename T, \ 503 | std::enable_if_t>* =nullptr \ 504 | > \ 505 | auto operator name(T&& x) \ 506 | { \ 507 | return function{ \ 508 | [](const auto& x){return name x;}, \ 509 | std::forward(x) \ 510 | }; \ 511 | } 512 | 513 | #define USINGSTDCPP2019_URP_DEFINE_BINARY_OP(name) \ 514 | template< \ 515 | typename T,typename U, \ 516 | std::enable_if_t< \ 517 | !detail::is_function_or_value&& \ 518 | detail::is_function_or_value \ 519 | >* =nullptr \ 520 | > \ 521 | auto operator name(T&& x,U&& y) \ 522 | { \ 523 | return function{ \ 524 | [x=std::forward(x)](const auto& y){return x name y;}, \ 525 | std::forward(y) \ 526 | }; \ 527 | } \ 528 | \ 529 | template< \ 530 | typename T,typename U, \ 531 | std::enable_if_t< \ 532 | detail::is_function_or_value&& \ 533 | !detail::is_function_or_value \ 534 | >* =nullptr \ 535 | > \ 536 | auto operator name(T&& x,U&& y) \ 537 | { \ 538 | return function{ \ 539 | [y=std::forward(y)](const auto& x){return x name y;}, \ 540 | std::forward(x) \ 541 | }; \ 542 | } \ 543 | \ 544 | template< \ 545 | typename T,typename U, \ 546 | std::enable_if_t< \ 547 | detail::is_function_or_value&& \ 548 | detail::is_function_or_value \ 549 | >* =nullptr \ 550 | > \ 551 | auto operator name(T&& x,U&& y) \ 552 | { \ 553 | return function{ \ 554 | [](const auto& x,const auto& y){return x name y;}, \ 555 | std::forward(x),std::forward(y) \ 556 | }; \ 557 | } 558 | 559 | USINGSTDCPP2019_URP_DEFINE_UNARY_OP(+) 560 | USINGSTDCPP2019_URP_DEFINE_UNARY_OP(-) 561 | USINGSTDCPP2019_URP_DEFINE_BINARY_OP(+) 562 | USINGSTDCPP2019_URP_DEFINE_BINARY_OP(-) 563 | USINGSTDCPP2019_URP_DEFINE_BINARY_OP(*) 564 | USINGSTDCPP2019_URP_DEFINE_BINARY_OP(/) 565 | 566 | #undef USINGSTDCPP2019_URP_DEFINE_BINARY_OP 567 | #undef USINGSTDCPP2019_URP_DEFINE_UNARY_OP 568 | 569 | template class event; 570 | 571 | template 572 | class trigger:public detail::node,void(const trigger&,const T&)> 573 | { 574 | using super=detail::node; 575 | 576 | public: 577 | using value_type=T; 578 | 579 | trigger()=default; 580 | trigger(const trigger&)=default; 581 | trigger(trigger&&)=default; 582 | 583 | trigger& operator=(const trigger& x)=default; 584 | trigger& operator=(trigger&& x)=default; 585 | 586 | trigger& operator=(const T& t) 587 | { 588 | this->super::signal(*this,t); 589 | return *this; 590 | } 591 | 592 | template 593 | auto operator|(Slot s)&{return event{s,*this};} 594 | }; 595 | 596 | template 597 | void swap(trigger& x,trigger& y){return x.swap(y);} 598 | 599 | namespace detail{ 600 | 601 | template 602 | class callback_class 603 | { 604 | public: 605 | using value_type=Value; 606 | 607 | callback_class(F f):f{f}{} 608 | callback_class(const callback_class&)=default; 609 | callback_class(callback_class&&)=default; 610 | 611 | callback_class& operator=(const callback_class& x) 612 | { 613 | if(this!=&x){ 614 | if constexpr(std::is_copy_assignable_v){ 615 | f=x.f; 616 | } 617 | else{ /* glorious hack for non-assignable capture lambdas */ 618 | static_assert(std::is_nothrow_move_constructible_v); 619 | auto f2=x.f; 620 | f.~F(); 621 | ::new (&f) F{std::move(f2)}; 622 | } 623 | } 624 | return *this; 625 | } 626 | 627 | template 628 | void operator()(Args&&... args){f(std::forward(args)...);} 629 | 630 | private: 631 | F f; 632 | }; 633 | 634 | template 635 | auto callback(F f){return callback_class{f};} 636 | 637 | template 638 | struct arg 639 | { 640 | const T& get()const; 641 | }; 642 | 643 | template 644 | auto callback_for(Reaction r) 645 | { 646 | return r(arg{}...); 647 | } 648 | 649 | template 650 | using callback_type=decltype(callback_for(std::declval())); 651 | 652 | template 653 | using event_value_type=typename callback_type::value_type; 654 | 655 | template 656 | auto type_passthrough(Callback c) 657 | { 658 | return [=](auto... args){ 659 | return callback...>(c); 660 | }; 661 | } 662 | 663 | template 664 | auto compose_reaction(Reaction r,Callback c) 665 | { 666 | return [=](auto...)mutable{ 667 | auto c2=callback_for(r); 668 | return callback( 669 | [=](auto& sig,auto index,const auto& x)mutable{ 670 | auto sig2=[&](const auto& y){c2(sig,node_index_type<0>{},y);}; 671 | c(sig2,index,x); 672 | } 673 | ); 674 | }; 675 | } 676 | 677 | } /* namespace detail */ 678 | 679 | template 680 | class event:public detail::node< 681 | event, 682 | void( 683 | const event&, 684 | const detail::event_value_type& 685 | ), 686 | Srcs... 687 | > 688 | { 689 | using super=detail::node< 690 | event, 691 | void(const event&,const detail::event_value_type&), 692 | Srcs... 693 | >; 694 | 695 | public: 696 | using value_type=detail::event_value_type; 697 | 698 | event(Reaction r,Srcs&... srcs): 699 | super{srcs...},c{detail::callback_for(r)}{} 700 | event(const event& x)=default; 701 | event(event&& x)=default; 702 | template 703 | event(Reaction1 r1,event&& x): 704 | super{std::move(x)}, 705 | c{detail::callback_for(detail::compose_reaction(r1,x.c))}{} 706 | 707 | event& operator=(const event&)=default; 708 | event& operator=(event&&)=default; 709 | 710 | void swap(event& x) 711 | { 712 | using std::swap; 713 | base().swap(x.base()); 714 | swap(c,x.c); 715 | } 716 | 717 | template 718 | auto operator|(Reaction2 r2)& {return urp::event{r2,*this};} 719 | template 720 | auto operator|(Reaction2 r2)&&{return urp::event{r2,std::move(*this)};} 721 | 722 | private: 723 | friend super; 724 | template friend class event; 725 | 726 | super& base()noexcept{return *this;} 727 | 728 | template 729 | void callback(Index index,Src&,const T& x) 730 | { 731 | auto sig=[this](const value_type& y){this->signal(*this,y);}; 732 | c(sig,index,x); 733 | } 734 | 735 | detail::callback_type c; 736 | }; 737 | 738 | template 739 | event(Reaction1,event&&)->event< 740 | decltype(detail::compose_reaction( 741 | std::declval(), 742 | std::declval>())), 743 | Srcs... 744 | >; 745 | 746 | template 747 | void swap(event& x,event& y){x.swap(y);} 748 | 749 | template 750 | class hold:public detail::node,void(const hold&),Src> 751 | { 752 | using super=detail::node; 753 | 754 | public: 755 | using value_type=typename Src::value_type; 756 | 757 | hold(Src&& src,const value_type& v=value_type{}): 758 | super{src},v{v},src{std::move(src)}{} 759 | hold(const hold& x):hold{Src{x.src},x.v}{} 760 | hold(hold&&)=default; 761 | 762 | hold& operator=(const hold& x) 763 | { 764 | if(this!=&x){ 765 | v=x.v; 766 | src=x.src; 767 | base()=super{src}; 768 | } 769 | return *this; 770 | } 771 | 772 | hold& operator=(hold&& x)=default; 773 | 774 | const value_type& get()const noexcept{return v;} 775 | 776 | template 777 | auto operator|(Slot s)&{return event{s,*this};} 778 | 779 | private: 780 | friend super; 781 | 782 | super& base()noexcept{return *this;} 783 | 784 | template 785 | void callback(Index,const Src&,const value_type& x) 786 | { 787 | v=x; 788 | this->signal(*this); 789 | } 790 | 791 | value_type v; 792 | Src src; 793 | }; 794 | 795 | template 796 | auto merge(Srcs&... srcs) 797 | { 798 | return event{ 799 | detail::type_passthrough([](auto& sig,auto,const auto& x){sig(x);}), 800 | srcs... 801 | }; 802 | } 803 | 804 | template 805 | auto combine(Srcs&... srcs) 806 | { 807 | using value_type=std::tuple; 808 | using cache_type=std::tuple...>; 809 | 810 | return event{ 811 | [=](auto...){return detail::callback( 812 | [os=cache_type{},remaining=sizeof...(Srcs)] 813 | (auto& sig,auto index,const auto& x)mutable{ 814 | auto& o=std::get(os); 815 | if(!o)--remaining; 816 | o=x; 817 | if(!remaining){ 818 | sig(std::apply([](auto&&... os){ 819 | return std::make_tuple(std::move(*os)...); 820 | },os)); 821 | os=cache_type{}; 822 | remaining=sizeof...(Srcs); 823 | } 824 | } 825 | );}, 826 | srcs... 827 | }; 828 | } 829 | 830 | template 831 | auto filter(Pred pred) 832 | { 833 | return detail::type_passthrough( 834 | [=](auto& sig,auto,const auto& x){if(pred(x))sig(x);}); 835 | } 836 | 837 | template 838 | auto map(F f) 839 | { 840 | return [=](auto... args){ 841 | return detail::callback...>( 842 | [=](auto& sig,auto,const auto& x){sig(f(x));}); 843 | }; 844 | } 845 | 846 | template 847 | auto accumulate(T init,BynaryOp op) 848 | { 849 | return [=](auto...){return detail::callback( 850 | [=,res=std::move(init)](auto& sig,auto,const auto& x)mutable{ 851 | res=op(std::move(res),x); 852 | sig(res); 853 | } 854 | );}; 855 | } 856 | 857 | template 858 | auto group_by(F f) 859 | { 860 | return [=](auto... args){ 861 | using arg_type=std::common_type_t; 862 | using key_type=std::common_type_t; 863 | using trigger_type=trigger; 864 | using value_type=decltype(merge(std::declval())); 865 | 866 | return detail::callback( 867 | [=,trgs=std::unordered_map{}] 868 | (auto& sig,auto,const auto& x)mutable{ 869 | const auto& k=f(x); 870 | auto [it,b]=trgs.try_emplace(k); 871 | if(b)sig(merge(it->second)); 872 | it->second=x; 873 | } 874 | ); 875 | }; 876 | } 877 | 878 | auto collect() 879 | { 880 | return [=](auto... args){ 881 | using element_type=std::common_type_t; 882 | using value_type=std::vector; 883 | 884 | return detail::callback( 885 | [v=value_type{}](auto& sig,auto,const auto& x)mutable{ 886 | v.push_back(x); 887 | sig(v); 888 | } 889 | ); 890 | }; 891 | } 892 | 893 | } /* namespace usingstdcpp2019::urp */ 894 | 895 | #endif 896 | --------------------------------------------------------------------------------