aboutsummaryrefslogtreecommitdiff
path: root/libstdc++-v3/include/bits/binders.h
blob: 6489edd5321b061963eaf904100f94ac6f53c762 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
// Implementation of std::move_only_function, std::copyable_function
// and std::function_ref  -*- C++ -*-

// Copyright The GNU Toolchain Authors.
//
// This file is part of the GNU ISO C++ Library.  This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.

// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
// <http://www.gnu.org/licenses/>.

/** @file include/bits/binder.h
 *  This is an internal header file, included by other library headers.
 *  Do not attempt to use it directly. @headername{functional}
 */

#ifndef _GLIBCXX_BINDERS_H
#define _GLIBCXX_BINDERS_H 1

#ifdef _GLIBCXX_SYSHDR
#pragma GCC system_header
#endif

#if __cplusplus >= 202002L

#include <bits/invoke.h>
#include <bits/move.h>

namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION

  template<size_t, typename _Tp>
    struct _Indexed_bound_arg
    {
      [[no_unique_address]] _Tp _M_val;
    };

  template<typename... _IndexedArgs>
    struct _Bound_arg_storage : _IndexedArgs...
    {
      template<bool _Back, typename _Fd, typename _Self, typename... _CallArgs>
	static constexpr
	decltype(auto)
	_S_apply(_Fd&& __fd, _Self&& __self, _CallArgs&&... __call_args)
	{
	  if constexpr (_Back)
	    return std::__invoke(std::forward<_Fd>(__fd),
				 std::forward<_CallArgs>(__call_args)...,
				 __like_t<_Self, _IndexedArgs>(__self)._M_val...);
	  else
	    return std::__invoke(std::forward<_Fd>(__fd),
				 __like_t<_Self, _IndexedArgs>(__self)._M_val...,
				 std::forward<_CallArgs>(__call_args)...);
	}
    };

  template<typename... _BoundArgs, typename... _Args>
    constexpr auto
    __make_bound_args(_Args&&... __args)
    {
      if constexpr (sizeof...(_BoundArgs) == 1)
	// pack has one element, so return copy of arg
	return (_BoundArgs(std::forward<_Args>(__args)), ...);
      else
	{
	  auto __impl = [&]<size_t... _Inds>(index_sequence<_Inds...>)
	  {
	    return _Bound_arg_storage<_Indexed_bound_arg<_Inds, _BoundArgs>...>
		   { {_BoundArgs(std::forward<_Args>(__args))}... };
	  };
	  return __impl(index_sequence_for<_BoundArgs...>());
	}
    }

  template<bool _Back, typename _Fd, typename... _BoundArgs>
    class _Binder
    {
      template<typename _Self, typename... _CallArgs>
	using _Result_t = __conditional_t<
	  _Back,
	  invoke_result<__like_t<_Self, _Fd>,
			_CallArgs..., __like_t<_Self, _BoundArgs>...>,
	  invoke_result<__like_t<_Self, _Fd>,
			__like_t<_Self, _BoundArgs>..., _CallArgs...>>::type;

      template<typename _Self, typename... _CallArgs>
	static consteval bool
	_S_noexcept_invocable()
	{
	  if constexpr (_Back)
	    return is_nothrow_invocable_v< __like_t<_Self, _Fd>,
		     _CallArgs..., __like_t<_Self, _BoundArgs>...>;
	  else
	    return is_nothrow_invocable_v<__like_t<_Self, _Fd>,
		     __like_t<_Self, _BoundArgs>..., _CallArgs...>;
	}

    public:
      static_assert(is_move_constructible_v<_Fd>);
      static_assert((is_move_constructible_v<_BoundArgs> && ...));

      // First parameter is to ensure this constructor is never used
      // instead of the copy/move constructor.
      template<typename _Fn, typename... _Args>
	explicit constexpr
	_Binder(int, _Fn&& __fn, _Args&&... __args)
	noexcept(__and_<is_nothrow_constructible<_Fd, _Fn>,
			is_nothrow_constructible<_BoundArgs, _Args>...>::value)
	: _M_fd(std::forward<_Fn>(__fn)),
	  _M_bound_args(__make_bound_args<_BoundArgs...>(std::forward<_Args>(__args)...))
	{ static_assert(sizeof...(_Args) == sizeof...(_BoundArgs)); }

#if __cpp_explicit_this_parameter
      template<typename _Self, typename... _CallArgs>
	constexpr _Result_t<_Self, _CallArgs...>
	operator()(this _Self&& __self, _CallArgs&&... __call_args)
	noexcept(_S_noexcept_invocable<_Self, _CallArgs...>())
	{
	  return _S_call(__like_t<_Self, _Binder>(__self),
			 std::forward<_CallArgs>(__call_args)...);
	}
#else
      template<typename... _CallArgs>
	requires true
	constexpr _Result_t<_Binder&, _CallArgs...>
	operator()(_CallArgs&&... __call_args) &
	noexcept(_S_noexcept_invocable<_Binder&, _CallArgs...>())
	{
	  return _S_call(*this, std::forward<_CallArgs>(__call_args)...);
	}

      template<typename... _CallArgs>
	requires true
	constexpr _Result_t<const _Binder&, _CallArgs...>
	operator()(_CallArgs&&... __call_args) const &
	noexcept(_S_noexcept_invocable<const _Binder&, _CallArgs...>())
	{
	  return _S_call(*this, std::forward<_CallArgs>(__call_args)...);
	}

      template<typename... _CallArgs>
	requires true
	constexpr _Result_t<_Binder&&, _CallArgs...>
	operator()(_CallArgs&&... __call_args) &&
	noexcept(_S_noexcept_invocable<_Binder&&, _CallArgs...>())
	{
	  return _S_call(std::move(*this),
			 std::forward<_CallArgs>(__call_args)...);
	}

      template<typename... _CallArgs>
	requires true
	constexpr _Result_t<const _Binder&&, _CallArgs...>
	operator()(_CallArgs&&... __call_args) const &&
	noexcept(_S_noexcept_invocable<const _Binder&&, _CallArgs...>())
	{
	  return _S_call(std::move(*this),
			 std::forward<_CallArgs>(__call_args)...);
	}

      template<typename... _CallArgs>
	void operator()(_CallArgs&&...) & = delete;

      template<typename... _CallArgs>
	void operator()(_CallArgs&&...) const & = delete;

      template<typename... _CallArgs>
	void operator()(_CallArgs&&...) && = delete;

      template<typename... _CallArgs>
	void operator()(_CallArgs&&...) const && = delete;
#endif

      template<typename _Tp, typename... _CallArgs>
	static constexpr
	decltype(auto)
	_S_call(_Tp&& __g, _CallArgs&&... __call_args)
	{
	  if constexpr (sizeof...(_BoundArgs) > 1)
	    return _BoundArgsStorage::template _S_apply<_Back>(
		      std::forward<_Tp>(__g)._M_fd,
		      std::forward<_Tp>(__g)._M_bound_args,
		      std::forward<_CallArgs>(__call_args)...);
	  else if constexpr (sizeof...(_BoundArgs) == 0)
	    return std::__invoke(std::forward<_Tp>(__g)._M_fd,
				 std::forward<_CallArgs>(__call_args)...);
	  else if constexpr (_Back) // sizeof...(_BoundArgs) == 1
	    return std::__invoke(std::forward<_Tp>(__g)._M_fd,
				 std::forward<_CallArgs>(__call_args)...,
				 std::forward<_Tp>(__g)._M_bound_args);
	  else // !_Back && sizeof...(_BoundArgs) == 1
	    return std::__invoke(std::forward<_Tp>(__g)._M_fd,
				 std::forward<_Tp>(__g)._M_bound_args,
				 std::forward<_CallArgs>(__call_args)...);
	}

    private:
      using _BoundArgsStorage
	// _BoundArgs are required to be move-constructible, so this is valid.
	= decltype(__make_bound_args<_BoundArgs...>(std::declval<_BoundArgs>()...));

      [[no_unique_address]] _Fd _M_fd;
      [[no_unique_address]] _BoundArgsStorage _M_bound_args;
    };

  template<typename _Fn, typename... _Args>
    using _Bind_front_t = _Binder<false, decay_t<_Fn>, decay_t<_Args>...>;

  // for zero bounds args behavior of bind_front and bind_back is the same,
  // so reuse _Bind_front_t, i.e. _Binder<false, ...>
  template<typename _Fn, typename... _Args>
    using _Bind_back_t
      = _Binder<(sizeof...(_Args) > 0), decay_t<_Fn>, decay_t<_Args>...>;

_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std

#endif // __cplusplus >= 202002L
#endif // _GLIBCXX_BINDERS_H