Skip to content

Latest commit

 

History

History
180 lines (145 loc) · 5.26 KB

276.md

File metadata and controls

180 lines (145 loc) · 5.26 KB
Info

Example

int main() {
  std::cout << std::bind_front(std::divides{}, 2.)(1.); // prints 2
  std::cout << std::bind_back (std::divides{}, 2.)(1.); // prints 0.5
}

https://godbolt.org/z/qK3rdKh39

Puzzle

  • Can you implement a simplified version of bind_back to illustrate its usage with higher order functions?
[[nodiscard]] constexpr auto bind_back(auto&&...); // TODO

int main() {
  using namespace boost::ut;

  "bind_back"_test = [] {
    expect(3._d == bind_back(std::plus{}, 1.)(2.));
    expect(3._d == bind_back(std::plus{}, 2.)(1.));
    expect(3._d == bind_back(std::plus{}, 1., 2.)());
    expect(3._d == bind_back(std::plus{})(1., 2.));

    expect(2._d == bind_back(std::divides{}, 1.)(2.));
    expect(.5_d == bind_back(std::divides{}, 2.)(1.));

    expect(1._d == bind_back(std::minus{}, 1.)(2.));
    expect(-1._d == bind_back(std::minus{}, 2.)(1.));
  };
}

https://godbolt.org/z/7rnfG6v7q

Solutions

#define FWD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)

[[nodiscard]] constexpr auto bind_back(const auto& func, auto... bound_args) {
    return [=] [[nodiscard]] (auto&&... unbound_args) {
        return func(FWD(unbound_args)..., bound_args...);
    };
}

https://godbolt.org/z/zjG8191Wb

[[nodiscard]] constexpr auto bind_back(auto&& fn, auto&&...bound_args) {
    return [=](auto&&...args){
        return fn(args..., bound_args...);
    };
};

https://godbolt.org/z/Md978687E

[[nodiscard]] constexpr auto bind_back(auto&& fn, auto&&... back_args) {
  return [fn = std::forward<decltype(fn)>(fn),
          ...back_args = std::forward<decltype(back_args)>(back_args)] (auto&&... front_args) {
    return std::invoke(fn, front_args..., back_args...);
  };
}

https://godbolt.org/z/d89xW84no

// From https://wg21.link/p2445
namespace proposed_std {
template <typename T, typename U>
using __override_ref_t = std::conditional_t<std::is_rvalue_reference_v<T>,
                                            std::remove_reference_t<U> &&, U &>;
template <typename T, typename U>
using __copy_const_t = std::conditional_t<std::is_const_v<std::remove_reference_t<T>>,
                                          U const, U>;
template <typename T, typename U>
using __forward_like_t = __override_ref_t<T &&, __copy_const_t<T, std::remove_reference_t<U>>>;

template <typename T>
[[nodiscard]] constexpr
auto forward_like(auto&& x) noexcept -> __forward_like_t<T, decltype(x)> {
  return static_cast<__forward_like_t<T, decltype(x)>>(x);
}
}

template <typename F, typename... BackArgs>
[[nodiscard]] constexpr auto bind_back(F&& fn, BackArgs&&... back_args) {
  return [fn = std::forward<F>(fn),
          ...back_args = std::forward<BackArgs>(back_args)]
          <typename Self, typename... FrontArgs> (
            this Self&& self, FrontArgs&&... front_args) -> decltype(auto) {
    return std::invoke(
      proposed_std::forward_like<Self>(fn),
      std::forward<FrontArgs>(front_args)...,
      proposed_std::forward_like<Self>(back_args)...);
  };
}

https://godbolt.org/z/a6M3E48E3

template <typename F, typename ... BArgs>
auto bind_back_helper (F const & f, BArgs ... bound_args)
{
    return [f,... bound_args = std::forward<BArgs>(bound_args)](auto... call_args){
        return std::invoke_r<double>(f,call_args...,bound_args...);
    };
}

template <typename ... Args>
[[nodiscard]] constexpr auto bind_back(auto &&... args)
{
    return bind_back_helper<Args...>(args...);
};

https://godbolt.org/z/58rzfz9a9

[[nodiscard]] constexpr auto bind_back(auto&&... args)
{
    if constexpr (sizeof...(args) == 1) {
        auto [fun] = std::tuple{args...};
        return [fun=fun](auto arg1, auto arg2) { return fun(arg1, arg2); };
     } else if constexpr(sizeof ...(args) == 2) {
        auto [fun, arg] = std::tuple{args...};
        return [fun=fun, arg1=arg](auto arg2) { return fun(arg2, arg1); };
    } else {
        auto [fun, arg1, arg2] = std::tuple{args...};
        return [fun=fun, arg1=arg1, arg2=arg2]() { return fun(arg1, arg2);};
    }
}

https://godbolt.org/z/hnd9G63v6

template <typename F, typename ... Args1>
[[nodiscard]] constexpr auto bind_back( F && f, Args1 && ... args1 ){
    return [&]<typename ... Args2>( Args2 && ... args2 ){
        return std::invoke(f, std::forward<Args2>(args2)..., std::forward<Args1>(args1)...);
    };
}

https://godbolt.org/z/3rKqx86M5

// Heavily inspired by https://stackoverflow.com/questions/64712892/mimic-stdbind-front-in-c17-for-calling-member-functions
// https://godbolt.org/z/cqPjTY

[[nodiscard]] constexpr auto bind_back(auto &&f, auto&&... args){
    return [f=std::forward<decltype(f)>(f),
            frontArgs = std::make_tuple(std::forward<decltype(args)>(args)...)]
            (auto&&...backArgs) {
            return std::apply(
                f,
                std::tuple_cat(
                    std::forward_as_tuple(backArgs...),
                    frontArgs
                ));
        };
}

https://godbolt.org/z/T9x8s59jh