Some time ago I implemented dynamic_array
and posted it on Code Review. It used std::function
internally for type erasure. This inspired me to implement a standard-conforming std::function
myself. This is just a showcase of how static polymorphism with templates can play well with dynamic polymorphism with inheritance and virtual functions.
Incidentally, some time ago I implemented std::invoke
and my own invoke_r
and also posted it on Code Review. Internally, function
uses invoke_r
. Its implementation is very short, so I included it as part of the code.
Here is the function.hpp
header: (the deduction guide was really painful to write)
// C++17 std::function implementation
// [func.wrap] and [depr.func.adaptor.typedefs]
#ifndef INC_FUNCTION_HPP_QLxVtsa5Mv
#define INC_FUNCTION_HPP_QLxVtsa5Mv
#include <cstddef>
#include <exception>
#include <functional>
#include <memory>
#include <typeinfo>
#include <type_traits>
namespace my_std {
// [func.wrap.badcall], class bad_function_call
struct bad_function_call :std::exception {
// [func.wrap.badcall.const], constructor
bad_function_call() = default;
};
// [func.wrap.func], class template function
template <class F>
class function; // not defined
namespace detail {
// [depr.func.adaptor.typedefs], typedefs to support function binders
template <class... Args>
struct function_base {};
template <class T>
struct function_base<T> {
using argument_type [[deprecated]] = T;
};
template <class T, class U>
struct function_base<T, U> {
using first_argument_type [[deprecated]] = T;
using second_argument_type [[deprecated]] = U;
};
// trait to check if a type is a pointer to function
template <class T>
struct is_function_pointer :std::false_type {};
template <class T>
struct is_function_pointer<T*> :std::is_function<T> {};
template <class T>
inline constexpr bool is_function_pointer_v = is_function_pointer<T>::value;
// trait to check if a type is a specialization of the class template function
template <class T>
struct is_function :std::false_type {};
template <class T>
struct is_function<function<T>> :std::true_type {};
template <class T>
inline constexpr bool is_function_v = is_function<T>::value;
// INVOKE<R>(f, args...), see [func.require]/2
template <class R, class F, class... Args>
R invoke_r(F&& f, Args&&... args)
noexcept(std::is_nothrow_invocable_r_v<R, F, Args...>)
{
if constexpr (std::is_void_v<R>)
return static_cast<void>(
std::invoke(std::forward<F>(f), std::forward<Args>(args)...)
);
else
return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
// polymorphic base for callable wrappers
template <class R, class... Args>
struct callable_base {
virtual ~callable_base() {}
virtual R invoke(Args...) const = 0;
virtual std::unique_ptr<callable_base> clone() const = 0;
virtual const std::type_info& target_type() const noexcept = 0;
virtual void* target() const noexcept = 0;
};
// callable wrapper
template <class F, class R, class... Args>
struct callable :callable_base<R, Args...> {
using Base = callable_base<R, Args...>;
public:
callable(F f)
:func{std::move(f)}
{
}
~callable() {}
R invoke(Args... args) const override
{
return invoke_r<R>(func, std::forward<Args>(args)...);
}
std::unique_ptr<Base> clone() const override
{
return std::unique_ptr<Base>{new callable{func}};
}
const std::type_info& target_type() const noexcept override
{
return typeid(F);
}
void* target() const noexcept override
{
return &func;
}
private:
mutable F func;
};
}
// [func.wrap.func], class template function
template <class R, class... Args>
class function<R(Args...)> :public detail::function_base<Args...> {
public:
using result_type = R;
// [func.wrap.func.con], construct/copy/destroy
function() noexcept
{
}
function(std::nullptr_t) noexcept
{
}
function(const function& f)
:func{f ? f.func->clone() : nullptr}
{
}
function(function&& f) noexcept // strengthened
{
swap(f);
}
template <class F,
std::enable_if_t<std::is_invocable_r_v<R, F&, Args...>, int> = 0>
function(F f)
{
if constexpr (detail::is_function_pointer_v<F> ||
std::is_member_pointer_v<F> ||
detail::is_function_v<F>) {
if (!f) return;
}
func.reset(new detail::callable<F, R, Args...>{std::move(f)});
}
function& operator=(const function& f)
{
function{f}.swap(*this);
return *this;
}
function& operator=(function&& f)
{
swap(f);
return *this;
}
function& operator=(std::nullptr_t) noexcept
{
func.reset();
return *this;
}
template <class F,
std::enable_if_t<
std::is_invocable_r_v<R, std::decay_t<F>&, Args...>,
int> = 0>
function& operator=(F&& f)
{
function{std::forward<F>(f)}.swap(*this);
return *this;
}
template <class F>
function& operator=(std::reference_wrapper<F> f) noexcept
{
function{f}.swap(*this);
return *this;
}
~function() = default;
// [func.wrap.func.mod], function modifiers
void swap(function& other) noexcept
{
using std::swap;
swap(func, other.func);
}
// [func.wrap.func.cap], function capacity
explicit operator bool() const noexcept
{
return static_cast<bool>(func);
}
// [func.wrap.func.inv], function invocation
R operator()(Args... args) const
{
if (*this)
return func->invoke(std::forward<Args>(args)...);
else
throw bad_function_call{};
}
// [func.wrap.func.targ], function target access
const type_info& target_type() const noexcept
{
if (*this)
return func->target_type();
else
return typeid(void);
}
template <class T>
T* target() noexcept
{
if (target_type() == typeid(T))
return reinterpret_cast<T*>(func->target());
else
return nullptr;
}
template <class T>
const T* target() const noexcept
{
if (target_type() == typeid(T))
return reinterpret_cast<const T*>(func->target());
else
return nullptr;
}
private:
std::unique_ptr<detail::callable_base<R, Args...>> func = nullptr;
};
namespace detail {
template <typename T>
struct deduce_function {};
template <typename T>
using deduce_function_t = typename deduce_function<T>::type;
template <typename G, typename R, typename... Args>
struct deduce_function<R (G::*)(Args...)> {
using type = R(Args...);
};
template <typename G, typename R, typename... Args>
struct deduce_function<R (G::*)(Args...) const> {
using type = R(Args...);
};
template <typename G, typename R, typename... Args>
struct deduce_function<R (G::*)(Args...) volatile> {
using type = R(Args...);
};
template <typename G, typename R, typename... Args>
struct deduce_function<R (G::*)(Args...) const volatile> {
using type = R(Args...);
};
template <typename G, typename R, typename... Args>
struct deduce_function<R (G::*)(Args...) &> {
using type = R(Args...);
};
template <typename G, typename R, typename... Args>
struct deduce_function<R (G::*)(Args...) const &> {
using type = R(Args...);
};
template <typename G, typename R, typename... Args>
struct deduce_function<R (G::*)(Args...) volatile &> {
using type = R(Args...);
};
template <typename G, typename R, typename... Args>
struct deduce_function<R (G::*)(Args...) const volatile &> {
using type = R(Args...);
};
template <typename G, typename R, typename... Args>
struct deduce_function<R (G::*)(Args...) noexcept> {
using type = R(Args...);
};
template <typename G, typename R, typename... Args>
struct deduce_function<R (G::*)(Args...) const noexcept> {
using type = R(Args...);
};
template <typename G, typename R, typename... Args>
struct deduce_function<R (G::*)(Args...) volatile noexcept> {
using type = R(Args...);
};
template <typename G, typename R, typename... Args>
struct deduce_function<R (G::*)(Args...) const volatile noexcept> {
using type = R(Args...);
};
template <typename G, typename R, typename... Args>
struct deduce_function<R (G::*)(Args...) & noexcept> {
using type = R(Args...);
};
template <typename G, typename R, typename... Args>
struct deduce_function<R (G::*)(Args...) const & noexcept> {
using type = R(Args...);
};
template <typename G, typename R, typename... Args>
struct deduce_function<R (G::*)(Args...) volatile & noexcept> {
using type = R(Args...);
};
template <typename G, typename R, typename... Args>
struct deduce_function<R (G::*)(Args...) const volatile & noexcept> {
using type = R(Args...);
};
}
template <class R, class... Args>
function(R (*)(Args...)) -> function<R(Args...)>;
template <class F, class Op = decltype(&F::operator())>
function(F) -> function<detail::deduce_function_t<Op>>;
// [func.wrap.func.nullptr], null pointer comparisons
template <class R, class... Args>
bool operator==(const function<R(Args...)>& f, std::nullptr_t) noexcept
{
return !f;
}
template <class R, class... Args>
bool operator==(std::nullptr_t, const function<R(Args...)>& f) noexcept
{
return !f;
}
template <class R, class... Args>
bool operator!=(const function<R(Args...)>& f, std::nullptr_t) noexcept
{
return static_cast<bool>(f);
}
template <class R, class... Args>
bool operator!=(std::nullptr_t, const function<R(Args...)>& f) noexcept
{
return static_cast<bool>(f);
}
// [func.wrap.func.alg], specialized algorithms
template <class R, class... Args>
void swap(function<R(Args...)>& lhs, function<R(Args...)>& rhs) noexcept
{
lhs.swap(rhs);
}
}
#endif