Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:37:58

0001 /*!
0002 @file
0003 Defines `boost::hana::demux`.
0004 
0005 Copyright Louis Dionne 2013-2022
0006 Distributed under the Boost Software License, Version 1.0.
0007 (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
0008  */
0009 
0010 #ifndef BOOST_HANA_FUNCTIONAL_DEMUX_HPP
0011 #define BOOST_HANA_FUNCTIONAL_DEMUX_HPP
0012 
0013 #include <boost/hana/basic_tuple.hpp>
0014 #include <boost/hana/config.hpp>
0015 #include <boost/hana/detail/decay.hpp>
0016 
0017 #include <cstddef>
0018 #include <utility>
0019 
0020 
0021 namespace boost { namespace hana {
0022     //! @ingroup group-functional
0023     //! Invoke a function with the results of invoking other functions
0024     //! on its arguments.
0025     //!
0026     //! Specifically, `demux(f)(g...)` is a function such that
0027     //! @code
0028     //!     demux(f)(g...)(x...) == f(g(x...)...)
0029     //! @endcode
0030     //!
0031     //! Each `g` is called with all the arguments, and then `f` is called
0032     //! with the result of each `g`. Hence, the arity of `f` must match
0033     //! the number of `g`s.
0034     //!
0035     //! This is called `demux` because of a vague similarity between this
0036     //! device and a demultiplexer in signal processing. `demux` takes what
0037     //! can be seen as a continuation (`f`), a bunch of functions to split a
0038     //! signal (`g...`) and zero or more arguments representing the signal
0039     //! (`x...`). Then, it calls the continuation with the result of
0040     //! splitting the signal with whatever functions where given.
0041     //!
0042     //! @note
0043     //! When used with two functions only, `demux` is associative. In other
0044     //! words (and noting `demux(f, g) = demux(f)(g)` to ease the notation),
0045     //! it is true that `demux(demux(f, g), h) == demux(f, demux(g, h))`.
0046     //!
0047     //!
0048     //! Signature
0049     //! ---------
0050     //! The signature of `demux` is
0051     //! \f[
0052     //!     \mathtt{demux} :
0053     //!         (B_1 \times \dotsb \times B_n \to C)
0054     //!             \to ((A_1 \times \dotsb \times A_n \to B_1)
0055     //!                 \times \dotsb
0056     //!                 \times (A_1 \times \dotsb \times A_n \to B_n))
0057     //!             \to (A_1 \times \dotsb \times A_n \to C)
0058     //! \f]
0059     //!
0060     //! This can be rewritten more tersely as
0061     //! \f[
0062     //!     \mathtt{demux} :
0063     //!         \left(\prod_{i=1}^n B_i \to C \right)
0064     //!         \to \prod_{j=1}^n \left(\prod_{i=1}^n A_i \to B_j \right)
0065     //!         \to \left(\prod_{i=1}^n A_i \to C \right)
0066     //! \f]
0067     //!
0068     //!
0069     //! Link with normal composition
0070     //! ----------------------------
0071     //! The signature of `compose` is
0072     //! \f[
0073     //!     \mathtt{compose} : (B \to C) \times (A \to B) \to (A \to C)
0074     //! \f]
0075     //!
0076     //! A valid observation is that this coincides exactly with the type
0077     //! of `demux` when used with a single unary function. Actually, both
0078     //! functions are equivalent:
0079     //! @code
0080     //!     demux(f)(g)(x) == compose(f, g)(x)
0081     //! @endcode
0082     //!
0083     //! However, let's now consider the curried version of `compose`,
0084     //! `curry<2>(compose)`:
0085     //! \f[
0086     //!     \mathtt{curry_2(compose)} : (B \to C) \to ((A \to B) \to (A \to C))
0087     //! \f]
0088     //!
0089     //! For the rest of this explanation, we'll just consider the curried
0090     //! version of `compose` and so we'll use `compose` instead of
0091     //! `curry<2>(compose)` to lighten the notation. With currying, we can
0092     //! now consider `compose` applied to itself:
0093     //! \f[
0094     //!     \mathtt{compose(compose, compose)} :
0095     //!         (B \to C) \to (A_1 \to A_2 \to B) \to (A_1 \to A_2 \to C)
0096     //! \f]
0097     //!
0098     //! If we uncurry deeply the above expression, we obtain
0099     //! \f[
0100     //!     \mathtt{compose(compose, compose)} :
0101     //!         (B \to C) \times (A_1 \times A_2 \to B) \to (A_1 \times A_2 \to C)
0102     //! \f]
0103     //!
0104     //! This signature is exactly the same as that of `demux` when given a
0105     //! single binary function, and indeed they are equivalent definitions.
0106     //! We can also generalize this further by considering
0107     //! `compose(compose(compose, compose), compose)`:
0108     //! \f[
0109     //!     \mathtt{compose(compose(compose, compose), compose)} :
0110     //!         (B \to C) \to (A_1 \to A_2 \to A_3 \to B)
0111     //!             \to (A_1 \to A_2 \to A_3 \to C)
0112     //! \f]
0113     //!
0114     //! which uncurries to
0115     //! \f[
0116     //!     \mathtt{compose(compose(compose, compose), compose)} :
0117     //!         (B \to C) \times (A_1 \times A_2 \times A_3 \to B)
0118     //!             \to (A_1 \times A_2 \times A_3 \to C)
0119     //! \f]
0120     //!
0121     //! This signature is exactly the same as that of `demux` when given a
0122     //! single ternary function. Hence, for a single n-ary function `g`,
0123     //! `demux(f)(g)` is equivalent to the n-times composition of `compose`
0124     //! with itself, applied to `g` and `f`:
0125     //! @code
0126     //!     demux(f)(g) == fold_left([compose, ..., compose], id, compose)(g, f)
0127     //!                           //  ^^^^^^^^^^^^^^^^^^^^^ n times
0128     //! @endcode
0129     //!
0130     //! More information on this insight can be seen [here][1]. Also, I'm
0131     //! not sure how this insight could be generalized to more than one
0132     //! function `g`, or if that is even possible.
0133     //!
0134     //!
0135     //! Proof of associativity in the binary case
0136     //! -----------------------------------------
0137     //! As explained above, `demux` is associative when it is used with
0138     //! two functions only. Indeed, given functions `f`, `g` and `h` with
0139     //! suitable signatures, we have
0140     //! @code
0141     //!     demux(f)(demux(g)(h))(x...) == f(demux(g)(h)(x...))
0142     //!                                 == f(g(h(x...)))
0143     //! @endcode
0144     //!
0145     //! On the other hand, we have
0146     //! @code
0147     //!     demux(demux(f)(g))(h)(x...) == demux(f)(g)(h(x...))
0148     //!                                 == f(g(h(x...)))
0149     //! @endcode
0150     //!
0151     //! and hence `demux` is associative in the binary case.
0152     //!
0153     //!
0154     //! Example
0155     //! -------
0156     //! @include example/functional/demux.cpp
0157     //!
0158     //! [1]: http://stackoverflow.com/q/5821089/627587
0159 #ifdef BOOST_HANA_DOXYGEN_INVOKED
0160     constexpr auto demux = [](auto&& f) {
0161         return [perfect-capture](auto&& ...g) {
0162             return [perfect-capture](auto&& ...x) -> decltype(auto) {
0163                 // x... can't be forwarded unless there is a single g
0164                 // function, or that could cause double-moves.
0165                 return forwarded(f)(forwarded(g)(x...)...);
0166             };
0167         };
0168     };
0169 #else
0170     template <typename F>
0171     struct pre_demux_t;
0172 
0173     struct make_pre_demux_t {
0174         struct secret { };
0175         template <typename F>
0176         constexpr pre_demux_t<typename detail::decay<F>::type> operator()(F&& f) const {
0177             return {static_cast<F&&>(f)};
0178         }
0179     };
0180 
0181     template <typename Indices, typename F, typename ...G>
0182     struct demux_t;
0183 
0184     template <typename F>
0185     struct pre_demux_t {
0186         F f;
0187 
0188         template <typename ...G>
0189         constexpr demux_t<std::make_index_sequence<sizeof...(G)>, F,
0190                           typename detail::decay<G>::type...>
0191         operator()(G&& ...g) const& {
0192             return {make_pre_demux_t::secret{}, this->f, static_cast<G&&>(g)...};
0193         }
0194 
0195         template <typename ...G>
0196         constexpr demux_t<std::make_index_sequence<sizeof...(G)>, F,
0197                           typename detail::decay<G>::type...>
0198         operator()(G&& ...g) && {
0199             return {make_pre_demux_t::secret{}, static_cast<F&&>(this->f), static_cast<G&&>(g)...};
0200         }
0201     };
0202 
0203     template <std::size_t ...n, typename F, typename ...G>
0204     struct demux_t<std::index_sequence<n...>, F, G...> {
0205         template <typename ...T>
0206         constexpr demux_t(make_pre_demux_t::secret, T&& ...t)
0207             : storage_{static_cast<T&&>(t)...}
0208         { }
0209 
0210         basic_tuple<F, G...> storage_;
0211 
0212         template <typename ...X>
0213         constexpr decltype(auto) operator()(X&& ...x) const& {
0214             return hana::at_c<0>(storage_)(
0215                 hana::at_c<n+1>(storage_)(x...)...
0216             );
0217         }
0218 
0219         template <typename ...X>
0220         constexpr decltype(auto) operator()(X&& ...x) & {
0221             return hana::at_c<0>(storage_)(
0222                 hana::at_c<n+1>(storage_)(x...)...
0223             );
0224         }
0225 
0226         template <typename ...X>
0227         constexpr decltype(auto) operator()(X&& ...x) && {
0228             return static_cast<F&&>(hana::at_c<0>(storage_))(
0229                 static_cast<G&&>(hana::at_c<n+1>(storage_))(x...)...
0230             );
0231         }
0232     };
0233 
0234     template <typename F, typename G>
0235     struct demux_t<std::index_sequence<0>, F, G> {
0236         template <typename ...T>
0237         constexpr demux_t(make_pre_demux_t::secret, T&& ...t)
0238             : storage_{static_cast<T&&>(t)...}
0239         { }
0240 
0241         basic_tuple<F, G> storage_;
0242 
0243         template <typename ...X>
0244         constexpr decltype(auto) operator()(X&& ...x) const& {
0245             return hana::at_c<0>(storage_)(
0246                 hana::at_c<1>(storage_)(static_cast<X&&>(x)...)
0247             );
0248         }
0249 
0250         template <typename ...X>
0251         constexpr decltype(auto) operator()(X&& ...x) & {
0252             return hana::at_c<0>(storage_)(
0253                 hana::at_c<1>(storage_)(static_cast<X&&>(x)...)
0254             );
0255         }
0256 
0257         template <typename ...X>
0258         constexpr decltype(auto) operator()(X&& ...x) && {
0259             return static_cast<F&&>(hana::at_c<0>(storage_))(
0260                 static_cast<G&&>(hana::at_c<1>(storage_))(static_cast<X&&>(x)...)
0261             );
0262         }
0263     };
0264 
0265     BOOST_HANA_INLINE_VARIABLE constexpr make_pre_demux_t demux{};
0266 #endif
0267 }} // end namespace boost::hana
0268 
0269 #endif // !BOOST_HANA_FUNCTIONAL_DEMUX_HPP