Skip to content

Commit c06dd8b

Browse files
author
Cppshizoid
committed
complete docs. for code
1 parent 9d5f88e commit c06dd8b

5 files changed

Lines changed: 1824 additions & 19 deletions

File tree

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
#pragma once
2+
3+
#include <functional>
4+
#include <print>
5+
#include <tuple>
6+
#include <type_traits>
7+
#include <utility>
8+
9+
namespace core::meta::utils {
10+
11+
/**
12+
* @class curried
13+
* @brief A class template that implements function currying - transforming a
14+
* function taking multiple arguments into a sequence of functions each taking a
15+
* single argument.
16+
*
17+
* @tparam F The type of the callable object to be curried
18+
* @tparam BoundArgs The types of the arguments already bound to the function
19+
*
20+
* This class stores a function and already bound arguments, and provides
21+
* operator() to either:
22+
* 1. Invoke the function if enough arguments are provided
23+
* 2. Return a new curried object with additional arguments bound
24+
*/
25+
template <typename F, typename... BoundArgs>
26+
class curried {
27+
F func; ///< The callable object to be curried
28+
std::tuple<BoundArgs...> bound; ///< Tuple of already bound arguments
29+
30+
/**
31+
* @brief Helper function to invoke the stored function with bound and new
32+
* arguments
33+
*
34+
* @tparam Args Types of additional arguments
35+
* @tparam Is Index sequence for unpacking the bound arguments tuple
36+
* @param f The callable object (forwarded)
37+
* @param tup Tuple of bound arguments
38+
* @param seq Index sequence for tuple unpacking
39+
* @param args Additional arguments to pass to the function
40+
* @return Result of invoking the function with all arguments
41+
*
42+
* @note This is noexcept when the function invocation is noexcept
43+
*/
44+
template <typename... Args, std::size_t... Is>
45+
static constexpr decltype(auto) invoke_impl(
46+
F&& f, std::tuple<BoundArgs...>& tup, std::index_sequence<Is...>,
47+
Args&&... args) noexcept(std::is_nothrow_invocable_v<F, BoundArgs...,
48+
Args...>) {
49+
return std::invoke(std::forward<F>(f), std::get<Is>(tup)...,
50+
std::forward<Args>(args)...);
51+
}
52+
53+
public:
54+
/**
55+
* @brief Construct a new curried object
56+
*
57+
* @param f The callable object to curry
58+
* @param args Initial arguments to bind
59+
*
60+
* @note Constructor is noexcept if both F and all BoundArgs are nothrow
61+
* move-constructible
62+
*/
63+
constexpr explicit curried(F f, BoundArgs... args) noexcept(
64+
std::is_nothrow_move_constructible_v<F> &&
65+
(std::is_nothrow_move_constructible_v<BoundArgs> && ...))
66+
: func(std::move(f)), bound(std::make_tuple(std::move(args)...)) {}
67+
68+
/**
69+
* @brief Const lvalue overload of function call operator for complete
70+
* invocation
71+
*
72+
* @tparam Args Types of additional arguments
73+
* @param args Additional arguments to pass to the function
74+
* @return Result of function invocation
75+
*
76+
* @note This overload is only available when the function can be invoked with
77+
* the current bound arguments and provided arguments
78+
*/
79+
template <typename... Args>
80+
constexpr decltype(auto) operator()(Args&&... args) const&
81+
requires std::is_invocable_v<const F&, const BoundArgs&..., Args...>
82+
{
83+
return invoke_impl(func, const_cast<std::tuple<BoundArgs...>&>(bound),
84+
std::index_sequence_for<BoundArgs...>{},
85+
std::forward<Args>(args)...);
86+
}
87+
88+
/**
89+
* @brief Rvalue overload of function call operator for complete invocation
90+
*
91+
* @tparam Args Types of additional arguments
92+
* @param args Additional arguments to pass to the function
93+
* @return Result of function invocation
94+
*
95+
* @note This overload is only available when the function can be invoked with
96+
* the current bound arguments and provided arguments
97+
*/
98+
template <typename... Args>
99+
constexpr decltype(auto) operator()(Args&&... args) &&
100+
requires std::is_invocable_v<F, BoundArgs..., Args...>
101+
{
102+
return invoke_impl(std::move(func), bound,
103+
std::index_sequence_for<BoundArgs...>{},
104+
std::forward<Args>(args)...);
105+
}
106+
107+
/**
108+
* @brief Const lvalue overload of function call operator for partial
109+
* application
110+
*
111+
* @tparam Args Types of additional arguments
112+
* @param args Additional arguments to bind
113+
* @return A new curried object with additional arguments bound
114+
*
115+
* @note This overload is only available when the function cannot yet be
116+
* invoked with the current bound arguments and provided arguments
117+
*/
118+
template <typename... Args>
119+
constexpr auto operator()(Args&&... args) const&
120+
requires(!std::is_invocable_v<const F&, const BoundArgs&..., Args...>)
121+
{
122+
return curried<F, BoundArgs..., std::decay_t<Args>...>(
123+
func, std::get<BoundArgs>(bound)..., std::forward<Args>(args)...);
124+
}
125+
126+
/**
127+
* @brief Rvalue overload of function call operator for partial application
128+
*
129+
* @tparam Args Types of additional arguments
130+
* @param args Additional arguments to bind
131+
* @return A new curried object with additional arguments bound
132+
*
133+
* @note This overload is only available when the function cannot yet be
134+
* invoked with the current bound arguments and provided arguments
135+
*/
136+
template <typename... Args>
137+
constexpr auto operator()(Args&&... args) &&
138+
requires(!std::is_invocable_v<F, BoundArgs..., Args...>) {
139+
return curried<F, BoundArgs..., std::decay_t<Args>...>(
140+
std::move(func), std::move(std::get<BoundArgs>(bound))...,
141+
std::forward<Args>(args)...);
142+
}
143+
144+
/**
145+
* @brief Const lvalue overload of zero-argument function call operator
146+
* @return Result of function invocation with just the bound arguments
147+
*
148+
* @note This overload is only available when the function can be invoked
149+
* with just the bound arguments
150+
*/
151+
constexpr decltype(auto) operator()() const&
152+
requires std::is_invocable_v<const F&, const BoundArgs&...>
153+
{
154+
return invoke_impl(func, const_cast<std::tuple<BoundArgs...>&>(bound),
155+
std::index_sequence_for<BoundArgs...>{});
156+
}
157+
158+
/**
159+
* @brief Rvalue overload of zero-argument function call operator
160+
* @return Result of function invocation with just the bound arguments
161+
*
162+
* @note This overload is only available when the function can be invoked with
163+
* just the bound arguments
164+
*/
165+
constexpr decltype(auto) operator()() &&
166+
requires std::is_invocable_v<F, BoundArgs...>
167+
{
168+
return invoke_impl(std::move(func), bound,
169+
std::index_sequence_for<BoundArgs...>{});
170+
}
171+
};
172+
173+
/**
174+
* @brief Creates a curried function object with no initially bound arguments
175+
*
176+
* @tparam F Type of the callable object
177+
* @param f The callable object to curry
178+
* @return A curried wrapper around f
179+
*
180+
* @note This is noexcept if constructing the curried wrapper is noexcept
181+
*/
182+
template <typename F>
183+
constexpr auto curry(F&& f) noexcept(
184+
std::is_nothrow_constructible_v<std::decay_t<F>, F>) {
185+
return curried<std::decay_t<F>>(std::forward<F>(f));
186+
}
187+
188+
/**
189+
* @brief Creates a curried function object with initially bound arguments
190+
*
191+
* @tparam F Type of the callable object
192+
* @tparam Args Types of the arguments to bind initially
193+
* @param f The callable object to curry
194+
* @param args Arguments to bind initially
195+
* @return A curried wrapper around f with args bound
196+
*
197+
* @note This is noexcept if constructing the curried wrapper with arguments is
198+
* noexcept
199+
* @note The returned object is marked [[nodiscard]] to prevent accidental
200+
* discarding
201+
*/
202+
[[nodiscard]] template <typename F, typename... Args>
203+
constexpr auto curry(F&& f, Args&&... args) noexcept(
204+
noexcept(curried<std::decay_t<F>, std::decay_t<Args>...>(
205+
std::forward<F>(f), std::forward<Args>(args)...))) {
206+
return curried<std::decay_t<F>, std::decay_t<Args>...>(
207+
std::forward<F>(f), std::forward<Args>(args)...);
208+
}
209+
210+
} // namespace core::meta::utils
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,96 @@
1+
/**
2+
* @file fill_utils.hpp
3+
* @brief Template metaprogramming utilities for filling and assigning types
4+
*
5+
* This header provides utilities for creating filled sequences of types and
6+
* assigning types to other type containers at compile-time.
7+
*/
8+
19
#pragma once
210

