Request Short Link
C++ 98
C++ 11
C++ 14
C++ 17
C++ 20
C++ 23
C++ 2c
for-loops as while-loops
array subscription
Show all implicit casts
Show all template parameters of a CallExpr
Use libc++
Transform std::initializer_list
Show noexcept internals
Show padding information
Show coroutine transformation
Show C++ to C transformation
Show object lifetime
Default
15
18
20
22
26
More
GitHub
Patreon
Issues
About
Policies
Examples
C++ Insights @ YouTube
Settings
Version
New C++ Insights Episode
×
Made by
Andreas Fertig
Powered by
Flask
and
CodeMirror
Source:
#include <memory> #include <type_traits> template <auto V> struct nontype_t { explicit nontype_t() = default; }; template <auto V> constexpr nontype_t<V> nontype{}; namespace detail { template <typename> struct is_nontype : std::false_type {}; template <auto f> struct is_nontype<nontype_t<f>> : std::true_type {}; template <typename T> inline constexpr bool is_nontype_v = is_nontype<T>::value; } // namespace detail namespace detail { template <typename> struct fn_types; template <typename R, typename... ARG_Ts> struct fn_types<R(ARG_Ts...)> { using type = R(ARG_Ts...); }; template <typename R, typename... ARG_Ts> struct fn_types<R(ARG_Ts...) const> { using type = R(ARG_Ts...); }; template <typename R, typename... ARG_Ts> struct fn_types<R(ARG_Ts...) noexcept> { using type = R(ARG_Ts...); }; template <typename R, typename... ARG_Ts> struct fn_types<R(ARG_Ts...) const noexcept> { using type = R(ARG_Ts...); }; template <typename SIG_T> using fn_types_t = typename fn_types<SIG_T>::type; static_assert(std::is_same_v<fn_types_t<void(int)>, void(int)>); static_assert(std::is_same_v<fn_types_t<int(void) noexcept>, int()>); static_assert(std::is_same_v<fn_types_t<int(bool) const noexcept>, int(bool)>); static_assert(std::is_same_v<fn_types_t<char(int) const throw()>, char(int)>); } // namespace detail namespace detail { template <typename> struct is_nothrow_fn; template <typename R, typename... ARG_Ts> struct is_nothrow_fn<R(ARG_Ts...)> : std::false_type {}; template <typename R, typename... ARG_Ts> struct is_nothrow_fn<R(ARG_Ts...) const> : std::false_type {}; template <typename R, typename... ARG_Ts> struct is_nothrow_fn<R(ARG_Ts...) noexcept> : std::true_type {}; template <typename R, typename... ARG_Ts> struct is_nothrow_fn<R(ARG_Ts...) const noexcept> : std::true_type {}; template <typename SIG_T> inline constexpr bool is_nothrow_fn_v = is_nothrow_fn<SIG_T>::value; static_assert(!is_nothrow_fn_v<int()>); static_assert(!is_nothrow_fn_v<char(int) const>); static_assert(is_nothrow_fn_v<char(int) noexcept>); static_assert(is_nothrow_fn_v<bool() const noexcept>); } // namespace detail namespace detail { template <typename, bool, typename...> struct is_invocable_using; template <typename R, typename... ARG_Ts, typename... T> struct is_invocable_using<R(ARG_Ts...), true, T...> : std::is_nothrow_invocable_r<R, T..., ARG_Ts...> {}; template <typename R, typename... ARG_Ts, typename... T> struct is_invocable_using<R(ARG_Ts...), false, T...> : std::is_invocable_r<R, T..., ARG_Ts...> {}; template <typename SIG_T, typename... T> inline constexpr bool is_invocable_using_v = is_invocable_using<fn_types_t<SIG_T>, is_nothrow_fn_v<SIG_T>, T...>::value; } // namespace detail namespace detail { template <typename> struct is_const_fn; template <typename R, typename... ARG_Ts> struct is_const_fn<R(ARG_Ts...)> : std::false_type {}; template <typename R, typename... ARG_Ts> struct is_const_fn<R(ARG_Ts...) const> : std::true_type {}; template <typename R, typename... ARG_Ts> struct is_const_fn<R(ARG_Ts...) noexcept> : std::false_type {}; template <typename R, typename... ARG_Ts> struct is_const_fn<R(ARG_Ts...) const noexcept> : std::true_type {}; template <typename SIG_T> inline constexpr bool is_const_fn_v = is_const_fn<SIG_T>::value; static_assert(!is_const_fn_v<int()>); static_assert(is_const_fn_v<char(int) const>); static_assert(!is_const_fn_v<char(int) noexcept>); static_assert(is_const_fn_v<bool() const noexcept>); } // namespace detail namespace detail { template <typename SIG_T, bool is_const = is_const_fn_v<SIG_T>> struct cv_qualifier { template <typename T> using cv = const T; }; template <typename SIG_T> struct cv_qualifier<SIG_T, false> { template <typename T> using cv = T; }; static_assert(std::is_same_v<cv_qualifier<void(int)>::cv<int>, int>); static_assert(std::is_same_v<cv_qualifier<int(void) noexcept>::cv<char>, char>); static_assert(std::is_same_v<cv_qualifier<int(bool) const noexcept>::cv<short>, const short>); static_assert( std::is_same_v<cv_qualifier<char(int) const>::cv<bool>, const bool>); } // namespace detail namespace detail { union bound_entity_type { void* obj_ptr{nullptr}; const void* const_obj_ptr; void (*fn_ptr)(); constexpr bound_entity_type() noexcept = default; template <typename T> requires std::is_object_v<T> constexpr explicit bound_entity_type(T* ptr) noexcept : obj_ptr(ptr) {} template <typename T> requires std::is_object_v<T> constexpr explicit bound_entity_type(const T* ptr) noexcept : const_obj_ptr(ptr) {} template <class T> requires std::is_function_v<T> constexpr explicit bound_entity_type(T* ptr) noexcept : fn_ptr(reinterpret_cast<decltype(fn_ptr)>(ptr)) {} }; static_assert(std::is_trivially_copyable_v<bound_entity_type>); template <typename T> constexpr static auto get(bound_entity_type entity) { if constexpr (std::is_const_v<T>) { return static_cast<T*>(entity.const_obj_ptr); } else if constexpr (std::is_object_v<T>) { return static_cast<T*>(entity.obj_ptr); } else { return reinterpret_cast<T*>(entity.fn_ptr); } } } // namespace detail #include <cassert> #include <functional> template <typename SIG_T, typename = detail::fn_types_t<SIG_T>> class function_ref; template <typename SIG_T, typename R, typename... ARG_Ts> class function_ref<SIG_T, R(ARG_Ts...)> { template <typename... T> static constexpr bool is_invocable_using = detail::is_invocable_using_v<SIG_T, T...>; using cv_qualifier = detail::cv_qualifier<SIG_T>; template <class T> using cv = typename cv_qualifier::template cv<T>; static constexpr bool noex = detail::is_nothrow_fn_v<SIG_T>; using bound_entity_t = detail::bound_entity_type; using thunk_ptr_t = R (*)(bound_entity_t, ARG_Ts...) noexcept(noex); public: template <typename F> requires(std::is_function_v<F> && is_invocable_using<F>) function_ref(F* f) noexcept : m_bound_entity{f}, m_thunk_ptr{[](bound_entity_t entity, ARG_Ts... args) noexcept(noex) -> R { if constexpr (std::is_void_v<R>) { std::invoke(get<F>(entity), std::forward<ARG_Ts>(args)...); } else { return std::invoke(get<F>(entity), std::forward<ARG_Ts>(args)...); } }} { assert(f != nullptr); } template <typename F, typename T = std::remove_reference_t<F>> requires(!std::is_same_v<std::remove_cvref_t<F>, function_ref> && !std::is_member_pointer_v<T> && is_invocable_using<cv<T>&>) constexpr function_ref(F&& f) noexcept : m_bound_entity{std::addressof(f)}, m_thunk_ptr{ [](bound_entity_t entity, ARG_Ts... args) noexcept(noex) -> R { cv<T>& obj = *get<T>(entity); if constexpr (std::is_void_v<R>) { std::invoke(obj, std::forward<ARG_Ts>(args)...); } else { return std::invoke(obj, std::forward<ARG_Ts>(args)...); } }} {} template <auto f> requires(is_invocable_using<decltype(f)>) constexpr function_ref(nontype_t<f>) noexcept : m_bound_entity{}, m_thunk_ptr{ [](bound_entity_t entity, ARG_Ts... args) noexcept(noex) -> R { if constexpr (std::is_void_v<R>) { std::invoke(f, std::forward<ARG_Ts>(args)...); } else { return std::invoke(f, std::forward<ARG_Ts>(args)...); } }} { using F = decltype(f); if constexpr (std::is_pointer_v<F> || std::is_member_pointer_v<F>) { static_assert(f != nullptr); } } template <auto f, typename U, typename T = std::remove_reference_t<U>> requires(!std::is_rvalue_reference_v<U &&> && is_invocable_using<decltype(f), cv<T>&>) constexpr function_ref(nontype_t<f>, U&& obj) noexcept : m_bound_entity{std::addressof(obj)}, m_thunk_ptr{ [](bound_entity_t entity, ARG_Ts... args) noexcept(noex) -> R { cv<T>& obj = *get<T>(entity); if constexpr (std::is_void_v<R>) { std::invoke(f, obj, std::forward<ARG_Ts>(args)...); } else { return std::invoke(f, obj, std::forward<ARG_Ts>(args)...); } }} { using F = decltype(f); if constexpr (std::is_pointer_v<F> || std::is_member_pointer_v<F>) { static_assert(f != nullptr); } } template <auto f, typename T> requires(is_invocable_using<decltype(f), cv<T>*>) constexpr function_ref(nontype_t<f>, cv<T>* obj) noexcept : m_bound_entity{obj}, m_thunk_ptr{ [](bound_entity_t entity, ARG_Ts... args) noexcept(noex) -> R { cv<T>* obj = get<cv<T>>(entity); if constexpr (std::is_void_v<R>) { std::invoke(f, obj, std::forward<ARG_Ts>(args)...); } else { return std::invoke(f, obj, std::forward<ARG_Ts>(args)...); } }} { using F = decltype(f); if constexpr (std::is_pointer_v<F> || std::is_member_pointer_v<F>) { static_assert(f != nullptr); } if constexpr (std::is_member_pointer_v<F>) { assert(obj != nullptr); } } constexpr function_ref(const function_ref&) noexcept = default; constexpr function_ref& operator=(const function_ref&) noexcept = default; template <typename T> requires(!std::is_same_v<std::remove_cvref_t<T>, function_ref> && !std::is_pointer_v<T> && !detail::is_nontype_v<T>) function_ref& operator=(T) = delete; R operator()(ARG_Ts... args) const noexcept(noex) { return m_thunk_ptr(m_bound_entity, std::forward<ARG_Ts>(args)...); } private: bound_entity_t m_bound_entity{}; thunk_ptr_t m_thunk_ptr{nullptr}; }; using test_fn_ref = function_ref<int(char, bool) noexcept>; static_assert(std::is_trivially_copyable_v<test_fn_ref>); static_assert(std::is_trivially_assignable_v<test_fn_ref, test_fn_ref>); struct functor { int operator()([[maybe_unused]] char) const { return 42; } void operator()([[maybe_unused]] bool) noexcept {} int operator()() const noexcept { return 17; } }; bool free_function1([[maybe_unused]] char) { return false; } int free_function2() noexcept { return 3; } int free_function3(int val) noexcept { return val; } struct data { const int i = 182; }; int free_function4(data* ptr) noexcept { return ptr->i; } struct static_function { static char fn([[maybe_unused]] int, [[maybe_unused]] int) { return 'g'; } }; #include <concepts> template <std::integral T> T generic_fn() { return {42}; } template <auto f> constexpr int non_type_call(nontype_t<f>) { static_assert(std::is_same_v<decltype(f), int (*)()>); return f(); } struct my_class { char fn1([[maybe_unused]] bool) { return '3'; } int fn2() const { return 17; } my_class() = default; my_class(const my_class&) = delete; my_class& operator=(const my_class&) = delete; my_class(my_class&&) = delete; my_class& operator=(my_class&&) = delete; }; int main() { { functor fun{}; function_ref<int()> fnref{[]() { return 17; }}; function_ref<int(char) const> cfnref{fun}; function_ref<void(bool) noexcept> exfnref{fun}; function_ref<int() const noexcept> cexfnref{fun}; assert((fnref() == 17)); assert((cfnref('a') == 42)); exfnref(false); assert((cexfnref() == 17)); } { const functor cfun{}; function_ref<int(char) const> cfnref{cfun}; function_ref<int() const noexcept> cexfnref{cfun}; assert((cfnref('a') == 42)); assert((cexfnref() == 17)); } { function_ref<bool(char)> ff1{free_function1}; function_ref<int() noexcept> ff2{free_function2}; function_ref<char(int, int)> sfn{static_function::fn}; assert(!ff1('a')); assert(ff2() == 3); assert(sfn(1, 3) == 'g'); } { assert(non_type_call(nontype<generic_fn<int>>) == 42); function_ref<int()> nfn{nontype<generic_fn<int>>}; assert(nfn() == 42); } { my_class obj{}; assert(std::invoke(&my_class::fn1, obj, false) == '3'); const my_class c_obj{}; assert(std::invoke(&my_class::fn2, c_obj) == 17); } { my_class obj{}; function_ref<char(bool)> mfn1{nontype<&my_class::fn1>, obj}; assert(mfn1(false) == '3'); const my_class c_obj{}; function_ref<int() const> mfn2{nontype<&my_class::fn2>, c_obj}; assert(mfn2() == 17); const int val = 42; function_ref<int() noexcept> ffn{nontype<&free_function3>, val}; assert(ffn() == val); } { data d{}; function_ref<int()> pfn{nontype<&free_function4>, &d}; assert(pfn() == 182); const my_class c_obj{}; function_ref<int() const> mfn{nontype<&my_class::fn2>, &c_obj}; assert(mfn() == 17); } }
Insight:
Console: