// 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 \
> \
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 \
> \
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(=, GreaterEqual)
REACT_DECLARE_OP(&&, LogicalAnd)
REACT_DECLARE_OP(||, LogicalOr)
REACT_DECLARE_OP(&, BitwiseAnd)
REACT_DECLARE_OP(|, BitwiseOr)
REACT_DECLARE_OP(^, BitwiseXor)
//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
{
return REACT::MakeSignal(arg, std::forward(func));
}
// Multiple args
template
<
typename D,
typename F,
typename ... TValues
>
auto operator->*(const SignalPack& argPack, F&& func)
-> Signal
{
return REACT::MakeSignal(argPack, std::forward(func));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
/// Flatten
///////////////////////////////////////////////////////////////////////////////////////////////////
template
<
typename D,
typename TInnerValue
>
auto Flatten(const Signal& outer)
-> Signal
{
return Signal(
std::make_shared(
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