Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Function.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Execution_Function_h_
5#define _Stroika_Foundation_Execution_Function_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <compare>
10#include <functional>
11#include <memory>
12
13#include "Stroika/Foundation/Common/Common.h"
14
15/**
16 * \file
17 *
18 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
19 *
20 * TODO
21 * @todo Instead of using shared_ptr<> - we could have an INT counter (except for special case of null where we use
22 * 0). That might be cheaper, and preserve the same semantics.
23 *
24 * @todo Better understand, and fix qFoundation_Execution_Function_OperatorForwardNeedsRefBug, and eliminate it
25 *
26 * @todo Consider if this should be copy-by-value (use SharedByValue instead of shared_ptr) so it more closely
27 * mimics copy behavior of std::function?
28 */
29
31 class String;
32}
33
35
36 // SB in .inl file except to support qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
37 namespace Private_ {
38 inline atomic<uint32_t> sFunctionObjectNextPtrID_{1};
39 }
40
41 /**
42 * IDEA is be SAME AS std::function<> but allow for operator<, a usable operator== etc...,
43 * which is an unfortunate omission from the c++ standard.
44 *
45 * This should be convertible to a normal std::function<>, and fairly if not totally interoperable.
46 *
47 * @aliases This template could have been called Callback<> - as that's principally what its used for.
48 * Callbacks you need to be able to create, and then later remove (by value) - and this class
49 * lets you create an object (callback/Function) - which can then be added to a Mapping (or Set)
50 * and then later removed by value.
51 *
52 * \note This was implemented using a shared_ptr<function<...>> instead of a directly aggregated function object
53 * until Stroika v2.1d8.
54 *
55 * \note <a href="Design-Overview.md#Comparisons">Comparisons</a>:
56 * static_assert (totally_ordered<Function<...>);
57 */
58 template <typename FUNCTION_SIGNATURE>
59 class Function {
60 public:
61 using STDFUNCTION = function<FUNCTION_SIGNATURE>;
62
63 public:
64 /**
65 */
66 using result_type = typename STDFUNCTION::result_type;
67
68 public:
69 /**
70 * \note Implementation note:
71 * Reason for the not derived_from<> restriction on CTOR/1(CTOR_FUNC_SIG&&) is to prevent compiler from
72 * instantiating that constructor template for argument subclasses of this Function type, and having those take precedence over the
73 * default X(const X&) CTOR.
74 *
75 * And also careful not to apply to non-functions.
76 *
77 * \note Constructor with nullptr or a null function object - will produce a UNIQUE function value (according to operator==).
78 * ANY other function object - each time you call the constructor - you will get a different (according to operator==) Function
79 * object. See http://stroika-bugs.sophists.com/browse/STK-960 for some of the reasoning behind this.
80 */
81 Function () = default;
82 Function (nullptr_t);
83 Function (const Function&) = default;
84 Function (Function&&) = default;
85#if qCompilerAndStdLib_template_Requires_constraint_not_treated_constexpr_Buggy
86 template <typename CTOR_FUNC_SIG, enable_if_t<is_convertible_v<remove_cvref_t<CTOR_FUNC_SIG>, function<FUNCTION_SIGNATURE>> and
87 not derived_from<remove_cvref_t<CTOR_FUNC_SIG>, Function<FUNCTION_SIGNATURE>>>* = nullptr>
88 Function (CTOR_FUNC_SIG&& f);
89#else
90 template <typename CTOR_FUNC_SIG>
91 requires (is_convertible_v<remove_cvref_t<CTOR_FUNC_SIG>, function<FUNCTION_SIGNATURE>> and
92 not derived_from<remove_cvref_t<CTOR_FUNC_SIG>, Function<FUNCTION_SIGNATURE>>)
93 Function (CTOR_FUNC_SIG&& f)
94#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
95 : fFun_{forward<CTOR_FUNC_SIG> (f)}
96 , fOrdering_{fFun_ == nullptr ? OrderingType_{} : ++Private_::sFunctionObjectNextPtrID_}
97 {
98 Assert ((fOrdering_ == OrderingType_{}) == (fFun_ == nullptr));
99 }
100#endif
101#endif
102 ;
103
104 public:
105 /**
106 */
107 nonvirtual Function& operator= (Function&&) = default;
108 nonvirtual Function& operator= (const Function&) = default;
109
110 public:
111 /**
112 */
113 nonvirtual operator STDFUNCTION () const;
114
115 public:
116 /**
117 */
118 template <typename... Args>
119 nonvirtual result_type operator() (Args... args) const;
120
121 public:
122 struct ThreeWayComparer;
123
124 public:
125 /**
126 */
127 nonvirtual strong_ordering operator<=> (const Function& rhs) const;
128
129 public:
130 /**
131 */
132 nonvirtual bool operator== (const Function& rhs) const;
133
134 public:
135 /**
136 * @see Characters::ToString ();
137 */
138 nonvirtual Characters::String ToString () const;
139
140 private:
141 STDFUNCTION fFun_;
142
143 private:
144 /*
145 * Before Stroika 2.1.11, we used f.template target<remove_cvref_t<CTOR_FUNC_SIG>> ()
146 * as OrderingType_. But this caused problems on g++-10 release builds (at least inside WTF). Not sure
147 * exactly how this happened, but sometimes different lambdas produced the same address. And the docs
148 * for std::function<> suggest this is possible.
149 *
150 * So just avoid altogether. Simply store a separate number ID.
151 */
152 using OrderingType_ = uint32_t;
153 OrderingType_ fOrdering_{}; // captured early when we have the right type info, so we can safely compare, and print
154 };
155
156}
157
158/*
159 ********************************************************************************
160 ***************************** Implementation Details ***************************
161 ********************************************************************************
162 */
163#include "Function.inl"
164
165#endif /*_Stroika_Foundation_Execution_Function_h_*/
nonvirtual Characters::String ToString() const
Definition Function.inl:65