Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Tutorial: closure and adaptor

Here is boost::stl_interfaces::closure:

    /** An invocable consisting of a contained invocable `f`.  Calling
        `operator()` with some argument `t` calls `f(t)` and returns the
        result.  This type is typically used to capture a the result of a call
        to `bind_back()`. */
    template<typename F>
    struct closure : range_adaptor_closure<closure<F>>
    {
        constexpr closure(F f) : f_(f) {}

#if BOOST_STL_INTERFACES_USE_CONCEPTS
        template<typename T>
        requires std::invocable<F const &, T>
#else
        template<
            typename T,
            typename Enable =
                std::enable_if_t<detail::is_invocable_v<F const &, T>>>
#endif
        constexpr decltype(auto) operator()(T && t) const
        {
            return f_((T &&) t);
        }

    private:
        F f_;
    };

As you can see, it inherits from boost::stl_interfaces::range_adaptor_closure, and its only member function is a call operator that forwards to the function F it is constructed from. It exists simply to adapt some function that constructs a particular view to use in pipe expressions.

adaptor is slightly different; it operates essentially just like closure if you call its operator() with all arguments necessary to invoke its function F — it just constructs a view. However, if you pass it all the arguments but the first, it returns a closure with all those arguments bound into it:

    /** Adapts an invocable `f` as a view adaptor.  Calling
        `operator(args...)` will either: call `f(args...)` and return the
        result, if `f(args...)` is well-formed; or return
        `closure(stl_interfaces::bind_back(f, args...))` otherwise. */
    template<typename F>
    struct adaptor
    {
        constexpr adaptor(F f) : f_(f) {}

        // clang-format off
        template<typename... Args>
        constexpr auto operator()(Args &&... args) const
        // clang-format on
        {
#if BOOST_STL_INTERFACES_USE_CONCEPTS
            if constexpr (std::is_invocable_v<F const &, Args...>) {
                return f((Args &&) args...);
            } else {
                return closure(
                    stl_interfaces::bind_back(f_, (Args &&) args...));
            }
#else
            return detail::adaptor_impl<
                F const &,
                detail::is_invocable_v<F const &, Args...>,
                Args...>::call(f_, (Args &&) args...);
#endif
        }

    private:
        F f_;
    };

This allows you to use it both as a view-constructor (e.g. take(range, 3)), and as a closure-constructor (e.g. range | take(3)).

[Important] Important

Due to the way adaptor works, your views's constructors should always take their view parameter before any other parameters. Otherwise, the combination of bind_back() and closure will probably not do anything very useful.


PrevUpHomeNext