// Copyright Sebastian Jeckel 2014. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef REACT_SIGNAL_H_INCLUDED #define REACT_SIGNAL_H_INCLUDED #pragma once #if _MSC_VER && !__INTEL_COMPILER #pragma warning(disable : 4503) #endif #include "react/detail/Defs.h" #include #include #include #include #include "react/Observer.h" #include "react/TypeTraits.h" #include "react/detail/SignalBase.h" /*****************************************/ REACT_BEGIN /*****************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Forward declarations /////////////////////////////////////////////////////////////////////////////////////////////////// template class Signal; template class VarSignal; template class TempSignal; /////////////////////////////////////////////////////////////////////////////////////////////////// /// SignalPack - Wraps several nodes in a tuple. Create with comma operator. /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename ... TValues > class SignalPack { public: SignalPack(const Signal& ... deps) : Data( std::tie(deps ...) ) {} template SignalPack(const SignalPack& curArgs, const Signal& newArg) : Data( std::tuple_cat(curArgs.Data, std::tie(newArg)) ) {} std::tuple& ...> Data; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// With - Utility function to create a SignalPack /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename ... TValues > auto With(const Signal& ... deps) -> SignalPack { return SignalPack(deps ...); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeVar /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename V, typename S = typename std::decay::type, class = typename std::enable_if< ! IsSignal::value>::type, class = typename std::enable_if< ! IsEvent::value>::type > auto MakeVar(V&& value) -> VarSignal { return VarSignal( std::make_shared>( std::forward(value))); } template < typename D, typename S > auto MakeVar(std::reference_wrapper value) -> VarSignal { return VarSignal( std::make_shared>>(value)); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeVar (higher order reactives) /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename V, typename S = typename std::decay::type, typename TInner = typename S::ValueT, class = typename std::enable_if< IsSignal::value>::type > auto MakeVar(V&& value) -> VarSignal> { return VarSignal>( std::make_shared>>( std::forward(value))); } template < typename D, typename V, typename S = typename std::decay::type, typename TInner = typename S::ValueT, class = typename std::enable_if< IsEvent::value>::type > auto MakeVar(V&& value) -> VarSignal> { return VarSignal>( std::make_shared>>( std::forward(value))); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// MakeSignal /////////////////////////////////////////////////////////////////////////////////////////////////// // Single arg template < typename D, typename TValue, typename FIn, typename F = typename std::decay::type, typename S = typename std::result_of::type, typename TOp = REACT_IMPL::FunctionOp> > auto MakeSignal(const Signal& arg, FIn&& func) -> TempSignal { return TempSignal( std::make_shared>( std::forward(func), GetNodePtr(arg))); } // Multiple args template < typename D, typename ... TValues, typename FIn, typename F = typename std::decay::type, typename S = typename std::result_of::type, typename TOp = REACT_IMPL::FunctionOp ...> > auto MakeSignal(const SignalPack& argPack, FIn&& func) -> TempSignal { using REACT_IMPL::SignalOpNode; struct NodeBuilder_ { NodeBuilder_(FIn&& func) : MyFunc( std::forward(func) ) {} auto operator()(const Signal& ... args) -> TempSignal { return TempSignal( std::make_shared>( std::forward(MyFunc), GetNodePtr(args) ...)); } FIn MyFunc; }; return REACT_IMPL::apply( NodeBuilder_( std::forward(func) ), argPack.Data); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Unary operators /////////////////////////////////////////////////////////////////////////////////////////////////// #define REACT_DECLARE_OP(op,name) \ template \ struct name ## OpFunctor \ { \ T operator()(const T& v) const { return op v; } \ }; \ \ template \ < \ typename TSignal, \ typename D = typename TSignal::DomainT, \ typename TVal = typename TSignal::ValueT, \ class = typename std::enable_if< \ IsSignal::value>::type, \ typename F = name ## OpFunctor, \ typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp> \ > \ auto operator op(const TSignal& arg) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ F(), GetNodePtr(arg))); \ } \ \ template \ < \ typename D, \ typename TVal, \ typename TOpIn, \ typename F = name ## OpFunctor, \ typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp \ > \ auto operator op(TempSignal&& arg) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ F(), arg.StealOp())); \ } REACT_DECLARE_OP(+, UnaryPlus) REACT_DECLARE_OP(-, UnaryMinus) REACT_DECLARE_OP(!, LogicalNegation) REACT_DECLARE_OP(~, BitwiseComplement) REACT_DECLARE_OP(++, Increment) REACT_DECLARE_OP(--, Decrement) #undef REACT_DECLARE_OP /////////////////////////////////////////////////////////////////////////////////////////////////// /// Binary operators /////////////////////////////////////////////////////////////////////////////////////////////////// #define REACT_DECLARE_OP(op,name) \ template \ struct name ## OpFunctor \ { \ auto operator()(const L& lhs, const R& rhs) const \ -> decltype(std::declval() op std::declval()) \ { \ return lhs op rhs; \ } \ }; \ \ template \ struct name ## OpRFunctor \ { \ name ## OpRFunctor(name ## OpRFunctor&& other) : \ LeftVal( std::move(other.LeftVal) ) \ {} \ \ template \ name ## OpRFunctor(T&& val) : \ LeftVal( std::forward(val) ) \ {} \ \ name ## OpRFunctor(const name ## OpRFunctor& other) = delete; \ \ auto operator()(const R& rhs) const \ -> decltype(std::declval() op std::declval()) \ { \ return LeftVal op rhs; \ } \ \ L LeftVal; \ }; \ \ template \ struct name ## OpLFunctor \ { \ name ## OpLFunctor(name ## OpLFunctor&& other) : \ RightVal( std::move(other.RightVal) ) \ {} \ \ template \ name ## OpLFunctor(T&& val) : \ RightVal( std::forward(val) ) \ {} \ \ name ## OpLFunctor(const name ## OpLFunctor& other) = delete; \ \ auto operator()(const L& lhs) const \ -> decltype(std::declval() op std::declval()) \ { \ return lhs op RightVal; \ } \ \ R RightVal; \ }; \ \ template \ < \ typename TLeftSignal, \ typename TRightSignal, \ typename D = typename TLeftSignal::DomainT, \ typename TLeftVal = typename TLeftSignal::ValueT, \ typename TRightVal = typename TRightSignal::ValueT, \ class = typename std::enable_if< \ IsSignal::value>::type, \ class = typename std::enable_if< \ IsSignal::value>::type, \ typename F = name ## OpFunctor, \ typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp, \ REACT_IMPL::SignalNodePtrT> \ > \ auto operator op(const TLeftSignal& lhs, const TRightSignal& rhs) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ F(), GetNodePtr(lhs), GetNodePtr(rhs))); \ } \ \ template \ < \ typename TLeftSignal, \ typename TRightValIn, \ typename D = typename TLeftSignal::DomainT, \ typename TLeftVal = typename TLeftSignal::ValueT, \ typename TRightVal = typename std::decay::type, \ class = typename std::enable_if< \ IsSignal::value>::type, \ class = typename std::enable_if< \ ! IsSignal::value>::type, \ typename F = name ## OpLFunctor, \ typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp> \ > \ auto operator op(const TLeftSignal& lhs, TRightValIn&& rhs) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ F( std::forward(rhs) ), GetNodePtr(lhs))); \ } \ \ template \ < \ typename TLeftValIn, \ typename TRightSignal, \ typename D = typename TRightSignal::DomainT, \ typename TLeftVal = typename std::decay::type, \ typename TRightVal = typename TRightSignal::ValueT, \ class = typename std::enable_if< \ ! IsSignal::value>::type, \ class = typename std::enable_if< \ IsSignal::value>::type, \ typename F = name ## OpRFunctor, \ typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp> \ > \ auto operator op(TLeftValIn&& lhs, const TRightSignal& rhs) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ F( std::forward(lhs) ), GetNodePtr(rhs))); \ } \ template \ < \ typename D, \ typename TLeftVal, \ typename TLeftOp, \ typename TRightVal, \ typename TRightOp, \ typename F = name ## OpFunctor, \ typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp \ > \ auto operator op(TempSignal&& lhs, \ TempSignal&& rhs) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ F(), lhs.StealOp(), rhs.StealOp())); \ } \ \ template \ < \ typename D, \ typename TLeftVal, \ typename TLeftOp, \ typename TRightSignal, \ typename TRightVal = typename TRightSignal::ValueT, \ class = typename std::enable_if< \ IsSignal::value>::type, \ typename F = name ## OpFunctor, \ typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp> \ > \ auto operator op(TempSignal&& lhs, \ const TRightSignal& rhs) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ F(), lhs.StealOp(), GetNodePtr(rhs))); \ } \ \ template \ < \ typename TLeftSignal, \ typename D, \ typename TRightVal, \ typename TRightOp, \ typename TLeftVal = typename TLeftSignal::ValueT, \ class = typename std::enable_if< \ IsSignal::value>::type, \ typename F = name ## OpFunctor, \ typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp, \ TRightOp> \ > \ auto operator op(const TLeftSignal& lhs, TempSignal&& rhs) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ F(), GetNodePtr(lhs), rhs.StealOp())); \ } \ \ template \ < \ typename D, \ typename TLeftVal, \ typename TLeftOp, \ typename TRightValIn, \ typename TRightVal = typename std::decay::type, \ class = typename std::enable_if< \ ! IsSignal::value>::type, \ typename F = name ## OpLFunctor, \ typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp \ > \ auto operator op(TempSignal&& lhs, TRightValIn&& rhs) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ F( std::forward(rhs) ), lhs.StealOp())); \ } \ \ template \ < \ typename TLeftValIn, \ typename D, \ typename TRightVal, \ typename TRightOp, \ typename TLeftVal = typename std::decay::type, \ class = typename std::enable_if< \ ! IsSignal::value>::type, \ typename F = name ## OpRFunctor, \ typename S = typename std::result_of::type, \ typename TOp = REACT_IMPL::FunctionOp \ > \ auto operator op(TLeftValIn&& lhs, TempSignal&& rhs) \ -> TempSignal \ { \ return TempSignal( \ std::make_shared>( \ F( std::forward(lhs) ), rhs.StealOp())); \ } REACT_DECLARE_OP(+, Addition) REACT_DECLARE_OP(-, Subtraction) REACT_DECLARE_OP(*, Multiplication) REACT_DECLARE_OP(/, Division) REACT_DECLARE_OP(%, Modulo) REACT_DECLARE_OP(==, Equal) REACT_DECLARE_OP(!=, NotEqual) REACT_DECLARE_OP(<, Less) REACT_DECLARE_OP(<=, LessEqual) REACT_DECLARE_OP(>, Greater) REACT_DECLARE_OP(>=, GreaterEqual) REACT_DECLARE_OP(&&, LogicalAnd) REACT_DECLARE_OP(||, LogicalOr) REACT_DECLARE_OP(&, BitwiseAnd) REACT_DECLARE_OP(|, BitwiseOr) REACT_DECLARE_OP(^, BitwiseXor) //REACT_DECLARE_OP(<<, BitwiseLeftShift); // MSVC: Internal compiler error //REACT_DECLARE_OP(>>, BitwiseRightShift); #undef REACT_DECLARE_OP /////////////////////////////////////////////////////////////////////////////////////////////////// /// Comma operator overload to create signal pack from 2 signals. /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename TLeftVal, typename TRightVal > auto operator,(const Signal& a, const Signal& b) -> SignalPack { return SignalPack(a, b); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Comma operator overload to append node to existing signal pack. /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename ... TCurValues, typename TAppendValue > auto operator,(const SignalPack& cur, const Signal& append) -> SignalPack { return SignalPack(cur, append); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// operator->* overload to connect signals to a function and return the resulting signal. /////////////////////////////////////////////////////////////////////////////////////////////////// // Single arg template < typename D, typename F, template class TSignal, typename TValue, class = typename std::enable_if< IsSignal>::value>::type > auto operator->*(const TSignal& arg, F&& func) -> Signal::type> { return REACT::MakeSignal(arg, std::forward(func)); } // Multiple args template < typename D, typename F, typename ... TValues > auto operator->*(const SignalPack& argPack, F&& func) -> Signal::type> { return REACT::MakeSignal(argPack, std::forward(func)); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Flatten /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename TInnerValue > auto Flatten(const Signal>& outer) -> Signal { return Signal( std::make_shared, TInnerValue>>( GetNodePtr(outer), GetNodePtr(outer.Value()))); } /////////////////////////////////////////////////////////////////////////////////////////////////// /// Signal /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename S > class Signal : public REACT_IMPL::SignalBase { private: using NodeT = REACT_IMPL::SignalNode; using NodePtrT = std::shared_ptr; public: using ValueT = S; // Default ctor Signal() = default; // Copy ctor Signal(const Signal&) = default; // Move ctor Signal(Signal&& other) : Signal::SignalBase( std::move(other) ) {} // Node ctor explicit Signal(NodePtrT&& nodePtr) : Signal::SignalBase( std::move(nodePtr) ) {} // Copy assignment Signal& operator=(const Signal&) = default; // Move assignment Signal& operator=(Signal&& other) { Signal::SignalBase::operator=( std::move(other) ); return *this; } const S& Value() const { return Signal::SignalBase::getValue(); } const S& operator()() const { return Signal::SignalBase::getValue(); } bool Equals(const Signal& other) const { return Signal::SignalBase::Equals(other); } bool IsValid() const { return Signal::SignalBase::IsValid(); } void SetWeightHint(WeightHint weight) { Signal::SignalBase::SetWeightHint(weight); } S Flatten() const { static_assert(IsSignal::value || IsEvent::value, "Flatten requires a Signal or Events value type."); return REACT::Flatten(*this); } }; // Specialize for references template < typename D, typename S > class Signal : public REACT_IMPL::SignalBase> { private: using NodeT = REACT_IMPL::SignalNode>; using NodePtrT = std::shared_ptr; public: using ValueT = S; // Default ctor Signal() = default; // Copy ctor Signal(const Signal&) = default; // Move ctor Signal(Signal&& other) : Signal::SignalBase( std::move(other) ) {} // Node ctor explicit Signal(NodePtrT&& nodePtr) : Signal::SignalBase( std::move(nodePtr) ) {} // Copy assignment Signal& operator=(const Signal&) = default; // Move assignment Signal& operator=(Signal&& other) { Signal::SignalBase::operator=( std::move(other) ); return *this; } const S& Value() const { return Signal::SignalBase::getValue(); } const S& operator()() const { return Signal::SignalBase::getValue(); } bool Equals(const Signal& other) const { return Signal::SignalBase::Equals(other); } bool IsValid() const { return Signal::SignalBase::IsValid(); } void SetWeightHint(WeightHint weight) { Signal::SignalBase::SetWeightHint(weight); } }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// VarSignal /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename S > class VarSignal : public Signal { private: using NodeT = REACT_IMPL::VarNode; using NodePtrT = std::shared_ptr; public: // Default ctor VarSignal() = default; // Copy ctor VarSignal(const VarSignal&) = default; // Move ctor VarSignal(VarSignal&& other) : VarSignal::Signal( std::move(other) ) {} // Node ctor explicit VarSignal(NodePtrT&& nodePtr) : VarSignal::Signal( std::move(nodePtr) ) {} // Copy assignment VarSignal& operator=(const VarSignal&) = default; // Move assignment VarSignal& operator=(VarSignal&& other) { VarSignal::SignalBase::operator=( std::move(other) ); return *this; } void Set(const S& newValue) const { VarSignal::SignalBase::setValue(newValue); } void Set(S&& newValue) const { VarSignal::SignalBase::setValue(std::move(newValue)); } const VarSignal& operator<<=(const S& newValue) const { VarSignal::SignalBase::setValue(newValue); return *this; } const VarSignal& operator<<=(S&& newValue) const { VarSignal::SignalBase::setValue(std::move(newValue)); return *this; } template void Modify(const F& func) const { VarSignal::SignalBase::modifyValue(func); } }; // Specialize for references template < typename D, typename S > class VarSignal : public Signal> { private: using NodeT = REACT_IMPL::VarNode>; using NodePtrT = std::shared_ptr; public: using ValueT = S; // Default ctor VarSignal() = default; // Copy ctor VarSignal(const VarSignal&) = default; // Move ctor VarSignal(VarSignal&& other) : VarSignal::Signal( std::move(other) ) {} // Node ctor explicit VarSignal(NodePtrT&& nodePtr) : VarSignal::Signal( std::move(nodePtr) ) {} // Copy assignment VarSignal& operator=(const VarSignal&) = default; // Move assignment VarSignal& operator=(VarSignal&& other) { VarSignal::Signal::operator=( std::move(other) ); return *this; } void Set(std::reference_wrapper newValue) const { VarSignal::SignalBase::setValue(newValue); } const VarSignal& operator<<=(std::reference_wrapper newValue) const { VarSignal::SignalBase::setValue(newValue); return *this; } }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// TempSignal /////////////////////////////////////////////////////////////////////////////////////////////////// template < typename D, typename S, typename TOp > class TempSignal : public Signal { private: using NodeT = REACT_IMPL::SignalOpNode; using NodePtrT = std::shared_ptr; public: // Default ctor TempSignal() = default; // Copy ctor TempSignal(const TempSignal&) = default; // Move ctor TempSignal(TempSignal&& other) : TempSignal::Signal( std::move(other) ) {} // Node ctor explicit TempSignal(NodePtrT&& ptr) : TempSignal::Signal( std::move(ptr) ) {} // Copy assignment TempSignal& operator=(const TempSignal&) = default; // Move assignemnt TempSignal& operator=(TempSignal&& other) { TempSignal::Signal::operator=( std::move(other) ); return *this; } TOp StealOp() { return std::move(reinterpret_cast(this->ptr_.get())->StealOp()); } }; /******************************************/ REACT_END /******************************************/ /***************************************/ REACT_IMPL_BEGIN /**************************************/ template bool Equals(const Signal& lhs, const Signal& rhs) { return lhs.Equals(rhs); } /****************************************/ REACT_IMPL_END /***************************************/ /////////////////////////////////////////////////////////////////////////////////////////////////// /// Flatten macros /////////////////////////////////////////////////////////////////////////////////////////////////// // Note: Using static_cast rather than -> return type, because when using lambda for inline // class initialization, decltype did not recognize the parameter r // Note2: MSVC doesn't like typename in the lambda #if _MSC_VER && !__INTEL_COMPILER #define REACT_MSVC_NO_TYPENAME #else #define REACT_MSVC_NO_TYPENAME typename #endif #define REACTIVE_REF(obj, name) \ Flatten( \ MakeSignal( \ obj, \ [] (const REACT_MSVC_NO_TYPENAME \ REACT_IMPL::Identity::Type::ValueT& r) \ { \ using T = decltype(r.name); \ using S = REACT_MSVC_NO_TYPENAME REACT::RemoveInput::Type; \ return static_cast(r.name); \ })) #define REACTIVE_PTR(obj, name) \ Flatten( \ MakeSignal( \ obj, \ [] (REACT_MSVC_NO_TYPENAME \ REACT_IMPL::Identity::Type::ValueT r) \ { \ assert(r != nullptr); \ using T = decltype(r->name); \ using S = REACT_MSVC_NO_TYPENAME REACT::RemoveInput::Type; \ return static_cast(r->name); \ })) #endif // REACT_SIGNAL_H_INCLUDED