1//! @file currying.hpp
2//! @author ryftchen
3//! @brief The declarations (currying) in the utility module.
4//! @version 0.1.0
5//! @copyright Copyright (c) 2022-2025 ryftchen. All rights reserved.
6
7#pragma once
8
9#include <functional>
10#include <tuple>
11
12//! @brief The utility module.
13namespace utility // NOLINT(modernize-concat-nested-namespaces)
14{
15//! @brief Currying-related functions in the utility module.
16namespace currying
17{
18//! @brief Brief function description.
19//! @return function description (module_function)
20inline static const char* description() noexcept
21{
22 return "UTIL_CURRYING";
23}
24extern const char* version() noexcept;
25
26//! @brief Alias for the tuple concat result.
27//! @tparam Tuples - type of tuples
28template <typename... Tuples>
29using TupleConcatResult = decltype(std::tuple_cat(std::declval<Tuples>()...));
30
31//! @brief Head of function arguments.
32//! @tparam IdxSeq - type of index sequence
33//! @tparam Args - type of function arguments
34template <typename IdxSeq, typename... Args>
35struct ArgsHead;
36//! @brief Head of function arguments.
37//! @tparam Args - type of function arguments
38template <typename... Args>
39struct ArgsHead<std::integer_sequence<std::size_t>, Args...>
40{
41 //! @brief Alias for the empty tuple.
42 using Type = std::tuple<>;
43};
44//! @brief Head of function arguments.
45//! @tparam Args - type of function arguments
46//! @tparam Rest - type of rest function arguments
47//! @tparam I0 - current index of specific function arguments
48//! @tparam Is - indices of sequence related to specific function arguments
49template <typename Args, typename... Rest, std::size_t I0, std::size_t... Is>
50struct ArgsHead<std::integer_sequence<std::size_t, I0, Is...>, Args, Rest...>
51{
52 //! @brief Alias for the tuple concat.
53 using Type = TupleConcatResult<
54 std::tuple<Args>,
55 typename ArgsHead<std::integer_sequence<std::size_t, Is...>, Rest...>::Type>;
56};
57//! @brief Alias for the function arguments head type.
58//! @tparam Num - number of specific function arguments
59//! @tparam Args - type of function arguments
60template <std::size_t Num, typename... Args>
61using ArgsHeadType = typename ArgsHead<std::make_index_sequence<Num>, Args...>::Type;
62
63//! @brief Exclusion of function arguments.
64//! @tparam IdxSeq - type of index sequence
65//! @tparam Args - type of function arguments
66template <typename IdxSeq, typename... Args>
67struct ArgsExcl;
68//! @brief Exclusion of function arguments.
69//! @tparam Args - type of function arguments
70template <typename... Args>
71struct ArgsExcl<std::integer_sequence<std::size_t>, Args...>
72{
73 //! @brief Alias for the rest tuple.
74 using Type = std::tuple<Args...>;
75};
76//! @brief Exclusion of function arguments.
77//! @tparam Args - type of function arguments
78//! @tparam Rest - type of rest function arguments
79//! @tparam I0 - current index of specific function arguments
80//! @tparam Is - indices of sequence related to specific function arguments
81template <typename Args, typename... Rest, std::size_t I0, std::size_t... Is>
82struct ArgsExcl<std::integer_sequence<std::size_t, I0, Is...>, Args, Rest...>
83{
84 //! @brief Alias for the rest function arguments.
85 using Type = typename ArgsExcl<std::integer_sequence<std::size_t, Is...>, Rest...>::Type;
86};
87//! @brief Alias for the function arguments exclusion type.
88//! @tparam Num - number of specific function arguments
89//! @tparam Args - type of function arguments
90template <std::size_t Num, typename... Args>
91using ArgsExclType = typename ArgsExcl<std::make_index_sequence<Num>, Args...>::Type;
92
93//! @brief Completion of curry.
94//! @tparam Call - type of currying function
95//! @tparam ArgsTuple - type of function arguments tuple
96//! @tparam UncurriedArgsTuple - type of uncurried function arguments tuple
97template <typename Call, typename ArgsTuple, typename UncurriedArgsTuple>
98class Curried;
99//! @brief Completion of curry.
100//! @tparam Call - type of currying function
101//! @tparam CurriedArgs - type of curried function arguments
102//! @tparam UncurriedArgs - type of uncurried function arguments
103template <typename Call, typename... CurriedArgs, typename... UncurriedArgs>
104class Curried<Call, std::tuple<CurriedArgs...>, std::tuple<UncurriedArgs...>>
105{
106public:
107 //! @brief Construct a new Curried object.
108 //! @tparam Func - type of wrapped function
109 //! @param callable - wrapped function
110 //! @param curriedArgs - curried function arguments tuple
111 template <typename Func>
112 Curried(Func&& callable, std::tuple<CurriedArgs...>&& curriedArgs) :
113 callable{std::forward<Func>(callable)}, curriedArgs{std::move(curriedArgs)}
114 {
115 }
116
117 //! @brief The operator (()) overloading of Curried class.
118 //! @param args - uncurried function arguments
119 //! @return function execution
120 decltype(auto) operator()(UncurriedArgs... args) const;
121 //! @brief To curry.
122 //! @tparam Args - type of function arguments
123 //! @param args - function arguments tuple
124 //! @return curried result
125 template <typename... Args>
126 auto curry(std::tuple<Args...>&& args) const&;
127 //! @brief To curry.
128 //! @tparam Args - type of function arguments
129 //! @param args - function arguments tuple
130 //! @return curried result
131 template <typename... Args>
132 auto curry(std::tuple<Args...>&& args) &&;
133
134private:
135 //! @brief Wrapped function.
136 const Call callable{};
137 //! @brief Curried function arguments tuple.
138 mutable std::tuple<CurriedArgs...> curriedArgs{};
139};
140
141template <typename Call, typename... CurriedArgs, typename... UncurriedArgs>
142decltype(auto) Curried<Call, std::tuple<CurriedArgs...>, std::tuple<UncurriedArgs...>>::operator()(
143 UncurriedArgs... args) const
144{
145 auto uncurriedArgs = std::tuple<UncurriedArgs...>(std::forward<UncurriedArgs>(args)...);
146 return std::apply(callable, std::tuple_cat(curriedArgs, std::move(uncurriedArgs)));
147}
148
149template <typename Call, typename... CurriedArgs, typename... UncurriedArgs>
150template <typename... Args>
151auto Curried<Call, std::tuple<CurriedArgs...>, std::tuple<UncurriedArgs...>>::curry(std::tuple<Args...>&& args) const&
152{
153 using OverlayCurried = Curried<
154 Call,
155 TupleConcatResult<std::tuple<CurriedArgs...>, std::tuple<Args...>>,
156 ArgsExclType<sizeof...(Args), UncurriedArgs...>>;
157 return OverlayCurried(callable, std::tuple_cat(curriedArgs, std::move(args)));
158}
159
160template <typename Call, typename... CurriedArgs, typename... UncurriedArgs>
161template <typename... Args>
162auto Curried<Call, std::tuple<CurriedArgs...>, std::tuple<UncurriedArgs...>>::curry(std::tuple<Args...>&& args) &&
163{
164 using OverlayCurried = Curried<
165 Call,
166 TupleConcatResult<std::tuple<CurriedArgs...>, std::tuple<Args...>>,
167 ArgsExclType<sizeof...(Args), UncurriedArgs...>>;
168 return OverlayCurried(std::move(callable), std::tuple_cat(std::move(curriedArgs), std::move(args)));
169}
170
171//! @brief Package curry.
172//! @tparam CurriedArgsTuple - type of curried function arguments tuple
173//! @tparam UncurriedArgsTuple - type of uncurried function arguments tuple
174template <typename CurriedArgsTuple, typename UncurriedArgsTuple>
175struct Curry;
176//! @brief Package curry.
177//! @tparam CurriedArgs - type of curried function arguments
178//! @tparam UncurriedArgs - type of uncurried function arguments
179template <typename... CurriedArgs, typename... UncurriedArgs>
180struct Curry<std::tuple<CurriedArgs...>, std::tuple<UncurriedArgs...>>
181{
182 //! @brief To curry for internal.
183 //! @tparam Ret - type of return value
184 //! @tparam Args - type of function arguments
185 //! @param call - wrapped function
186 //! @param args - function arguments
187 //! @return curried result
188 template <typename Ret, typename... Args>
189 static auto process(const std::function<Ret(CurriedArgs..., UncurriedArgs...)>& call, Args&&... args)
190 {
191 using Call = std::function<Ret(CurriedArgs..., UncurriedArgs...)>;
192 using OverlayCurried = Curried<Call, std::tuple<CurriedArgs...>, std::tuple<UncurriedArgs...>>;
193 return OverlayCurried(call, std::tuple<CurriedArgs...>(std::forward<Args>(args)...));
194 }
195 //! @brief To curry for internal.
196 //! @tparam Ret - type of return value
197 //! @tparam Args - type of function arguments
198 //! @param call - wrapped function
199 //! @param args - function arguments
200 //! @return curried result
201 template <typename Ret, typename... Args>
202 static auto process(std::function<Ret(CurriedArgs..., UncurriedArgs...)>&& call, Args&&... args)
203 {
204 using Call = std::function<Ret(CurriedArgs..., UncurriedArgs...)>;
205 using OverlayCurried = Curried<Call, std::tuple<CurriedArgs...>, std::tuple<UncurriedArgs...>>;
206 return OverlayCurried(std::move(call), std::tuple<CurriedArgs...>(std::forward<Args>(args)...));
207 }
208};
209
210//! @brief To curry.
211//! @tparam Ret - type of return value
212//! @tparam FullArgs - type of full function arguments
213//! @tparam Args - type of function arguments
214//! @param call - wrapped function
215//! @param args - function arguments
216//! @return curried result
217template <typename Ret, typename... FullArgs, typename... Args>
218inline auto curry(std::function<Ret(FullArgs...)>&& call, Args&&... args)
219{
220 using CurriedArgsTuple = ArgsHeadType<sizeof...(Args), FullArgs...>;
221 using UncurriedArgsTuple = ArgsExclType<sizeof...(Args), FullArgs...>;
222 using CurryWrapper = Curry<CurriedArgsTuple, UncurriedArgsTuple>;
223 return CurryWrapper::process(std::move(call), std::forward<Args>(args)...);
224}
225
226//! @brief To curry.
227//! @tparam Ret - type of return value
228//! @tparam FullArgs - type of full function arguments
229//! @tparam Args - type of function arguments
230//! @param call - wrapped function
231//! @param args - function arguments
232//! @return curried result
233template <typename Ret, typename... FullArgs, typename... Args>
234inline auto curry(const std::function<Ret(FullArgs...)>& call, Args&&... args)
235{
236 using CurriedArgsTuple = ArgsHeadType<sizeof...(Args), FullArgs...>;
237 using UncurriedArgsTuple = ArgsExclType<sizeof...(Args), FullArgs...>;
238 using CurryWrapper = Curry<CurriedArgsTuple, UncurriedArgsTuple>;
239 return CurryWrapper::process(call, std::forward<Args>(args)...);
240}
241
242//! @brief To curry.
243//! @tparam Ret - type of return value
244//! @tparam FullArgs - type of full function arguments
245//! @tparam Args - type of function arguments
246//! @param func - currying function
247//! @param args - function arguments
248//! @return curried result
249template <typename Ret, typename... FullArgs, typename... Args>
250inline auto curry(Ret (*func)(FullArgs...), Args&&... args)
251{
252 std::function<Ret(FullArgs...)> call = func;
253 return curry(std::move(call), std::forward<Args>(args)...);
254}
255
256//! @brief To curry.
257//! @tparam Ret - type of return value
258//! @tparam Obj - type of object to which the member belongs
259//! @tparam FullArgs - type of full function arguments
260//! @param func - currying function
261//! @return curried result
262template <typename Ret, typename Obj, typename... FullArgs>
263inline auto curry(Ret (Obj::*func)(FullArgs...))
264{
265 std::function<Ret(Obj*, FullArgs...)> call = func;
266 return curry(std::move(call));
267}
268
269//! @brief To curry.
270//! @tparam Ret - type of return value
271//! @tparam Obj - type of object to which the member belongs
272//! @tparam FullArgs - type of full function arguments
273//! @tparam Args - type of function arguments
274//! @param func - currying function
275//! @param caller - object to which the member belongs
276//! @param args - function arguments
277//! @return curried result
278template <typename Ret, typename Obj, typename... FullArgs, typename... Args>
279inline auto curry(Ret (Obj::*func)(FullArgs...), Obj* caller, Args&&... args)
280{
281 std::function<Ret(Obj*, FullArgs...)> call = func;
282 return curry(std::move(call), caller, std::forward<Args>(args)...);
283}
284
285//! @brief To curry.
286//! @tparam CurriedArgsList - type of curried function arguments list
287//! @tparam Args - type of function arguments
288//! @param curried - curried object
289//! @param args - function arguments
290//! @return curried result
291template <typename... CurriedArgsList, typename... Args>
292inline auto curry(const Curried<CurriedArgsList...>& curried, Args&&... args)
293{
294 return curried.curry(std::tuple<Args...>(std::forward<Args>(args)...));
295}
296
297//! @brief To curry.
298//! @tparam CurriedArgsList - type of curried function arguments list
299//! @tparam Args - type of function arguments
300//! @param curried - curried object
301//! @param args - function arguments
302//! @return curried result
303template <typename... CurriedArgsList, typename... Args>
304inline auto curry(Curried<CurriedArgsList...>&& curried, Args&&... args)
305{
306 return std::move(curried).curry(std::tuple<Args...>(std::forward<Args>(args)...));
307}
308} // namespace currying
309} // namespace utility
310