|
| 1 | +#pragma once |
| 2 | + |
| 3 | +#include "util.hpp" |
| 4 | + |
| 5 | +/* **************************************************************** |
| 6 | + 説明 |
| 7 | + source : LINQ で言うところの IEnumerable 等 |
| 8 | + gate : LINQ で言うところの Where, Select 等 |
| 9 | + drain : LINQ で言うところの ToList, Aggregate 等 |
| 10 | +
|
| 11 | + 書き方: Range, RxCpp と同じようにパイプ演算子使う |
| 12 | + auto result = source | gate | gate | ... | drain; |
| 13 | +
|
| 14 | + 型結合則 |
| 15 | + source | gate => source |
| 16 | + gate | gate => gate |
| 17 | + gate | drain => drain |
| 18 | + source | drain => decltype(drain.OnComplete()) |
| 19 | +**************************************************************** */ |
| 20 | +namespace fet |
| 21 | +{ |
| 22 | + |
| 23 | +namespace impl |
| 24 | +{ |
| 25 | + |
| 26 | +template <class T> |
| 27 | +struct SourceInfo |
| 28 | +{ |
| 29 | + using value_type = T; |
| 30 | + size_t capacity; |
| 31 | +}; |
| 32 | + |
| 33 | +/* **************************************************************** |
| 34 | + std::is_base_of<> の SFINAE によるディスパッチ用タグクラス |
| 35 | + それぞれコメント内のメソッドを実装すること |
| 36 | + */ |
| 37 | + |
| 38 | +class IJunction |
| 39 | +{ |
| 40 | + // CTX OnConnect(const SourceInfo<E>&) const; |
| 41 | + // void OnNext(CTX&, E&&) const; |
| 42 | + |
| 43 | +protected: |
| 44 | + template <class T> |
| 45 | + constexpr auto OnConnect(const SourceInfo<T>&) const { return nullptr; } |
| 46 | +}; |
| 47 | + |
| 48 | +class ISource |
| 49 | +{ |
| 50 | + // using value_type; |
| 51 | + // SourceInfo<value_type> GetInfo() const; |
| 52 | + // CTX Emit(J&&); enable_if J: IJunction |
| 53 | +}; |
| 54 | + |
| 55 | +class IGate |
| 56 | +{ |
| 57 | + // SourceInfo<T> GetInfo(const SourceInfo<E>&) const; |
| 58 | + // CTX OnConnect(const SourceInfo<E>&) const; |
| 59 | + // void OnNext(CTX&, E&&, callback) const; |
| 60 | + |
| 61 | +protected: |
| 62 | + template <class E> |
| 63 | + constexpr auto GetInfo(const SourceInfo<E> &info) const { return info; } |
| 64 | + |
| 65 | + template <class E> |
| 66 | + constexpr auto OnConnect(const SourceInfo<E>&) const { return nullptr; } |
| 67 | +}; |
| 68 | + |
| 69 | +class IDrain: IJunction |
| 70 | +{ |
| 71 | + // CTX OnConnect(const SourceInfo<E>&) const; |
| 72 | + // void OnNext(CTX&, E&&) const; |
| 73 | + // R OnComplete(CTX&&) const; |
| 74 | + |
| 75 | +protected: |
| 76 | + using IJunction::OnConnect; |
| 77 | +}; |
| 78 | + |
| 79 | +/* **************************************************************** |
| 80 | + SFINAE 用 type_traits |
| 81 | + gcc4.9 では変数テンプレート使えなくて辛い |
| 82 | + C++20 になったら concepts に移行したい |
| 83 | + */ |
| 84 | + |
| 85 | +template <class... T> |
| 86 | +using is_jct = is_base_of<IJunction, T ...>; |
| 87 | + |
| 88 | +template <class... T> |
| 89 | +using is_src = is_base_of<ISource, T ...>; |
| 90 | + |
| 91 | +template <class... T> |
| 92 | +using is_gate = is_base_of<IGate, T ...>; |
| 93 | + |
| 94 | +template <class... T> |
| 95 | +using is_drain = is_base_of<IDrain, T ...>; |
| 96 | + |
| 97 | +/* **************************************************************** |
| 98 | + 型結合用クラス |
| 99 | + source | gate => source |
| 100 | + gate | gate => gate |
| 101 | + gate | drain => drain |
| 102 | + */ |
| 103 | + |
| 104 | +template <class G, class J, enable_if<is_gate<G>, is_jct<J>> = nullptr> |
| 105 | +class Junction: IJunction |
| 106 | +{ |
| 107 | +protected: |
| 108 | + G m_gate; |
| 109 | + J m_jct; |
| 110 | + |
| 111 | +public: |
| 112 | + constexpr Junction(G &&gate, J &&jct): |
| 113 | + m_gate (std::forward<G>(gate)), |
| 114 | + m_jct (std::forward<J>(jct)) |
| 115 | + { } |
| 116 | + |
| 117 | + template <class E> |
| 118 | + constexpr auto OnConnect(const SourceInfo<E> &info) const |
| 119 | + { |
| 120 | + return std::pair<decltype(m_gate.OnConnect(info)), decltype(m_jct.OnConnect(m_gate.GetInfo(info)))> { |
| 121 | + m_gate.OnConnect(info), m_jct.OnConnect(m_gate.GetInfo(info)) |
| 122 | + }; |
| 123 | + } |
| 124 | + |
| 125 | + template <class CTX, class E> |
| 126 | + constexpr auto OnNext(CTX &ctx, E &&e) const |
| 127 | + { |
| 128 | + return m_gate.OnNext(ctx.first, std::forward<E>(e), [&](auto &&e) { |
| 129 | + return m_jct.OnNext(ctx.second, std::forward<decltype(e)>(e)); |
| 130 | + }); |
| 131 | + } |
| 132 | +}; |
| 133 | + |
| 134 | +template <class G, class J, enable_if<is_gate<G>, is_jct<J>> = nullptr> |
| 135 | +constexpr Junction<G, J> make_jct(G &&gate, J &&jct) |
| 136 | +{ |
| 137 | + return { std::forward<G>(gate), std::forward<J>(jct) }; |
| 138 | +} |
| 139 | + |
| 140 | +template <class S, class G, enable_if<is_src<S>, is_gate<G>> = nullptr> |
| 141 | +class Source: ISource |
| 142 | +{ |
| 143 | + S m_src; |
| 144 | + G m_gate; |
| 145 | + |
| 146 | +public: |
| 147 | + constexpr auto GetInfo() const |
| 148 | + { |
| 149 | + return m_gate.GetInfo(m_src.GetInfo()); |
| 150 | + } |
| 151 | + |
| 152 | + using value_type = typename decltype(std::declval<Source<S, G>>().GetInfo())::value_type; |
| 153 | + |
| 154 | + constexpr Source(S &&src, G &&gate): |
| 155 | + m_src (std::forward<S>(src)), |
| 156 | + m_gate (std::forward<G>(gate)) |
| 157 | + { } |
| 158 | + |
| 159 | + template <class J, enable_if<is_jct<J>> = nullptr> |
| 160 | + constexpr decltype(auto) Emit(J && jct) const & { |
| 161 | + return m_src.Emit(make_jct(m_gate, std::forward<J>(jct))).second; |
| 162 | + } |
| 163 | + |
| 164 | + template <class J, enable_if<is_jct<J>> = nullptr> |
| 165 | + decltype(auto) Emit(J && jct) && { |
| 166 | + return std::forward<S>(m_src).Emit(make_jct(std::forward<G>(m_gate), std::forward<J>(jct))).second; |
| 167 | + } |
| 168 | +}; |
| 169 | + |
| 170 | +template <class S, class G, enable_if<is_src<S>, is_gate<G>> = nullptr> |
| 171 | +constexpr Source<S, G> make_src(S &&src, G &&gate) |
| 172 | +{ |
| 173 | + return { std::forward<S>(src), std::forward<G>(gate) }; |
| 174 | +} |
| 175 | + |
| 176 | +template <class G1, class G2, enable_if<is_gate<G1, G2>> = nullptr> |
| 177 | +class Gate: IGate |
| 178 | +{ |
| 179 | + G1 m_gate1; |
| 180 | + G2 m_gate2; |
| 181 | + |
| 182 | +public: |
| 183 | + constexpr Gate(G1 &&gate1, G2 &&gate2): |
| 184 | + m_gate1 (std::forward<G1>(gate1)), |
| 185 | + m_gate2 (std::forward<G2>(gate2)) |
| 186 | + { } |
| 187 | + |
| 188 | + template <class E> |
| 189 | + constexpr auto GetInfo(const SourceInfo<E> &info) const |
| 190 | + { |
| 191 | + return m_gate2.GetInfo(m_gate1.GetInfo(info)); |
| 192 | + } |
| 193 | + |
| 194 | + template <class E> |
| 195 | + constexpr auto OnConnect(const SourceInfo<E> &info) const |
| 196 | + { |
| 197 | + return std::pair<decltype(m_gate1.OnConnect(info)), decltype(m_gate2.OnConnect(m_gate1.GetInfo(info)))> { |
| 198 | + m_gate1.OnConnect(info), m_gate2.OnConnect(m_gate1.GetInfo(info)) |
| 199 | + }; |
| 200 | + } |
| 201 | + |
| 202 | + template <class CTX, class E, class CB> |
| 203 | + constexpr decltype(auto) OnNext(CTX & ctx, E && e, CB && cb) const { |
| 204 | + return m_gate1.OnNext(ctx.first, std::forward<E>(e), [&](auto &&e) { |
| 205 | + return m_gate2.OnNext(ctx.second, std::forward<decltype(e)>(e), std::forward<CB>(cb)); |
| 206 | + }); |
| 207 | + } |
| 208 | +}; |
| 209 | + |
| 210 | +template <class G1, class G2, enable_if<is_gate<G1, G2>> = nullptr> |
| 211 | +constexpr Gate<G1, G2> make_gate(G1 &&gate1, G2 &&gate2) |
| 212 | +{ |
| 213 | + return { std::forward<G1>(gate1), std::forward<G2>(gate2) }; |
| 214 | +} |
| 215 | + |
| 216 | +template <class G, class D, enable_if<is_gate<G>, is_drain<D>> = nullptr> |
| 217 | +class Drain: IDrain, |
| 218 | + Junction<G, D> |
| 219 | +{ |
| 220 | +public: |
| 221 | + constexpr Drain(G &&gate, D &&drain): |
| 222 | + Junction<G, D>(std::forward<G>(gate), std::forward<D>(drain)) |
| 223 | + { } |
| 224 | + |
| 225 | + using Junction<G, D>::OnConnect; |
| 226 | + using Junction<G, D>::OnNext; |
| 227 | + |
| 228 | + template <class CTX> |
| 229 | + constexpr decltype(auto) OnComplete(CTX && ctx) const & { |
| 230 | + return this->m_jct.OnComplete(std::forward<CTX>(ctx).second); |
| 231 | + } |
| 232 | + |
| 233 | + template <class CTX> |
| 234 | + decltype(auto) OnComplete(CTX && ctx) && { |
| 235 | + return std::forward<D>(this->m_jct).OnComplete(std::forward<CTX>(ctx).second); |
| 236 | + } |
| 237 | +}; |
| 238 | + |
| 239 | +template <class G, class D, enable_if<is_gate<G>, is_drain<D>> = nullptr> |
| 240 | +constexpr Drain<G, D> make_drain(G &&gate, D &&drain) |
| 241 | +{ |
| 242 | + return { std::forward<G>(gate), std::forward<D>(drain) }; |
| 243 | +} |
| 244 | + |
| 245 | +/* **************************************************************** |
| 246 | + パイプ演算子オーバーロード |
| 247 | + source | gate => source |
| 248 | + gate | gate => gate |
| 249 | + gate | drain => drain |
| 250 | + source | drain => decltype(drain.OnComplete()) |
| 251 | + */ |
| 252 | + |
| 253 | +template <class S, class G, enable_if<is_src<S>, is_gate<G>> = nullptr> |
| 254 | +constexpr auto operator |(S &&src, G &&gate) |
| 255 | +{ |
| 256 | + return make_src(std::forward<S>(src), std::forward<G>(gate)); |
| 257 | +} |
| 258 | + |
| 259 | +template <class G1, class G2, enable_if<is_gate<G1, G2>> = nullptr> |
| 260 | +constexpr auto operator |(G1 &&gate1, G2 &&gate2) |
| 261 | +{ |
| 262 | + return make_gate(std::forward<G1>(gate1), std::forward<G2>(gate2)); |
| 263 | +} |
| 264 | + |
| 265 | +template <class G, class D, enable_if<is_gate<G>, is_drain<D>> = nullptr> |
| 266 | +constexpr auto operator |(G &&gate, D &&drain) |
| 267 | +{ |
| 268 | + return make_drain(std::forward<G>(gate), std::forward<D>(drain)); |
| 269 | +} |
| 270 | + |
| 271 | +template <class S, class D, enable_if<is_src<S>, is_drain<D>> = nullptr> |
| 272 | +constexpr decltype(auto) operator |(S && src, D && drain) { |
| 273 | + return std::forward<D>(drain).OnComplete(std::forward<S>(src).Emit(drain)); |
| 274 | +} |
| 275 | + |
| 276 | +} // namespace impl |
| 277 | + |
| 278 | +} // namespace fet |
0 commit comments