• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DNKpp / Simple-Utility / 6497034454

12 Oct 2023 02:27PM UTC coverage: 86.872% (-12.8%) from 99.712%
6497034454

Pull #71

github

web-flow
Merge 967744a15 into e4166ac5d
Pull Request #71: graph namespace

281 of 281 new or added lines in 17 files covered. (100.0%)

622 of 716 relevant lines covered (86.87%)

154.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.44
/include/Simple-Utility/functional/BasicClosure.hpp
1
//          Copyright Dominic Koepke 2019 - 2023.
2
// Distributed under the Boost Software License, Version 1.0.
3
//    (See accompanying file LICENSE_1_0.txt or copy at
4
//          https://www.boost.org/LICENSE_1_0.txt)
5

6
#ifndef SIMPLE_UTILITY_FUNCTIONAL_BASIC_CLOSURE_HPP
7
#define SIMPLE_UTILITY_FUNCTIONAL_BASIC_CLOSURE_HPP
8

9
#pragma once
10

11
#include <concepts>
12
#include <functional>
13
#include <type_traits>
14
#include <utility>
15

16
#include "Simple-Utility/concepts/stl_extensions.hpp"
17

18
namespace sl::functional
19
{
20
        /**
21
         * \defgroup GROUP_FUNCTIONAL functional
22
         * \brief This namespace contains helper types for complex functional chaining and concrete functional objects.
23
         * \details As working more and more with ``ranges`` and functional style approach, this library is designed to fully
24
         * support the programmers in such cases.
25
         *
26
         * This namespaces aims to simplify caller code, when supplying algorithm with user provided function objects. Therefore several
27
         * types are offered, which enable different composition operations. Which one exactly, depends on the concrete closure template.
28
         * 
29
         * \note The wrapped function serves only as an enabler for the composition operators. Once a composition has been started, any
30
         * type of functional may be provided.
31
         *
32
         * \details
33
         * # Transform closure
34
         * The transform part of this library is designed to help the programmer when working with ``ranges`` or any other algorithm,
35
         * which accepts a functional as part of its interface. There is often the need to apply multiple sub-steps on a value,
36
         * before the work is actually done.
37
         * Because of this many existing functions are not usable by their own, thus they must be wrapped into a lambda, which
38
         * results in quite a bit of noise. With objects of type ``Transform`` programmers have the option to pipe function results into
39
         * other functions, and thus nest multiple functions into each other.
40
         * \snippet functional/Transform.cpp transform piped
41
         * ``sf::envelop`` is a simple factory function, which hands over the given functional to the ``Transform`` template, so users don't have
42
         * to supply the actual template params (CTAD unfortunately isn't a thing yet for typedefs...).
43
         * 
44
         * ``composition`` is equivalent to
45
         * \code{.cpp}
46
         * [](const int i) { return std::to_string(i + 42); };
47
         * \endcode
48
         * 
49
         * Sure, that seems to be a lot more effort for this trivial task, but this approach lets you compose existing features into more complex
50
         * ones. Have a look at the next example, where we pre-define the ``to_string`` in forehand:
51
         * \snippet functional/Transform.cpp transform to_string definition
52
         * 
53
         * \snippet functional/Transform.cpp transform more complex pipeline
54
         * We create a range of integer values and transform these into strings (NOTE: ``std::format`` was no option on gcc at the time of writing).
55
         * We first negate the values, multiply the result by two, transform them to a string and then formulate a sentence with them.
56
         * As you can see, once the pipe is active, one can chain any type of functional; even mutable lambdas are supported!
57
         * 
58
         * # Predicate closure
59
         * ``Predicate`` objects are fully compatible with ``Transform`` objects, thus they may be mixed as desired. Additionally they
60
         * offer composing via operator &&, || and other common operations. Other than piping, all such composed predicates will receive the
61
         * identically input and are required to return boolean convertible results. Eventually ``Predicate`` are easily invertible via operator !.
62
         * 
63
         * Let there be a pre-defined predicate ``is_even``:
64
         * \snippet functional/Predicate.cpp predicate is_even definition
65
         * You can combine predicates in various ways and therefore create much more complex predicates. For example, this snippet filters values which
66
         * are even and less than 44 OR odd and greater than 45. The usual operator precedences apply here as-well.
67
         * \snippet functional/Predicate.cpp predicate more complex
68
         * 
69
         * \{
70
         */
71

72
        /**
73
         * \brief Determines whether the given type satisfies the constraints of a function type.
74
         * \tparam T Type to check.
75
         * \details There are not many syntactic constraints, the concept can check for. The important one is rather semantic:
76
         * The given type must be invokable with params which will be determined later.
77
         */
78
        template <class T>
79
        concept function = concepts::unqualified<T>
80
                                                && std::is_object_v<T>;
81

82
        /**
83
         * \brief Determines whether the given type satisfies the constraints of an invoke-policy.
84
         * \tparam T Type to check.
85
         * \details There are not many syntactic constraints, the concept can check for. The policy enables the invoke operators
86
         * for the closure type and should therefore accept all valid argument combinations.
87
         */
88
        template <template <class> class T>
89
        concept invoke_policy = true;
90

91
        /**
92
         * \brief Determines whether the given type satisfies the constraints of a operator-policy.
93
         * \tparam T Type to check.
94
         * \details There are not many syntactic constraints, the concept can check for. The policy enables the various composing operators
95
         * for the closure type. It should define a binary operator(-set) and return a composition (wrapped in another closure type) of both.
96
         */
97
        template <template <class> class T>
98
        concept operator_policy = true;
99

100
        /**
101
         * \brief The core class, wrapping one functional object and enabling a variety of composing operators for it.
102
         * \tparam Fn The wrapped functional type.
103
         * \tparam InvokePolicy CRTP type, providing different operator () implementations.
104
         * \tparam OperatorPolicies CRTP types, providing different composing operators.
105
         */
106
        template <function Fn, template <class> class InvokePolicy, template <class> class... OperatorPolicies>
107
                requires invoke_policy<InvokePolicy>
108
                                && (... && operator_policy<OperatorPolicies>)
109
        class BasicClosure
110
                : public InvokePolicy<BasicClosure<Fn, InvokePolicy, OperatorPolicies...>>,
111
                public OperatorPolicies<BasicClosure<Fn, InvokePolicy, OperatorPolicies...>>...
112
        {
113
        public:
114
                /**
115
                 * \brief Forwarding constructor, forwarding all of its arguments to its stored functional.
116
                 * \tparam Args Constructor argument types.
117
                 * \param args Constructor arguments.
118
                 */
119
                template <class... Args>
120
                        requires std::constructible_from<Fn, Args...>
121
                explicit constexpr BasicClosure(Args&&... args) noexcept(std::is_nothrow_constructible_v<Fn, Args...>)
278✔
122
                        : m_Fn{std::forward<Args>(args)...}
278✔
123
                {
124
                }
278✔
125

126
                /**
127
                 * \brief Defaulted copy constructor.
128
                 * \note Only present if the stored functional is also copy constructible.
129
                 */
130
                BasicClosure(const BasicClosure&)
×
131
                        requires std::copy_constructible<Fn> = default;
132

133
                /**
134
                 * \brief Defaulted copy assign operator.
135
                 * \note Only present if the stored functional is also copy assignable.
136
                 */
137
                BasicClosure& operator =(const BasicClosure&)
138
                        requires std::is_copy_assignable_v<Fn> = default;
139

140
                /**
141
                 * \brief Defaulted move constructor.
142
                 * \note Only present if the stored functional is also move constructible.
143
                 */
144
                BasicClosure(BasicClosure&&)
55✔
145
                        requires std::move_constructible<Fn> = default;
146

147
                /**
148
                 * \brief Defaulted move assign operator.
149
                 * \note Only present if the stored functional is also move assignable.
150
                 */
151
                BasicClosure& operator =(BasicClosure&&)
152
                        requires std::is_move_assignable_v<Fn> = default;
153

154
                /**
155
                 * \brief Defaulted destructor.
156
                 */
157
                ~BasicClosure() = default;
262✔
158

159
                /**
160
                 * \brief Conversion operator, providing access to the stored functional as const lvalue-reference.
161
                 */
162
                [[nodiscard]]
163
                explicit constexpr operator const Fn&() const & noexcept
259✔
164
                {
165
                        return m_Fn;
259✔
166
                }
167

168
                /**
169
                 * \brief Conversion operator, providing access to the stored functional as lvalue-reference.
170
                 */
171
                [[nodiscard]]
172
                explicit constexpr operator Fn&() & noexcept
232✔
173
                {
174
                        return m_Fn;
232✔
175
                }
176

177
                /**
178
                 * \brief Conversion operator, providing access to the stored functional as const rvalue-reference.
179
                 */
180
                [[nodiscard]]
181
                explicit constexpr operator const Fn&&() const && noexcept
30✔
182
                {
183
                        return std::move(m_Fn);
30✔
184
                }
185

186
                /**
187
                 * \brief Conversion operator, providing access to the stored functional as rvalue-reference.
188
                 */
189
                [[nodiscard]]
190
                explicit constexpr operator Fn&&() && noexcept
134✔
191
                {
192
                        return std::move(m_Fn);
134✔
193
                }
194

195
        private:
196
                Fn m_Fn{};
197
        };
198

199
        /**
200
         * \}
201
         */
202

203
        /**
204
         * \defgroup GROUP_FUNCTIONAL_UNWRAP_FUNCTIONAL unwrap_functional
205
         * \ingroup GROUP_FUNCTIONAL
206
         * \brief Trait, unwrapping the functional type if a closure type is given, otherwise the given type.
207
         * 
208
         * \{
209
         */
210

211
        /**
212
         * \brief Primary template for non BasicClosure types.
213
         * \tparam T The functional type.
214
         */
215
        template <class T>
216
        struct unwrap_functional
217
        {
218
                using type = T;
219
        };
220

221
        /**
222
         * \brief Specialization for BasicClosure class. Purposely undefined.
223
         * \tparam Fn The wrapped functional type.
224
         * \tparam InvokePolicy CRTP type, providing different operator () implementations.
225
         * \tparam OperatorPolicies CRTP types, providing different composing operators.
226
         */
227
        template <class Fn, template <class> class InvokePolicy, template <class> class... OperatorPolicies>
228
        struct unwrap_functional<BasicClosure<Fn, InvokePolicy, OperatorPolicies...>>;
229

230
        /**
231
         * \brief Specialization for const BasicClosure class. Purposely undefined.
232
         * \tparam Fn The wrapped functional type.
233
         * \tparam InvokePolicy CRTP type, providing different operator () implementations.
234
         * \tparam OperatorPolicies CRTP types, providing different composing operators.
235
         */
236
        template <class Fn, template <class> class InvokePolicy, template <class> class... OperatorPolicies>
237
        struct unwrap_functional<const BasicClosure<Fn, InvokePolicy, OperatorPolicies...>>;
238

239
        /**
240
         * \brief Specialization for const lvalue-references of BasicClosure class.
241
         * \tparam Fn The wrapped functional type.
242
         * \tparam InvokePolicy CRTP type, providing different operator () implementations.
243
         * \tparam OperatorPolicies CRTP types, providing different composing operators.
244
         */
245
        template <class Fn, template <class> class InvokePolicy, template <class> class... OperatorPolicies>
246
        struct unwrap_functional<const BasicClosure<Fn, InvokePolicy, OperatorPolicies...>&>
247
        {
248
                using type = const Fn&;
249
        };
250

251
        /**
252
         * \brief Specialization for lvalue-references of BasicClosure class.
253
         * \tparam Fn The wrapped functional type.
254
         * \tparam InvokePolicy CRTP type, providing different operator () implementations.
255
         * \tparam OperatorPolicies CRTP types, providing different composing operators.
256
         */
257
        template <class Fn, template <class> class InvokePolicy, template <class> class... OperatorPolicies>
258
        struct unwrap_functional<BasicClosure<Fn, InvokePolicy, OperatorPolicies...>&>
259
        {
260
                using type = Fn&;
261
        };
262

263
        /**
264
         * \brief Specialization for const rvalue-references of BasicClosure class.
265
         * \tparam Fn The wrapped functional type.
266
         * \tparam InvokePolicy CRTP type, providing different operator () implementations.
267
         * \tparam OperatorPolicies CRTP types, providing different composing operators.
268
         */
269
        template <class Fn, template <class> class InvokePolicy, template <class> class... OperatorPolicies>
270
        struct unwrap_functional<const BasicClosure<Fn, InvokePolicy, OperatorPolicies...>&&>
271
        {
272
                using type = const Fn&&;
273
        };
274

275
        /**
276
         * \brief Specialization for rvalue-references of BasicClosure class.
277
         * \tparam Fn The wrapped functional type.
278
         * \tparam InvokePolicy CRTP type, providing different operator () implementations.
279
         * \tparam OperatorPolicies CRTP types, providing different composing operators.
280
         */
281
        template <class Fn, template <class> class InvokePolicy, template <class> class... OperatorPolicies>
282
        struct unwrap_functional<BasicClosure<Fn, InvokePolicy, OperatorPolicies...>&&>
283
        {
284
                using type = Fn&&;
285
        };
286

287
        /**
288
         * \brief Convenient alias for the type member alias to unwrap_functional trait.
289
         * \tparam T The functional type.
290
         */
291
        template <class T>
292
        using unwrap_functional_t = typename unwrap_functional<T>::type;
293

294
        /**
295
         * \}
296
         */
297

298
        /**
299
         * \brief Unwraps the functional if stored in a closure. Otherwise forwards as-is.
300
         * \ingroup GROUP_FUNCTIONAL
301
         * \tparam Fn The functional type.
302
         * \param fn The functional to be unwrapped.
303
         * \return The unwrapped functional.
304
         */
305
        template <class Fn>
306
        [[nodiscard]]
307
        constexpr unwrap_functional_t<Fn&&>&& forward_unwrapped(std::remove_reference_t<Fn>& fn) noexcept
753✔
308
        {
309
                return static_cast<unwrap_functional_t<Fn&&>&&>(std::forward<Fn>(fn));
753✔
310
        }
311

312
        /**
313
         * \defgroup GROUP_FUNCTIONAL_CLOSURE_TEMPLATE closure_template
314
         * \ingroup GROUP_FUNCTIONAL
315
         * \brief Trait type, exposing the closure template.
316
         * 
317
         * \{
318
         */
319

320
        /**
321
         * \brief Primary template, purposely undefined.
322
         * \tparam T Given functional type. 
323
         */
324
        template <class T>
325
        struct closure_template;
326

327
        /**
328
         * \brief Specialization, providing the closure template as member alias.
329
         * \tparam Fn The wrapped functional type.
330
         * \tparam InvokePolicy CRTP type, providing different operator () implementations.
331
         * \tparam OperatorPolicies CRTP types, providing different composing operators.
332
         */
333
        template <class Fn, template <class> class InvokePolicy, template <class> class... OperatorPolicies>
334
        struct closure_template<BasicClosure<Fn, InvokePolicy, OperatorPolicies...>>
335
        {
336
                template <class NewFn>
337
                using type = BasicClosure<NewFn, InvokePolicy, OperatorPolicies...>;
338
        };
339

340
        /**
341
         * \}
342
         */
343

344
        /**
345
         * \brief Wraps the given functional into the given closure type.
346
         * \ingroup GROUP_FUNCTIONAL
347
         * \tparam Fn The functional type.
348
         * \param fn The functional to be wrapped.
349
         * \return The wrapped functional.
350
         */
351
        template <template <class> class Closure, class Fn>
352
        constexpr Closure<std::remove_cvref_t<Fn>> envelop(
129✔
353
                Fn&& fn
354
        ) noexcept(std::is_nothrow_constructible_v<std::remove_cvref_t<Fn>, Fn>)
355
        {
356
                return Closure<std::remove_cvref_t<Fn>>{std::forward<Fn>(fn)};
129✔
357
        }
358
}
359

360
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc