[llvm-branch-commits] [libcxx] [libc++] Implement std::move_only_function (P0288R9) (PR #94670)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Jun 6 20:45:29 PDT 2024


================
@@ -0,0 +1,233 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// This header is unguarded on purpose. This header is an implementation detail of move_only_function.h
+// and generates multiple versions of std::move_only_function
+
+#include <__config>
+#include <__functional/invoke.h>
+#include <__functional/move_only_function_common.h>
+#include <__type_traits/is_trivially_destructible.h>
+#include <__utility/exchange.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+#include <__utility/pointer_int_pair.h>
+#include <__utility/small_buffer.h>
+#include <__utility/swap.h>
+#include <cstddef>
+#include <cstring>
+#include <initializer_list>
+#include <new>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#ifndef _LIBCPP_IN_MOVE_ONLY_FUNCTION_H
+#  error This header should only be included from move_only_function.h
+#endif
+
+#ifndef _LIBCPP_MOVE_ONLY_FUNCTION_CV
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_CV
+#endif
+
+#ifndef _LIBCPP_MOVE_ONLY_FUNCTION_REF
----------------
EricWF wrote:

Here's what I'm imagining...

```c++
#ifndef _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_H
#define _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_H

#include <__assert>
#include <__config>
#include <__functional/invoke.h>
#include <__type_traits/decay.h>
#include <__type_traits/invoke.h>
#include <__type_traits/is_function.h>
#include <__type_traits/is_member_function_pointer.h>
#include <__type_traits/is_pointer.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_trivially_constructible.h>
#include <__type_traits/is_trivially_destructible.h>
#include <__type_traits/remove_cvref.h>
#include <__type_traits/remove_pointer.h>
#include <__utility/forward.h>
#include <__utility/in_place.h>
#include <__utility/swap.h>
#include <cstddef>
#include <initializer_list>
#include <new>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#  pragma GCC system_header
#endif

_LIBCPP_BEGIN_NAMESPACE_STD

enum class _Quals : unsigned {
  _None     = 0,
  _Const    = 1,
  _RVRef    = 2,
  _LVRef    = 4,
  _NoExcept = 8,
};

constexpr _Quals operator|(_Quals __x, _Quals __y) {
  return static_cast<_Quals>(static_cast<unsigned>(__x) | static_cast<unsigned>(__y));
}
constexpr _Quals operator&(_Quals __x, _Quals __y) {
  return static_cast<_Quals>(static_cast<unsigned>(__x) & static_cast<unsigned>(__y));
}
constexpr _Quals operator^(_Quals __x, _Quals __y) {
  return static_cast<_Quals>(static_cast<unsigned>(__x) ^ static_cast<unsigned>(__y));
}
constexpr _Quals operator~(_Quals __x) { return static_cast<_Quals>(~static_cast<unsigned>(__x)); }

constexpr bool operator==(const _Quals __x, const _Quals __y) {
  return static_cast<unsigned>(__x) == static_cast<unsigned>(__y);
}
constexpr bool operator!=(const _Quals __x, const _Quals __y) {
  return static_cast<unsigned>(__x) != static_cast<unsigned>(__y);
}

constexpr _Quals __cvref_quals(_Quals __q) { return __q & (_Quals::_Const | _Quals::_RVRef | _Quals::_LVRef); }

constexpr bool __is_noexcept(_Quals __q) { return (__q & _Quals::_NoExcept) == _Quals::_NoExcept; }

constexpr _Quals __ref_quals(_Quals __q) { return __q & (_Quals::_RVRef | _Quals::_LVRef); }

template <class _Ptr>
struct __unqualify_type;

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...)> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_None;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_Const;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...)&> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_None | _Quals::_LVRef;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const&> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_Const | _Quals::_LVRef;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) &&> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_None | _Quals::_RVRef;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const&&> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_Const | _Quals::_RVRef;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) noexcept> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_None | _Quals::_NoExcept;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const noexcept> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_Const | _Quals::_NoExcept;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) & noexcept> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_None | _Quals::_LVRef | _Quals::_NoExcept;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const & noexcept> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_Const | _Quals::_LVRef | _Quals::_NoExcept;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) && noexcept> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_None | _Quals::_RVRef | _Quals::_NoExcept;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const && noexcept> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_Const | _Quals::_RVRef | _Quals::_NoExcept;
};

template <class _Tp>
struct __identity {
  using type = _Tp;
};

template <_Quals __qual, class _Ret, class... _Args>
constexpr auto __qualify_function_type() {
  if constexpr (__qual == _Quals::_None) {
    return __identity<_Ret(_Args...)>{};
  } else if constexpr (__qual == _Quals::_Const) {
    return __identity<_Ret(_Args...) const>{};
  } else if constexpr (__qual == _Quals::_LVRef) {
    return __identity<_Ret(_Args...)&>{};
  } else if constexpr (__qual == (_Quals::_Const | _Quals::_LVRef)) {
    return __identity<_Ret(_Args...) const&>{};
  } else if constexpr (__qual == _Quals::_RVRef) {
    return __identity<_Ret(_Args...) &&>{};
  } else if constexpr (__qual == (_Quals::_Const | _Quals::_RVRef)) {
    return __identity<_Ret(_Args...) const&&>{};
  } else if constexpr (__qual == _Quals::_NoExcept) {
    return __identity<_Ret(_Args...) noexcept>{};
  } else if constexpr (__qual == (_Quals::_Const | _Quals::_NoExcept)) {
    return __identity<_Ret(_Args...) const noexcept>{};
  } else if constexpr (__qual == (_Quals::_LVRef | _Quals::_NoExcept)) {
    return __identity<_Ret(_Args...) & noexcept>{};
  } else if constexpr (__qual == (_Quals::_Const | _Quals::_LVRef | _Quals::_NoExcept)) {
    return __identity<_Ret(_Args...) const & noexcept>{};
  } else if constexpr (__qual == (_Quals::_RVRef | _Quals::_NoExcept)) {
    return __identity < _Ret(_Args...) && noexcept > {};
  } else if constexpr (__qual == (_Quals::_Const | _Quals::_RVRef | _Quals::_NoExcept)) {
    return __identity < _Ret(_Args...) const && noexcept > {};
  } else {
    static_assert(sizeof(__qual) == 0, "Invalid qualifier");
  }
}

template <_Quals __qual, class _Ret>
constexpr auto __qualify_type_impl() {
  static_assert(!__is_noexcept(__qual), "Noexcept qualifier is not allowed on type");
  if constexpr (__qual == _Quals::_None) {
    return __identity<_Ret>{};
  } else if constexpr (__qual == _Quals::_Const) {
    return __identity<_Ret const>{};
  } else if constexpr (__qual == _Quals::_LVRef) {
    return __identity<_Ret&>{};
  } else if constexpr (__qual == (_Quals::_Const | _Quals::_LVRef)) {
    return __identity<_Ret const&>{};
  } else if constexpr (__qual == _Quals::_RVRef) {
    return __identity<_Ret&&>{};
  } else if constexpr (__qual == (_Quals::_Const | _Quals::_RVRef)) {
    return __identity<_Ret const&&>{};
  } else {
    static_assert(sizeof(__qual) == 0, "Invalid qualifier");
  }
}
template <_Quals __qual, class _Ret>
using __qualify_type_t = typename decltype(__qualify_type_impl<__qual, _Ret>())::type;

template <_Quals __quals, class _Tp>
__qualify_type_t<__quals, _Tp> __cast_as(void* __ptr) {
  return std::forward<__qualify_type_t<__quals, _Tp>>(*static_cast<_Tp*>(__ptr));
}
template <class _Tp, _Quals __q>
struct __move_only_function_base;

struct __move_only_function_buffer {
  union {
    void* __ptr_;
    std::byte __buf_[3 * sizeof(void*)];
  };
};

struct _AllocGuard {
  _AllocGuard()                   = delete;
  _AllocGuard(_AllocGuard const&) = delete;

  _AllocGuard(size_t __size, size_t __align)
      : __ptr_(__libcpp_allocate(__size, __align)), __size_(__size), __align_(__align) {}

  void* __release() {
    void* __ret = __ptr_;
    __ptr_      = nullptr;
    return __ret;
  }

  ~_AllocGuard() {
    if (__ptr_) {
      __libcpp_deallocate(__ptr_, __size_, __align_);
    }
  }

  void* __ptr_;
  size_t __size_;
  size_t __align_;
};

template <class _Functor>
constexpr bool __use_inline_storage =
    sizeof(_Functor) <= sizeof(__move_only_function_buffer) &&
    alignof(_Functor) <= alignof(__move_only_function_buffer) && std::is_trivially_destructible_v<_Functor> &&
    std::is_trivially_move_constructible_v<_Functor>;

template <class...>
struct move_only_function;

template <class _Ret, class... _Args, _Quals __q>
struct __move_only_function_base<_Ret(_Args...), __q> {
private:
  using _BufferT = __move_only_function_buffer;

  static constexpr _Quals __inv_quals =
      __ref_quals(__q) == _Quals::_None ? __cvref_quals(__q) | _Quals::_LVRef : __cvref_quals(__q);

  struct _VTable {
    using __destroy_t = void (*)(_BufferT&);
    using __call_t    = _Ret (*)(_BufferT&, _Args...) noexcept(__is_noexcept(__q));

    __call_t __call_       = nullptr;
    __destroy_t __destroy_ = nullptr;

    template <class _RawFunctor>
    static constexpr _VTable __create() {
      using _Functor = std::decay_t<_RawFunctor>;

      if constexpr (__use_inline_storage<_Functor>) {
        return _VTable{.__call_ = [](_BufferT& __buffer, _Args... __args) noexcept(__is_noexcept(__q)) -> _Ret {
                         return std::invoke_r<_Ret>(
                             __cast_as<__inv_quals, _Functor>(__buffer.__buf_), std::forward<_Args>(__args)...);
                       },
                       .__destroy_ = nullptr};
      } else {
        return _VTable{.__call_ = [](_BufferT& __buffer, _Args... __args) noexcept(__is_noexcept(__q)) -> _Ret {
                         return std::invoke_r<_Ret>(
                             __cast_as<__inv_quals, _Functor>(__buffer.__ptr_), std::forward<_Args>(__args)...);
                       },
                       .__destroy_ = [](_BufferT& __buffer) noexcept -> void {
                         static_cast<_Functor*>(__buffer.__ptr_)->~_Functor();
                         __libcpp_deallocate(__buffer.__ptr_, sizeof(_Functor), alignof(_Functor));
                       }};
      };
    }
  };

  template <class _Functor>
  static constexpr _VTable __functor_vtable = _VTable::template __create<_Functor>();

  mutable _BufferT __buffer_;
  const _VTable* __vtable_ = {};

  template <class _VT>
  static constexpr bool __is_callable_from = []() {
    using _DVT = decay_t<_VT>;
    if constexpr (__is_noexcept(__q)) {
      return is_nothrow_invocable_r_v<_Ret, __qualify_type_t<__cvref_quals(__q), _DVT>, _Args...> &&
             is_nothrow_invocable_r_v<_Ret, __qualify_type_t<__inv_quals, _DVT>, _Args...>;
    } else {
      return is_invocable_r_v<_Ret, __qualify_type_t<__cvref_quals(__q), _DVT>, _Args...> &&
             is_invocable_r_v<_Ret, __qualify_type_t<__inv_quals, _DVT>, _Args...>;
    }
  }();

  template <class _Func, class... _FArgs>
  void __construct(_FArgs&&... __args) {
    using _DFunc = decay_t<_Func>;
    if (__use_inline_storage<_DFunc>) {
      ::new (&__buffer_.__buf_) _DFunc(std::forward<_FArgs>(__args)...);
    } else {
      _AllocGuard __alloc(sizeof(_DFunc), alignof(_DFunc));
      ::new (__alloc.__ptr_) _DFunc(std::forward<_FArgs>(__args)...);
      __buffer_.__ptr_ = __alloc.__release();
    }
    __vtable_ = &__functor_vtable<_Func>;
  }

  _LIBCPP_HIDE_FROM_ABI void __reset() {
    if (__vtable_ && __vtable_->__destroy_)
      __vtable_->__destroy_(__buffer_);
    __vtable_ = nullptr;
  }

public:
  using result_type = _Ret;

  // [func.wrap.move.ctor]
  __move_only_function_base() noexcept = default;

  _LIBCPP_HIDE_FROM_ABI __move_only_function_base(__move_only_function_base&& __other) noexcept
      : __vtable_(__other.__vtable_), __buffer_(__other.__buffer_) {
    __other.__vtable_ = {};
  }

  template <_Quals __call_qual>
  static constexpr bool __has_cvref_quals = (__cvref_quals(__q) & __call_qual) == __call_qual;

  _LIBCPP_HIDE_FROM_ABI ~__move_only_function_base() { __reset(); }

  _LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) noexcept(__is_noexcept(__q))
    requires __has_cvref_quals<_Quals::_None>
  {
    _LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
    return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
  }

  _LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) const noexcept(__is_noexcept(__q))
    requires __has_cvref_quals<_Quals::_Const>
  {
    _LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
    return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
  }

  _LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) & noexcept(__is_noexcept(__q))
    requires __has_cvref_quals< _Quals::_LVRef>
  {
    _LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
    return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
  }

  _LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) && noexcept(__is_noexcept(__q))
    requires __has_cvref_quals<_Quals::_RVRef>
  {
    _LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
    return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
  }

  _LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) const& noexcept(__is_noexcept(__q))
    requires __has_cvref_quals<_Quals::_Const | _Quals::_LVRef>
  {
    _LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
    return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
  }

  _LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) const&& noexcept(__is_noexcept(__q))
    requires __has_cvref_quals<_Quals::_Const | _Quals::_RVRef>
  {
    _LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
    return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
  }

private:
  template <class, _Quals>
  friend struct __move_only_function_base;
  template <class...>
  friend struct move_only_function;
};

template <class _Ret>
struct __is_move_only_function : false_type {};
template <class... _Obj>
struct __is_move_only_function<move_only_function<_Obj...>> : true_type{};

template <class _Tp>
struct move_only_function<_Tp>
    : __move_only_function_base<typename __unqualify_type<_Tp>::type, __unqualify_type<_Tp>::__qual> {
  using __base = __move_only_function_base<typename __unqualify_type<_Tp>::type, __unqualify_type<_Tp>::__qual>;

  move_only_function() noexcept = default;
  _LIBCPP_HIDE_FROM_ABI move_only_function(nullptr_t) noexcept {}
  move_only_function(move_only_function const&) = delete;
  move_only_function(move_only_function&&)      = default;

  ~move_only_function() = default;

  template <class _Func>
    requires(!is_same_v<remove_cvref_t<_Func>, move_only_function> && !__is_inplace_type<_Func>::value &&
             __base::template __is_callable_from<_Func>)
  _LIBCPP_HIDE_FROM_ABI move_only_function(_Func&& __func) {
    using _StoredFunc = decay_t<_Func>;

    if constexpr ((is_pointer_v<_StoredFunc> && is_function_v<remove_pointer_t<_StoredFunc>>) ||
                  is_member_function_pointer_v<_StoredFunc>) {
      if (__func != nullptr) {
        this->template __construct<_StoredFunc>(std::forward<_Func>(__func));
      }
    } else if constexpr (__is_move_only_function<_StoredFunc>::value) {
      if (__func) {
        std::swap(this->__vtable_, __func.__vtable_);
        this->__buffer_ = __func.__buffer_;
      }
    } else {
      this->template __construct<_Func>(std::forward<_Func>(__func));
    }
  }

  template <class _Func, class... _FArgs>
    requires is_constructible_v<decay_t<_Func>, _FArgs...> && __base::template
  __is_callable_from<_Func> _LIBCPP_HIDE_FROM_ABI explicit move_only_function(
      in_place_type_t<_Func>, _FArgs&&... __args) {
    static_assert(is_same_v<decay_t<_Func>, _Func>);
    this->template __construct<_Func>(std::forward<_FArgs>(__args)...);
  }

  template <class _Func, class _InitListType, class... _Args>
    requires is_constructible_v<decay_t<_Func>, initializer_list<_InitListType>&, _Args...> && __base::template
  __is_callable_from<_Func> _LIBCPP_HIDE_FROM_ABI explicit move_only_function(
      in_place_type_t<_Func>, initializer_list<_InitListType> __il, _Args&&... __args) {
    static_assert(is_same_v<decay_t<_Func>, _Func>);
    this->template __construct<_Func>(__il, std::forward<_Args>(__args)...);
  }

  _LIBCPP_HIDE_FROM_ABI move_only_function& operator=(move_only_function&& __other) noexcept {
    move_only_function(std::move(__other)).swap(*this);
    return *this;
  }

  _LIBCPP_HIDE_FROM_ABI move_only_function& operator=(nullptr_t) noexcept {
    this->__reset();
    return *this;
  }

  template <class _Func>
    requires(!is_same_v<remove_cvref_t<_Func>, move_only_function> && !__is_inplace_type<_Func>::value &&
             __base::template __is_callable_from<_Func>)
  _LIBCPP_HIDE_FROM_ABI move_only_function& operator=(_Func&& __func) {
    move_only_function(std::forward<_Func>(__func)).swap(*this);
    return *this;
  }

  explicit operator bool() const noexcept { return this->__vtable_ != nullptr; }
  using __base::operator();

  // [func.wrap.move.util]
  _LIBCPP_HIDE_FROM_ABI void swap(move_only_function& __other) noexcept {
    std::swap(this->__vtable_, __other.__vtable_);
    std::swap(this->__buffer_, __other.__buffer_);
  }

  _LIBCPP_HIDE_FROM_ABI friend void swap(move_only_function& __lhs, move_only_function& __rhs) noexcept {
    __lhs.swap(__rhs);
  }

  _LIBCPP_HIDE_FROM_ABI friend bool operator==(const move_only_function& __func, nullptr_t) noexcept { return !__func; }
};

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_H


```

https://github.com/llvm/llvm-project/pull/94670


More information about the llvm-branch-commits mailing list