@@ -38,53 +38,123 @@ inline namespace kernel
38
38
39
39
auto operator <<(std::ostream &, callable const &) -> std::ostream &;
40
40
41
- template <typename , typename Callee >
42
- struct procedure : public callable
41
+ template <typename F >
42
+ struct generic_procedure : public callable
43
43
{
44
- Callee call ;
44
+ std:: enable_if_t <std::is_invocable_v<F> or std::is_invocable_v<F, object &>, F> invocable ;
45
45
46
- explicit procedure (std::string const & name, Callee call )
46
+ explicit generic_procedure (std::string const & name, F f )
47
47
: callable { name }
48
- , call { call }
48
+ , invocable { f }
49
49
{}
50
50
51
51
auto operator ()(object & xs) const -> object override
52
52
{
53
- if constexpr (std::is_invocable_v<Callee >)
53
+ if constexpr (std::is_invocable_v<F >)
54
54
{
55
- if constexpr (std::is_same_v<std::invoke_result_t <Callee >, void >)
55
+ if constexpr (std::is_same_v<std::invoke_result_t <F >, void >)
56
56
{
57
- call ( );
57
+ std::invoke (invocable );
58
58
return unspecified;
59
59
}
60
- else if constexpr (std::is_same_v<std::invoke_result_t <Callee >, bool >)
60
+ else if constexpr (std::is_same_v<std::invoke_result_t <F >, bool >)
61
61
{
62
- return call ( ) ? t : f;
62
+ return std::invoke (invocable ) ? t : f;
63
63
}
64
64
else
65
65
{
66
- return call ( );
66
+ return std::invoke (invocable );
67
67
}
68
68
}
69
69
else
70
70
{
71
- if constexpr (std::is_same_v<std::invoke_result_t <Callee , object &>, void >)
71
+ if constexpr (std::is_same_v<std::invoke_result_t <F , object &>, void >)
72
72
{
73
- call ( xs);
73
+ std::invoke (invocable, xs);
74
74
return unspecified;
75
75
}
76
- else if constexpr (std::is_same_v<std::invoke_result_t <Callee , object &>, bool >)
76
+ else if constexpr (std::is_same_v<std::invoke_result_t <F , object &>, bool >)
77
77
{
78
- return call ( xs) ? t : f;
78
+ return std::invoke (invocable, xs) ? t : f;
79
79
}
80
80
else
81
81
{
82
- return call ( xs);
82
+ return std::invoke (invocable, xs);
83
83
}
84
84
}
85
85
}
86
86
};
87
87
88
+ /*
89
+ There is no need to define specializations for this primary template to
90
+ work. However, since the template parameter `F` is usually given a
91
+ closure, there will be a unique template instantiation for each lambda
92
+ expression. The problem in this case is that the binary size becomes huge.
93
+ To deal with this, we take advantage of the fact that closures that do not
94
+ capture anything can be converted to function pointers, and decay closures
95
+ into function pointer types to reduce the number of template
96
+ instantiations.
97
+ */
98
+ template <typename , typename F, typename = void >
99
+ struct procedure
100
+ {
101
+ using type = generic_procedure<F>;
102
+ };
103
+
104
+ /*
105
+ Thunk
106
+ */
107
+ template <typename T, typename F>
108
+ struct procedure <T, F, std::enable_if_t <std::is_convertible_v<F, auto (*)() -> object>>>
109
+ {
110
+ using type = generic_procedure<auto (*)() -> object>;
111
+ };
112
+
113
+ /*
114
+ Procedure
115
+ */
116
+ template <typename T, typename F>
117
+ struct procedure <T, F, std::enable_if_t <std::is_convertible_v<F, auto (*)(object const &) -> object>>>
118
+ {
119
+ using type = generic_procedure<auto (*)(object const &) -> object>;
120
+ };
121
+
122
+ /*
123
+ Linear update procedure
124
+ */
125
+ template <typename T, typename F>
126
+ struct procedure <T, F, std::enable_if_t <std::is_convertible_v<F, auto (*)(object &) -> object>>>
127
+ {
128
+ using type = generic_procedure<auto (*)(object &) -> object>;
129
+ };
130
+
131
+ /*
132
+ Predicate
133
+ */
134
+ template <typename T, typename F>
135
+ struct procedure <T, F, std::enable_if_t <std::is_convertible_v<F, auto (*)(object const &) -> bool >>>
136
+ {
137
+ using type = generic_procedure<auto (*)(object const &) -> bool >;
138
+ };
139
+
140
+ /*
141
+ Command
142
+ */
143
+ template <typename T, typename F>
144
+ struct procedure <T, F, std::enable_if_t <std::is_convertible_v<F, auto (*)(object const &) -> void >>>
145
+ {
146
+ using type = generic_procedure<auto (*)(object const &) -> void >;
147
+ };
148
+
149
+ /*
150
+ Mutation
151
+ */
152
+ template <typename T, typename F>
153
+ struct procedure <T, F, std::enable_if_t <std::is_convertible_v<F, auto (*)(object &) -> void >>>
154
+ {
155
+ using type = generic_procedure<auto (*)(object &) -> void >;
156
+ };
157
+
88
158
using procedure_pointer = auto (*)(object &) -> object;
89
159
} // namespace kernel
90
160
} // namespace meevax
0 commit comments