Skip to content

Instantly share code, notes, and snippets.

@razielanarki
Created June 27, 2023 20:12
Show Gist options
  • Save razielanarki/294645c4e9467b0a91e01cd6f730c5fa to your computer and use it in GitHub Desktop.
Save razielanarki/294645c4e9467b0a91e01cd6f730c5fa to your computer and use it in GitHub Desktop.
constexpr function_ref based on p0792r14 (sans the nontype parts)
#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