llvm-project/orc-rt/include/orc-rt/move_only_function.h
Lang Hames 735522a3c7
[orc-rt] Restore perfect forwarding to move_only_function init (#157784)
After the recent change to hoist std::decay_t (cd8f47b2d4e) we were
forcing move-initialization of the callable type. This commit restores
perfect forwarding so that we copy-initialize where expected.
2025-09-10 13:26:23 +10:00

147 lines
4.8 KiB
C++

//===-- move_only_function.h - moveable, type-erasing function --*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// A substitute for std::move_only_function that can be used until the ORC
/// runtime is allowed to assume c++-23.
///
/// TODO: Replace all uses with std::move_only_function once we can assume
/// c++-23.
///
/// TODO: Re-implement using techniques from LLVM's unique_function
/// (llvm/include/llvm/ADT/FunctionExtras.h), which uses some extra
/// inline storage to avoid heap allocations for small objects. This
/// would require first porting some other LLVM utilities like
/// PointerIntPair, PointerUnion, and PointerLikeTypeTraits. (These are
/// likely to be independently useful in the orc runtime, so porting will
/// have additional benefits).
///
//===----------------------------------------------------------------------===//
#ifndef ORC_RT_MOVE_ONLY_FUNCTION_H
#define ORC_RT_MOVE_ONLY_FUNCTION_H
#include <memory>
#include <type_traits>
namespace orc_rt {
namespace move_only_function_detail {
template <typename RetT, typename... ArgTs> class GenericCallable {
public:
virtual ~GenericCallable() = default;
virtual RetT call(ArgTs &&...Args) = 0;
};
template <typename CallableT, typename RetT, typename... ArgTs>
class GenericCallableImpl : public GenericCallable<RetT, ArgTs...> {
public:
template <typename CallableInitT>
GenericCallableImpl(CallableInitT &&Callable)
: Callable(std::forward<CallableInitT>(Callable)) {}
RetT call(ArgTs &&...Args) override {
return Callable(std::forward<ArgTs>(Args)...);
}
private:
CallableT Callable;
};
template <typename RetT, typename... ArgTs> class GenericConstCallable {
public:
virtual ~GenericConstCallable() = default;
virtual RetT call(ArgTs &&...Args) const = 0;
};
template <typename CallableT, typename RetT, typename... ArgTs>
class GenericConstCallableImpl : public GenericConstCallable<RetT, ArgTs...> {
public:
template <typename CallableInitT>
GenericConstCallableImpl(CallableInitT &&Callable)
: Callable(std::forward<CallableInitT>(Callable)) {}
RetT call(ArgTs &&...Args) const override {
return Callable(std::forward<ArgTs>(Args)...);
}
private:
CallableT Callable;
};
} // namespace move_only_function_detail
template <typename FnT> class move_only_function;
template <typename RetT, typename... ArgTs>
class move_only_function<RetT(ArgTs...)> {
private:
using GenericCallable =
move_only_function_detail::GenericCallable<RetT, ArgTs...>;
template <typename CallableT>
using GenericCallableImpl =
move_only_function_detail::GenericCallableImpl<CallableT, RetT, ArgTs...>;
public:
move_only_function() = default;
move_only_function(std::nullptr_t) {}
move_only_function(move_only_function &&) = default;
move_only_function(const move_only_function &) = delete;
move_only_function &operator=(move_only_function &&) = default;
move_only_function &operator=(const move_only_function &) = delete;
template <typename CallableT>
move_only_function(CallableT &&Callable)
: C(std::make_unique<GenericCallableImpl<std::decay_t<CallableT>>>(
std::forward<CallableT>(Callable))) {}
RetT operator()(ArgTs... Params) const {
return C->call(std::forward<ArgTs>(Params)...);
}
explicit operator bool() const { return !!C; }
private:
std::unique_ptr<GenericCallable> C;
};
template <typename RetT, typename... ArgTs>
class move_only_function<RetT(ArgTs...) const> {
private:
using GenericCallable =
move_only_function_detail::GenericConstCallable<RetT, ArgTs...>;
template <typename CallableT>
using GenericCallableImpl =
move_only_function_detail::GenericConstCallableImpl<CallableT, RetT,
ArgTs...>;
public:
move_only_function() = default;
move_only_function(std::nullptr_t) {}
move_only_function(move_only_function &&) = default;
move_only_function(const move_only_function &) = delete;
move_only_function &operator=(move_only_function &&) = default;
move_only_function &operator=(const move_only_function &) = delete;
template <typename CallableT>
move_only_function(CallableT &&Callable)
: C(std::make_unique<const GenericCallableImpl<std::decay_t<CallableT>>>(
std::forward<CallableT>(Callable))) {}
RetT operator()(ArgTs... Params) const {
return C->call(std::forward<ArgTs>(Params)...);
}
explicit operator bool() const { return !!C; }
private:
std::unique_ptr<const GenericCallable> C;
};
} // namespace orc_rt
#endif // ORC_RT_MOVE_ONLY_FUNCTION_H