From d3cb6f324f5bcafd2716764d935268ee0d40a348 Mon Sep 17 00:00:00 2001 From: Tobias Zindl Date: Fri, 13 Oct 2023 18:59:51 +0200 Subject: [PATCH] :sparkles: add first function_ref implementation * the current implementation only supports up to one argument * void return type is currently not fully supported --- ...3-10-13T17_05_55Z_post_HJOtakocHZZVtTha.md | 8 + .../functional/detail/bound_entity_type.hpp | 53 ++++++ include/zll/functional/detail/fn_types.hpp | 18 +++ include/zll/functional/function_ref.hpp | 153 ++++++++++++++++++ .../functional/function_ref_tests.cpp | 112 +++++++++++++ tests/unit_tests/meson.build | 2 + 6 files changed, 346 insertions(+) create mode 100644 docs/sherds/2023/2023-10/2023-10-13T17_05_55Z_post_HJOtakocHZZVtTha.md create mode 100644 include/zll/functional/detail/bound_entity_type.hpp create mode 100644 include/zll/functional/detail/fn_types.hpp create mode 100644 include/zll/functional/function_ref.hpp create mode 100644 tests/unit_tests/functional/function_ref_tests.cpp diff --git a/docs/sherds/2023/2023-10/2023-10-13T17_05_55Z_post_HJOtakocHZZVtTha.md b/docs/sherds/2023/2023-10/2023-10-13T17_05_55Z_post_HJOtakocHZZVtTha.md new file mode 100644 index 0000000..bbb51cb --- /dev/null +++ b/docs/sherds/2023/2023-10/2023-10-13T17_05_55Z_post_HJOtakocHZZVtTha.md @@ -0,0 +1,8 @@ +Shortcuts for `function_ref` + +We made some shortcuts in the `function_ref` implementation: + +* direct call to the callables (no `invoke`), this could mess up the calls for binding +* no specialisation to `invocables` this could end up in ugly compiler errors +* only support for one argument (to much boilerplate) +* void is not proper supported (would need a specialisation) diff --git a/include/zll/functional/detail/bound_entity_type.hpp b/include/zll/functional/detail/bound_entity_type.hpp new file mode 100644 index 0000000..4f9959f --- /dev/null +++ b/include/zll/functional/detail/bound_entity_type.hpp @@ -0,0 +1,53 @@ +#ifndef ZLL_FUNCTIONAL_DETAIL_BOUNDENTITYTPE_HPP +#define ZLL_FUNCTIONAL_DETAIL_BOUNDENTITYTPE_HPP + +#include "zll/meta/traits/enable_if.hpp" +#include "zll/meta/traits/is_const.hpp" +#include "zll/meta/traits/is_function.hpp" +#include "zll/meta/traits/is_object.hpp" + +namespace zll { +namespace detail { + +struct bound_entity_object {}; +struct bound_entity_function {}; + +union bound_entity_type { + void* object_ptr; + const void* const_object_ptr; + void (*fn_ptr)(); + + bound_entity_type() : object_ptr(NULL) {} + + template + explicit bound_entity_type(T* ptr, bound_entity_object) : object_ptr(ptr) {} + + template + explicit bound_entity_type(const T* ptr, bound_entity_object) : const_object_ptr(ptr) {} + + template + explicit bound_entity_type(T* ptr, bound_entity_function) : fn_ptr(reinterpret_cast(ptr)) {} +}; + +template +static typename zll::meta::enable_if::value && zll::meta::is_const::value, T*>::type get( + bound_entity_type entity) { + return static_cast(entity.const_object_ptr); +} + +template +static typename zll::meta::enable_if::value && !(zll::meta::is_const::value), T*>::type get( + bound_entity_type entity) { + return static_cast(entity.object_ptr); +} + +template +static typename zll::meta::enable_if::value && (zll::meta::is_function::value), T*>::type +get(bound_entity_type entity) { + return reinterpret_cast(entity.fn_ptr); +} + +} // namespace detail +} // namespace zll + +#endif // ZLL_FUNCTIONAL_DETAIL_BOUNDENTITYTPE_HPP diff --git a/include/zll/functional/detail/fn_types.hpp b/include/zll/functional/detail/fn_types.hpp new file mode 100644 index 0000000..58bf03e --- /dev/null +++ b/include/zll/functional/detail/fn_types.hpp @@ -0,0 +1,18 @@ +#ifndef ZLL_FUNCTIONAL_DETAIL_FNTYPES_HPP +#define ZLL_FUNCTIONAL_DETAIL_FNTYPES_HPP + +namespace zll { +namespace detail { +// clang-format off +template struct fn_types; + +template struct fn_types { typedef R type(); }; +template struct fn_types { typedef R type(); }; + +template struct fn_types { typedef R type(T); }; +template struct fn_types { typedef R type(T); }; +// clang-format on +} // namespace detail +} // namespace zll + +#endif // ZLL_FUNCTIONAL_DETAIL_FNTYPES_HPP diff --git a/include/zll/functional/function_ref.hpp b/include/zll/functional/function_ref.hpp new file mode 100644 index 0000000..15f0633 --- /dev/null +++ b/include/zll/functional/function_ref.hpp @@ -0,0 +1,153 @@ +#ifndef ZLL_FUNCTIONAL_FUNCTIONREF_HPP +#define ZLL_FUNCTIONAL_FUNCTIONREF_HPP + +#include "zll/functional/detail/bound_entity_type.hpp" +#include "zll/functional/detail/fn_types.hpp" + +#include "zll/memory/address_of.hpp" + +#include "zll/meta/traits/enable_if.hpp" +#include "zll/meta/traits/is_object.hpp" +#include "zll/meta/traits/is_function.hpp" +#include "zll/meta/traits/remove_reference.hpp" + +#include "zll/utils/nontype.hpp" + +namespace zll { +namespace detail { + +template +struct fn_enable_if_object { + typedef typename zll::meta::enable_if::value, U>::type type; +}; + +template +struct fn_enable_if_function { + typedef typename zll::meta::enable_if::value, U>::type type; +}; + +} // namespace detail + +template ::type> +class function_ref; + +template +class function_ref { + typedef detail::bound_entity_type entity_type; + typedef R (*thunk_ptr_type)(entity_type); + +public: + typedef R (*signature_type)(); + + template + explicit function_ref( + const F& f, typename zll::meta::enable_if::value || + zll::is_nontype::type>::value), + void*>::type = NULL) + : m_bound_entity(zll::addressof(f), detail::bound_entity_object()), + m_thunk_ptr(function_ref::thunk_call::type>) {} + + template + explicit function_ref(F* f, typename detail::fn_enable_if_function::type = NULL) + : m_bound_entity(f, detail::bound_entity_function()), m_thunk_ptr(function_ref::thunk_fn_call) {} + + template + function_ref(zll::nontype_t) + : m_bound_entity(), m_thunk_ptr(function_ref::thunk_nontype_call) {} + + template + function_ref(zll::nontype_t, U& obj) + : m_bound_entity(zll::addressof(obj), detail::bound_entity_object()), + m_thunk_ptr(function_ref::thunk_member_call) {} + + R operator()() const { return m_thunk_ptr(m_bound_entity); } + +private: + template + static typename zll::meta::enable_if::value), R>::type thunk_call(entity_type entity) { + T& obj = *detail::get(entity); + return obj(); + } + + template + static R thunk_fn_call(entity_type entity) { + return detail::get(entity)(); + } + + template + static R thunk_nontype_call(entity_type) { + return VALUE(); + } + + template + static R thunk_member_call(entity_type entity) { + U& obj = *detail::get(entity); + return (obj.*VALUE)(); + } + + entity_type m_bound_entity; + thunk_ptr_type m_thunk_ptr; +}; + +template +class function_ref { + typedef detail::bound_entity_type entity_type; + typedef R (*thunk_ptr_type)(entity_type, ARG1_T); + +public: + typedef R (*signature_type)(ARG1_T); + + template + explicit function_ref( + const F& f, typename zll::meta::enable_if::value || + zll::is_nontype::type>::value), + void*>::type = NULL) + : m_bound_entity(zll::addressof(f), detail::bound_entity_object()), + m_thunk_ptr(function_ref::thunk_call::type>) {} + + template + explicit function_ref(F* f, typename detail::fn_enable_if_function::type = NULL) + : m_bound_entity(f, detail::bound_entity_function()), m_thunk_ptr(function_ref::thunk_fn_call) {} + + template + function_ref(zll::nontype_t) + : m_bound_entity(), m_thunk_ptr(function_ref::thunk_nontype_call) {} + + template + function_ref(zll::nontype_t, U& obj) + : m_bound_entity(zll::addressof(obj), detail::bound_entity_object()), + m_thunk_ptr(function_ref::thunk_member_call) {} + + R operator()(ARG1_T arg) const { return m_thunk_ptr(m_bound_entity, arg); } + +private: + template + static typename zll::meta::enable_if::value), R>::type thunk_call(entity_type entity, + ARG1_T arg) { + T& obj = *detail::get(entity); + return obj(arg); + } + + template + static R thunk_fn_call(entity_type entity, ARG1_T arg) { + return detail::get(entity)(arg); + } + + template + static R thunk_nontype_call(entity_type, ARG1_T arg) { + return VALUE(arg); + } + + template + static R thunk_member_call(entity_type entity, ARG1_T arg) { + U& obj = *detail::get(entity); + return (obj.*VALUE)(arg); + } + + entity_type m_bound_entity; + thunk_ptr_type m_thunk_ptr; +}; + +} // namespace zll + +#endif // ZLL_FUNCTIONAL_FUNCTIONREF_HPP diff --git a/tests/unit_tests/functional/function_ref_tests.cpp b/tests/unit_tests/functional/function_ref_tests.cpp new file mode 100644 index 0000000..0385236 --- /dev/null +++ b/tests/unit_tests/functional/function_ref_tests.cpp @@ -0,0 +1,112 @@ +#include +#include + +#include + +struct functor1 { + int operator()() const { return 17; } +}; + +int free_fun1() { return 42; } + +struct static_fun { + static int fun() { return 15; } +}; + +struct member_fun { + int fun() const { return 3; } +}; + +UTEST(functional_function_ref, no_parameter) { + { + functor1 fun; + zll::function_ref fn_ref(fun); + + ASSERT_EQ(fn_ref(), 17); + } + { + const functor1 fun; + zll::function_ref fn_ref(fun); + + ASSERT_EQ(fn_ref(), 17); + } + { + zll::function_ref fn_ref(free_fun1); + ASSERT_EQ(fn_ref(), 42); + } + { + zll::function_ref fn_ref(&free_fun1); + ASSERT_EQ(fn_ref(), 42); + } + { + zll::nontype_t nontype; + zll::function_ref fn_ref(nontype); + ASSERT_EQ(fn_ref(), 42); + } + { + zll::nontype_t nontype; + zll::function_ref fn_ref(nontype); + ASSERT_EQ(fn_ref(), 15); + } + { + zll::nontype_t nontype; + + member_fun obj; + zll::function_ref fn_ref(nontype, obj); + ASSERT_EQ(fn_ref(), 3); + } +} + +struct functor2 { + int operator()(int a) const { return a; } +}; + +int free_fun2(char) { return 42; } + +struct static_fun2 { + static int fun(int a) { return a; } +}; + +struct member_fun2 { + int fun(int a) const { return a; } +}; + +UTEST(functional_function_ref, one_parameter) { + { + functor2 fun; + zll::function_ref fn_ref(fun); + + ASSERT_EQ(fn_ref(5), 5); + } + { + const functor2 fun; + zll::function_ref fn_ref(fun); + + ASSERT_EQ(fn_ref(8), 8); + } + { + zll::function_ref fn_ref(free_fun2); + ASSERT_EQ(fn_ref('a'), 42); + } + { + zll::function_ref fn_ref(&free_fun2); + ASSERT_EQ(fn_ref('f'), 42); + } + { + zll::nontype_t nontype; + zll::function_ref fn_ref(nontype); + ASSERT_EQ(fn_ref('g'), 42); + } + { + zll::nontype_t nontype; + zll::function_ref fn_ref(nontype); + ASSERT_EQ(fn_ref(11), 11); + } + { + zll::nontype_t nontype; + + member_fun2 obj; + zll::function_ref fn_ref(nontype, obj); + ASSERT_EQ(fn_ref(-12), -12); + } +} diff --git a/tests/unit_tests/meson.build b/tests/unit_tests/meson.build index f397a04..e8a0094 100644 --- a/tests/unit_tests/meson.build +++ b/tests/unit_tests/meson.build @@ -1,5 +1,7 @@ unit_tests_srcs = [ + 'functional/function_ref_tests.cpp', + 'memory/address_of_tests.cpp', 'meta/integral_constant_tests.cpp',