311
#include "./expand.hpp"
412

513
namespace core::meta::utils {
614

15+
/**
16+
* @brief Fills a container with a type or value N times
17+
* @tparam n The number of times to fill
18+
* @tparam T The type or value to fill with
19+
*
20+
* This template creates a sequence containing the type T repeated n times.
21+
* The result is adapted to match the input container type (variadic, tuple,
22+
* etc).
23+
*/
724
template <auto n, typename T>
825
struct fill {
26+
/**
27+
* @brief Implementation helper for filling with types
28+
*/
929
template <auto N, typename U>
1030
struct impl : U {};
1131

32+
/**
33+
* @brief Applies a filler function
34+
*/
1235
template <template <auto, typename> typename F>
1336
using call = expand<F, T, index_sequence_of_c<n>>;
1437

38+
/**
39+
* @brief Selects appropriate filling strategy
40+
*/
1541
using curr = type_if<!has_type_v<T> || !has_value_type_v<T>, call<identity>,
1642
call<impl>>;
43+
44+
/**
45+
* @brief The resulting filled type sequence
46+
*/
1747
using type = rename_if_t<is_variadic_type_v<T>, curr, std::tuple<>>;
1848
};
1949

50+
/**
51+
* @brief Helper type for fill
52+
*/
2053
template <auto n, typename T>
2154
using fill_t = typeof_t<fill<n, T>>;
2255

56+
/**
57+
* @brief Fills with a constant value
58+
* @tparam n Number of times to fill
59+
* @tparam v The constant value to fill with
60+
*/
2361
template <auto n, auto v>
2462
using fill_c = typeof_t<fill<n, c_<v>>>;
2563

64+
/**
65+
* @brief Assigns a type to fill another type's container
66+
* @tparam T The target type to assign to
67+
* @tparam U The type to fill with
68+
*
69+
* Creates a new container of the same type as T but filled with U
70+
*/
2671
template <typename T, typename U>
2772
struct assign : rename<fill_t<sizeof_t_v<T>, U>, clear_t<T>> {};
2873

74+
/**
75+
* @brief Helper type for assign
76+
*/
2977
template <typename T, typename U>
3078
using assign_t = typeof_t<assign<T, U>>;
3179

80+
/**
81+
* @brief Creates a nested fill structure
82+
* @tparam N The nesting depth
83+
* @tparam T The template to nest
84+
*
85+
* Creates a recursively nested structure of the template T, N levels deep
86+
*/
3287
template <auto N, template <typename...> typename T>
3388
struct nest_fill : to_nest<fill_t<N, T<>>> {};
3489

90+
/**
91+
* @brief Helper type for nest_fill
92+
*/
3593
template <auto N, template <typename...> typename T>
3694
using nest_fill_t = typeof_t<nest_fill<N, T>>;
95+
3796
} // namespace core::meta::utils

0 commit comments

Comments
 (0)