// file : libbutl/move-only-function.hxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #pragma once #include #include #include namespace butl { // This is a move-only std::function version which is implemented in terms // of std::function. It is similar to C++23 std::move_only_function but // still provides target() (but not target_type()). // template class move_only_function_ex; // Alias butl::move_only_function to std::move_only_function if available // and to move_only_function_ex otherwise. // #ifdef __cpp_lib_move_only_function using std::move_only_function; #else template using move_only_function = move_only_function_ex; #endif template class move_only_function_ex { public: using result_type = R; move_only_function_ex () = default; move_only_function_ex (std::nullptr_t) noexcept {} // Note: according to the spec we should also disable these if F is not // callable, but that is not easy to do in C++14. Maybe we should do // something for C++17 and later (without this the diagnostics is quite // hairy). // template move_only_function_ex (F&& f, typename std::enable_if::type, move_only_function_ex>::value>::type* = 0) { using FV = typename std::decay::type; if (!null (f)) f_ = wrapper (std::forward (f)); } template typename std::enable_if::type, move_only_function_ex>::value, move_only_function_ex>::type& operator= (F&& f) { move_only_function_ex (std::forward (f)).swap (*this); return *this; } move_only_function_ex& operator= (std::nullptr_t) noexcept { f_ = nullptr; return *this; } void swap (move_only_function_ex& f) noexcept { f_.swap (f.f_); } R operator() (A... args) const { return f_ (std::forward (args)...); } explicit operator bool () const noexcept { return static_cast (f_); } template T* target() noexcept { wrapper* r (f_.template target> ()); return r != nullptr ? &r->f : nullptr; } template const T* target() const noexcept { const wrapper* r (f_.template target> ()); return r != nullptr ? &r->f : nullptr; } move_only_function_ex (move_only_function_ex&&) = default; move_only_function_ex& operator= (move_only_function_ex&&) = default; move_only_function_ex (const move_only_function_ex&) = delete; move_only_function_ex& operator= (const move_only_function_ex&) = delete; private: template struct wrapper { struct empty {}; union { F f; empty e; }; explicit wrapper (F&& f_): f (std::move (f_)) {} explicit wrapper (const F& f_): f (f_) {} R operator() (A... args) { return f (std::forward (args)...); } R operator() (A... args) const { return f (std::forward (args)...); } wrapper (wrapper&& w) noexcept (std::is_nothrow_move_constructible::value) : f (std::move (w.f)) {} wrapper& operator= (wrapper&&) = delete; // Shouldn't be needed. ~wrapper () {f.~F ();} // These shouldn't be called. // wrapper (const wrapper&) {} wrapper& operator= (const wrapper&) {return *this;} }; template static bool null (const F&) {return false;} template static bool null (R1 (*p) (A1...)) {return p == nullptr;} template static bool null (const move_only_function_ex& f) {return !f;} template static bool null (R1 (C::*p) (A1...)) {return p == nullptr;} template static bool null (R1 (C::*p) (A1...) const) {return p == nullptr;} std::function f_; }; template inline bool operator== (const move_only_function_ex& f, std::nullptr_t) noexcept { return !f; } template inline bool operator== (std::nullptr_t, const move_only_function_ex& f) noexcept { return !f; } template inline bool operator!= (const move_only_function_ex& f, std::nullptr_t) noexcept { return static_cast (f); } template inline bool operator!= (std::nullptr_t, const move_only_function_ex& f) noexcept { return static_cast (f); } }