Skip to content

Commit

Permalink
Merge pull request #11 from cmazakas/feature/function-ref-cpp17
Browse files Browse the repository at this point in the history
implement pointer-to-member function_ref overloads
  • Loading branch information
pdimov authored Jun 22, 2024
2 parents 915a207 + 37dbb81 commit 9244cd1
Show file tree
Hide file tree
Showing 5 changed files with 540 additions and 2 deletions.
73 changes: 73 additions & 0 deletions doc/compat/function_ref.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ class function_ref<R(ArgTypes...) cv noexcept(noex)> {
public:
template<class F> function_ref(F*) noexcept;
template<class F> function_ref(F&&) noexcept;
template<auto f> function_ref(nontype_t<f>) noexcept;
template<auto f, class U> function_ref(nontype_t<f>, U&&) noexcept;
template<auto f, class T> function_ref(nontype_t<f>, cv T*) noexcept;

function_ref(const function_ref&) noexcept = default;
function_ref& operator=(const function_ref&) noexcept = default;
Expand Down Expand Up @@ -92,6 +95,76 @@ participates in resolution when `fn` is not a pointer-to-member or pointer-to-me
+
Calling the `function_ref` is expression-equivalent to: + `invoke_r<R>(static_cast<cv T&>(f), call-args...)`.

### Pointer to Member Function Constructor

```cpp
template<auto f> function_ref(nontype_t<f>) noexcept;
```

[horizontal]
Effects:;; Constructs a `function_ref` using the supplied pointer to member function. This overload only participates
in resolution when `f` is a pointer to member or pointer to member function. +
+
Calling the `function_ref` is express-equivalent to: `invoke_r<R>(f, class-args)`.
Example:;;
+
--
```cpp
struct point { int x = 1, y = 2; };

point p;
compat::function_ref<int(point const&)> f(compat::nontype_t<&point::x>{});

BOOST_TEST_EQ(f(p), 1);
```
--

### Bound Object Constructor

```cpp
template<auto f, class U> function_ref(nontype_t<f>, U&&) noexcept;
```

[horizontal]
Effects:;; Constructs a `function_ref` using the supplied pointer to member or pointer to member function and a reference
to an object to bind it to. +
+
This overload only participates in resolution if `is_rvalue_reference_v<U&&>` is false.
Example:;;
+
--
```cpp
struct point { int x = 1, y = 2; };

point p;
compat::function_ref<int()> f(compat::nontype_t<&point::x>{}, p);

BOOST_TEST_EQ(f(), 1);
```
--

### Bound Pointer Constructor

```cpp
template<auto f, class T> function_ref(nontype_t<f>, cv T*) noexcept;
```

[horizontal]
Effects:;; Constructs a `function_ref` using the supplied pointer to member or pointer to member function and a pointer
to an object.
Example:;;
+
--
```cpp
struct point { int x = 1, y = 2; };

point p;
compat::function_ref<int()> f(compat::nontype_t<&point::x>{}, &p);

BOOST_TEST_EQ(f(), 1);
```
--

### Copy Constructor

```cpp
Expand Down
127 changes: 125 additions & 2 deletions include/boost/compat/function_ref.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,28 @@
#include <boost/compat/invoke.hpp>
#include <boost/compat/type_traits.hpp>

#include <functional>
#include <memory>
#include <type_traits>
#include <utility>

#if defined(__cpp_nontype_template_parameter_auto) && __cpp_nontype_template_parameter_auto >= 201606L
#define BOOST_COMPAT_HAS_AUTO_NTTP
#endif

namespace boost {
namespace compat {

template <class... S>
struct function_ref;

#if defined(BOOST_COMPAT_HAS_AUTO_NTTP)

template <auto V>
struct nontype_t {
explicit nontype_t() = default;
};

#endif

namespace detail {

template <bool NoEx>
Expand All @@ -43,6 +55,34 @@ struct invoke_object_holder {
}
};

#if defined(BOOST_COMPAT_HAS_AUTO_NTTP)

template <auto f, bool Const, bool NoEx, class R, class... Args>
struct invoke_mem_fn_holder {
static R invoke_mem_fn(thunk_storage<NoEx> /* s */, Args&&... args) noexcept(NoEx) {
return compat::invoke_r<R>(f, std::forward<Args>(args)...);
}
};

template <auto f, class U, bool Const, bool NoEx, class R, class... Args>
struct invoke_target_mem_fn_holder {
static R invoke_mem_fn(thunk_storage<NoEx> s, Args&&... args) noexcept(NoEx) {
using T = remove_reference_t<U>;
using cv_T = conditional_t<Const, add_const_t<T>, T>;
return compat::invoke_r<R>(f, *static_cast<cv_T*>(s.pobj_), std::forward<Args>(args)...);
}
};

template <auto f, class T, bool Const, bool NoEx, class R, class... Args>
struct invoke_ptr_mem_fn_holder {
static R invoke_mem_fn(thunk_storage<NoEx> s, Args&&... args) noexcept(NoEx) {
using cv_T = conditional_t<Const, add_const_t<T>, T>;
return compat::invoke_r<R>(f, static_cast<cv_T*>(s.pobj_), std::forward<Args>(args)...);
}
};

#endif

template <bool Const, bool NoEx, class R, class... Args>
struct function_ref_base {
private:
Expand All @@ -52,6 +92,7 @@ struct function_ref_base {
public:
struct fp_tag {};
struct obj_tag {};
struct mem_fn_tag {};

template <class F>
function_ref_base(fp_tag, F* fn) noexcept
Expand All @@ -65,6 +106,28 @@ struct function_ref_base {
thunk_.pobj_ = const_cast<void*>(static_cast<void const*>(std::addressof(fn)));
}

#if defined(BOOST_COMPAT_HAS_AUTO_NTTP)

template <auto f, class F = decltype(f)>
function_ref_base(mem_fn_tag, nontype_t<f>)
: thunk_{}, invoke_(&invoke_mem_fn_holder<F{f}, Const, NoEx, R, Args...>::invoke_mem_fn) {
thunk_.pobj_ = nullptr;
}

template <auto f, class U, class F = decltype(f)>
function_ref_base(mem_fn_tag, nontype_t<f>, U&& obj)
: thunk_{}, invoke_(&invoke_target_mem_fn_holder<F{f}, U, Const, NoEx, R, Args...>::invoke_mem_fn) {
thunk_.pobj_ = const_cast<void*>(static_cast<void const*>(std::addressof(obj)));
}

template <auto f, class T, class F = decltype(f)>
function_ref_base(mem_fn_tag, nontype_t<f>, T* obj)
: thunk_{}, invoke_(&invoke_ptr_mem_fn_holder<F{f}, T, Const, NoEx, R, Args...>::invoke_mem_fn) {
thunk_.pobj_ = const_cast<void*>(static_cast<void const*>(obj));
}

#endif

function_ref_base(const function_ref_base&) noexcept = default;
function_ref_base& operator=(const function_ref_base&) noexcept = default;

Expand All @@ -78,6 +141,7 @@ struct function_ref<R(Args...)> : public detail::function_ref_base<false, false,
private:
using base_type = detail::function_ref_base<false, false, R, Args...>;
using typename base_type::fp_tag;
using typename base_type::mem_fn_tag;
using typename base_type::obj_tag;

template <class... T>
Expand All @@ -93,6 +157,20 @@ struct function_ref<R(Args...)> : public detail::function_ref_base<false, false,
int> = 0>
function_ref(F&& fn) noexcept : base_type(obj_tag{}, fn) {}

#if defined(BOOST_COMPAT_HAS_AUTO_NTTP)

template <auto f, class F = decltype(f), enable_if_t<is_invocable_using<F>::value, int> = 0>
function_ref(nontype_t<f> x) noexcept : base_type(mem_fn_tag{}, x) {}

template <auto f, class U, class T = remove_reference_t<U>, class F = decltype(f),
enable_if_t<!std::is_rvalue_reference_v<U&&> && is_invocable_using<F, T&>::value, int> = 0>
function_ref(nontype_t<f> x, U&& obj) noexcept : base_type(mem_fn_tag{}, x, std::forward<U>(obj)) {}

template <auto f, class T, class F = decltype(f), enable_if_t<is_invocable_using<F, T*>::value, int> = 0>
function_ref(nontype_t<f> x, T* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {}

#endif

function_ref(const function_ref&) noexcept = default;
function_ref& operator=(const function_ref&) noexcept = default;

Expand All @@ -105,6 +183,7 @@ struct function_ref<R(Args...) const> : public detail::function_ref_base<true, f
private:
using base_type = detail::function_ref_base<true, false, R, Args...>;
using typename base_type::fp_tag;
using typename base_type::mem_fn_tag;
using typename base_type::obj_tag;

template <class... T>
Expand All @@ -120,6 +199,20 @@ struct function_ref<R(Args...) const> : public detail::function_ref_base<true, f
int> = 0>
function_ref(F&& fn) noexcept : base_type(obj_tag{}, fn) {}

#if defined(BOOST_COMPAT_HAS_AUTO_NTTP)

template <auto f, class F = decltype(f), enable_if_t<is_invocable_using<F>::value, int> = 0>
function_ref(nontype_t<f> x) noexcept : base_type(mem_fn_tag{}, x) {}

template <auto f, class U, class T = remove_reference_t<U>, class F = decltype(f),
enable_if_t<!std::is_rvalue_reference_v<U&&> && is_invocable_using<F, T const&>::value, int> = 0>
function_ref(nontype_t<f> x, U&& obj) noexcept : base_type(mem_fn_tag{}, x, std::forward<U>(obj)) {}

template <auto f, class T, class F = decltype(f), enable_if_t<is_invocable_using<F, T const*>::value, int> = 0>
function_ref(nontype_t<f> x, T const* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {}

#endif

function_ref(const function_ref&) noexcept = default;
function_ref& operator=(const function_ref&) noexcept = default;

Expand All @@ -134,6 +227,7 @@ struct function_ref<R(Args...) noexcept> : public detail::function_ref_base<fals
private:
using base_type = detail::function_ref_base<false, true, R, Args...>;
using typename base_type::fp_tag;
using typename base_type::mem_fn_tag;
using typename base_type::obj_tag;

template <class... T>
Expand All @@ -149,6 +243,20 @@ struct function_ref<R(Args...) noexcept> : public detail::function_ref_base<fals
int> = 0>
function_ref(F&& fn) noexcept : base_type(obj_tag{}, fn) {}

#if defined(BOOST_COMPAT_HAS_AUTO_NTTP)

template <auto f, class F = decltype(f), enable_if_t<is_invocable_using<F>::value, int> = 0>
function_ref(nontype_t<f> x) noexcept : base_type(mem_fn_tag{}, x) {}

template <auto f, class U, class T = remove_reference_t<U>, class F = decltype(f),
enable_if_t<!std::is_rvalue_reference_v<U&&> && is_invocable_using<F, T&>::value, int> = 0>
function_ref(nontype_t<f> x, U&& obj) noexcept : base_type(mem_fn_tag{}, x, std::forward<U>(obj)) {}

template <auto f, class T, class F = decltype(f), enable_if_t<is_invocable_using<F, T*>::value, int> = 0>
function_ref(nontype_t<f> x, T* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {}

#endif

function_ref(const function_ref&) noexcept = default;
function_ref& operator=(const function_ref&) noexcept = default;

Expand All @@ -161,6 +269,7 @@ struct function_ref<R(Args...) const noexcept> : public detail::function_ref_bas
private:
using base_type = detail::function_ref_base<true, true, R, Args...>;
using typename base_type::fp_tag;
using typename base_type::mem_fn_tag;
using typename base_type::obj_tag;

template <class... T>
Expand All @@ -176,6 +285,20 @@ struct function_ref<R(Args...) const noexcept> : public detail::function_ref_bas
int> = 0>
function_ref(F&& fn) noexcept : base_type(obj_tag{}, fn) {}

#if defined(BOOST_COMPAT_HAS_AUTO_NTTP)

template <auto f, class F = decltype(f), enable_if_t<is_invocable_using<F>::value, int> = 0>
function_ref(nontype_t<f> x) noexcept : base_type(mem_fn_tag{}, x) {}

template <auto f, class U, class T = remove_reference_t<U>, class F = decltype(f),
enable_if_t<!std::is_rvalue_reference_v<U&&> && is_invocable_using<F, T const&>::value, int> = 0>
function_ref(nontype_t<f> x, U&& obj) noexcept : base_type(mem_fn_tag{}, x, std::forward<U>(obj)) {}

template <auto f, class T, class F = decltype(f), enable_if_t<is_invocable_using<F, T const*>::value, int> = 0>
function_ref(nontype_t<f> x, T const* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {}

#endif

function_ref(const function_ref&) noexcept = default;
function_ref& operator=(const function_ref&) noexcept = default;

Expand Down
2 changes: 2 additions & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ run is_nothrow_invocable_r_test.cpp ;

run function_ref_fn_test.cpp ;
run function_ref_obj_test.cpp ;
run function_ref_mfn_test.cpp ;

run function_ref_fn_noexcept_test.cpp ;
run function_ref_mfn_noexcept_test.cpp ;
run function_ref_obj_noexcept_test.cpp ;
Loading

0 comments on commit 9244cd1

Please sign in to comment.