Created
June 27, 2023 20:12
-
-
Save razielanarki/294645c4e9467b0a91e01cd6f730c5fa to your computer and use it in GitHub Desktop.
constexpr function_ref based on p0792r14 (sans the nontype parts)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <type_traits> | |
namespace nonstd | |
{ | |
// ============================================================================ | |
namespace detail | |
{ | |
// ---------------------------------------------------------------------------- | |
template<class Sig> | |
struct signature; | |
template<class R, class...Args> | |
struct signature<R( Args... )> | |
{ | |
typedef R function( Args... ); | |
//using function = std::type_identity_t<R( Args... )>; | |
static constexpr bool is_noexcept = false; | |
static constexpr bool is_const = false; | |
}; | |
template<class R, class...Args> | |
struct signature<R( Args... ) noexcept> | |
: signature<R(Args...)> | |
{ | |
static constexpr bool is_noexcept = true; | |
}; | |
template<class R, class... Args> | |
struct signature<R( Args... ) const> | |
: signature<R( Args... )> | |
{ | |
static constexpr bool is_const = true; | |
}; | |
template<class R, class... Args> | |
struct signature<R( Args... ) const noexcept> | |
: signature<R( Args... ) noexcept> | |
{ | |
static constexpr bool is_const = true; | |
}; | |
// ---------------------------------------------------------------------------- | |
struct function_ref_base | |
{ | |
union storage_t | |
{ | |
void* p = nullptr; | |
const void* cp; | |
void ( *fp )( ); | |
constexpr storage_t() noexcept = default; | |
template<class T> | |
requires std::is_object_v<T> | |
constexpr explicit storage_t( T* t ) : p( t ) | |
{ | |
} | |
template<class T> | |
requires std::is_object_v<T> | |
constexpr explicit storage_t( const T* t ) : cp( t ) | |
{ | |
} | |
template<class T> | |
requires std::is_function_v<T> | |
constexpr explicit storage_t( T* t ) | |
: fp( reinterpret_cast<decltype( fp )>( t ) ) | |
{ | |
} | |
template<class T> | |
requires std::is_const_v<T> | |
constexpr T* as() | |
{ | |
return static_cast<T*>( cp ); | |
} | |
template<class T> | |
requires std::is_object_v<T> | |
constexpr T* as() | |
{ | |
return static_cast<T*>( p ); | |
} | |
template<class T> | |
requires std::is_function_v<T> | |
constexpr T* as() | |
{ | |
return reinterpret_cast<T*>( fp ); | |
} | |
}; | |
}; | |
} // namespace nonstd::detail | |
// ============================================================================ | |
template<class Sig, class = typename detail::signature<Sig>::function> | |
class function_ref; | |
// ---------------------------------------------------------------------------- | |
template<class Sig, class R, class...Args> | |
class function_ref<Sig, R( Args... )> | |
: detail::function_ref_base | |
{ | |
using signature = detail::signature<Sig>; | |
static constexpr bool is_noexcept = signature::is_noexcept; | |
static constexpr bool is_const = signature::is_noexcept; | |
template<class T> | |
using cv_t = std::conditional_t<is_const, const T, T>; | |
template<class T> | |
using fwd_t = std::conditional_t<std::is_trivially_copyable_v<T>, T, std::add_rvalue_reference_t<T>>; | |
typedef R invoker_t( storage_t, fwd_t<Args>... ) noexcept ( is_noexcept ); | |
//using invoker_t = std::type_identity_t<R( storage_t, fwd_t<Args>... ) noexcept ( is_noexcept )>; //MSVC2022 (cl.exe v19.37.32705) chokes on this | |
invoker_t* invoker = nullptr; | |
storage_t storage; | |
public: | |
constexpr function_ref( nullptr_t ) = delete; | |
template<class Fn> | |
constexpr function_ref( Fn* fn ) noexcept | |
requires ( | |
std::is_function_v<Fn> and | |
std::is_invocable_r_v<R, Fn, Args...> | |
) | |
: invoker( []( storage_t storage, fwd_t<Args>...args ) noexcept ( is_noexcept ) -> R | |
{ | |
return storage.as<Fn>()( static_cast<decltype( args )>( args )... ); | |
} ) | |
, storage( fn ) | |
{ | |
} | |
template<class Ftor> | |
constexpr function_ref( Ftor&& ftor ) noexcept | |
requires ( | |
not std::is_same_v<std::remove_cvref_t<Ftor>, function_ref> and | |
not std::is_member_pointer_v<std::remove_reference_t<Ftor>> and | |
std::is_invocable_r_v<R, cv_t<std::remove_reference_t<Ftor>>&, Args...> | |
) | |
: invoker( []( storage_t storage, fwd_t<Args>... args ) noexcept ( is_noexcept ) -> R | |
{ | |
cv_t<std::remove_reference_t<Ftor>>& obj = *storage.as<std::remove_reference_t<Ftor>>(); | |
return obj( static_cast<decltype( args )>( args )... ); | |
} ) | |
, storage( std::addressof( ftor ) ) | |
{ | |
} | |
public: | |
template<class T> | |
function_ref& operator=(T) | |
requires( | |
not std::is_same_v<std::remove_cvref_t<T>, function_ref> and | |
not std::is_pointer_v<T> | |
) | |
= delete; | |
public: | |
constexpr R operator()( Args... args ) const noexcept( is_noexcept ) | |
{ | |
return invoker( storage, std::forward<Args>( args )... ); | |
} | |
}; | |
// ---------------------------------------------------------------------------- | |
template<class F> | |
requires std::is_function_v<F> | |
function_ref( F* ) -> function_ref<F>; | |
// ============================================================================ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment