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',